= 7.4' => version_compare(PHP_VERSION, '7.4.0', '>='), 'PDO Extension' => extension_loaded('pdo'), 'PDO MySQL Extension' => extension_loaded('pdo_mysql'), 'Config Directory Writable' => is_writable(__DIR__ . '/db/'), 'Uploads Directory Writable' => is_writable(__DIR__ . '/uploads/') || (mkdir(__DIR__ . '/uploads/', 0777, true) && is_writable(__DIR__ . '/uploads/')), ]; $all_requirements_met = !in_array(false, $requirements, true); // Current step $step = isset($_GET['step']) ? (int)$_GET['step'] : 1; // Handle form submissions $error = ''; $success = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($step === 2) { // Save database configuration $host = $_POST['db_host'] ?? '127.0.0.1'; $name = $_POST['db_name'] ?? 'app_database'; $user = $_POST['db_user'] ?? 'root'; $password = $_POST['db_pass'] ?? ''; // Test connection try { $test_pdo = new PDO("mysql:host=$host;dbname=$name;charset=utf8mb4", $user, $password); $test_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Generate config file content $content = "setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); "; $content .= " \$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); "; $content .= " \$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); "; $content .= " } catch (PDOException \$e) {\n"; $content .= " die('Connection failed: ' . \$e->getMessage());\n"; $content .= " }\n"; $content .= " }\n"; $content .= " return \$pdo;\n"; $content .= " }\n"; $content .= "}\n"; if (file_put_contents($config_file, $content)) { header('Location: ' . htmlspecialchars($_SERVER['SCRIPT_NAME']) . '?step=3'); exit; } else { $error = "Failed to write configuration file to $config_file. Please check permissions."; } } catch (PDOException $e) { $error = "Connection failed: " . $e->getMessage(); } } elseif ($step === 3) { // Run migrations if (!file_exists($config_file)) { $error = "Configuration file not found. Please go back to Step 2."; } else { try { require_once $config_file; if (!function_exists('db')) { throw new Exception("The 'db()' function is not defined in your config file."); } $pdo = db(); $pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); // Create migration table if not exists $pdo->exec("CREATE TABLE IF NOT EXISTS migrations ( id INT AUTO_INCREMENT PRIMARY KEY, migration_name VARCHAR(255) NOT NULL UNIQUE, applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )"); $migrations_dir = __DIR__ . '/db/migrations/'; $files = glob($migrations_dir . '*.sql'); if ($files === false) $files = []; sort($files); $applied = 0; $errors = []; foreach ($files as $file) { $migration_name = basename($file); // Check if already applied $stmt = $pdo->prepare("SELECT id FROM migrations WHERE migration_name = ?"); $stmt->execute([$migration_name]); if ($stmt->fetch()) { continue; } $sql = file_get_contents($file); if (empty($sql)) continue; // Split SQL into individual statements by ; followed by newline $statements = preg_split('/;(?:\\s*[ ]+)/', $sql); $file_success = true; foreach ($statements as $stmt_sql) { $stmt_sql = trim($stmt_sql); if (empty($stmt_sql)) continue; // Basic comment removal $stmt_lines = explode("\n", $stmt_sql); $clean_stmt = ""; foreach ($stmt_lines as $line) { if (trim(substr(trim($line), 0, 2)) === '--') continue; $clean_stmt .= $line . "\n"; } $clean_stmt = trim($clean_stmt); if (empty($clean_stmt)) continue; try { $res = $pdo->query($clean_stmt); if ($res) { $res->closeCursor(); } } catch (Throwable $e) { $msg = $e->getMessage(); // If the error is about a table already existing, it's fine if (strpos($msg, "already exists") !== false || strpos($msg, "Duplicate column") !== false || strpos($msg, "Duplicate entry") !== false || strpos($msg, "already a column") !== false || strpos($msg, "Duplicate key") !== false || strpos($msg, "errno: 121") !== false) { continue; } else { $errors[] = $migration_name . " at statement: " . substr($clean_stmt, 0, 50) . "... Error: " . $msg; $file_success = false; break; } } } if ($file_success) { $ins = $pdo->prepare("INSERT INTO migrations (migration_name) VALUES (?)"); $ins->execute([$migration_name]); $applied++; } } if (empty($errors)) { $success = "Successfully applied migrations."; header('Location: ' . htmlspecialchars($_SERVER['SCRIPT_NAME']) . '?step=4'); exit; } else { $error = "Applied migrations, but some errors occurred:
"; } } catch (Throwable $e) { $error = "Migration failed: " . $e->getMessage(); } } } elseif ($step === 4) { // Final setup (Admin account) require_once $config_file; $pdo = db(); $admin_user = $_POST['admin_user'] ?? 'admin'; $admin_pass = $_POST['admin_pass'] ?? ''; $admin_email = $_POST['admin_email'] ?? 'admin@example.com'; if (strlen($admin_pass) < 6) { $error = "Password must be at least 6 characters long."; } else { try { $hashed_pass = password_hash($admin_pass, PASSWORD_DEFAULT); $stmt = $pdo->prepare("INSERT INTO users (username, password, email, role) VALUES (?, ?, ?, 'admin') ON DUPLICATE KEY UPDATE password = ?, email = ?"); $stmt->execute([$admin_user, $hashed_pass, $admin_email, $hashed_pass, $admin_email]); // Set initial settings $pdo->exec("INSERT IGNORE INTO charity_settings (id, charity_name) VALUES (1, 'Admin Panel')"); $pdo->exec("INSERT IGNORE INTO smtp_settings (id, is_enabled) VALUES (1, 0)"); header('Location: ' . htmlspecialchars($_SERVER['SCRIPT_NAME']) . '?step=5'); exit; } catch (Throwable $e) { $error = "Failed to create admin account: " . $e->getMessage(); } } } } // UI Template ?> Installer - Step <?= $step ?>

Installer

$i\n"; endfor; ?>

Step 1: System Requirements

    $met): echo "
  • "; echo "$name\n"; if ($met) { echo "OK"; } else { echo "Failed"; } echo "
  • \n"; endforeach; ?>
Next: Database Config Fix requirements to continue"; endif; ?>

Step 2: Database Connection

Step 3: Database Migrations

We will now run the SQL scripts to set up your database tables.

Step 4: Admin Account

Installation Complete!

The system is ready to use. For security, please delete install.php or rename it.