= 1024 && $i < count($units) - 1) { $n /= 1024; $i++; } return ($i === 0 ? $n : round($n, 1)) . ' ' . $units[$i]; } // -------------------------------------------------------------------------- // Environment pre-flight checks. Each entry: label, ok, detail, fatal. // -------------------------------------------------------------------------- function preflight_checks($configPath, $schemaPath, $uploadsDir) { $checks = array(); $phpOk = version_compare(PHP_VERSION, '7.4.0', '>='); $checks[] = array( 'label' => 'PHP version 7.4 or newer', 'ok' => $phpOk, 'detail' => $phpOk ? 'Running PHP ' . PHP_VERSION . '.' : 'You are on PHP ' . PHP_VERSION . '. Ask your host to switch the PHP ' . 'version (in cPanel this is "Select PHP Version" / "MultiPHP Manager"), ' . 'or update your local MAMP/XAMPP.', 'fatal' => true, ); $pdo = extension_loaded('pdo_mysql'); $checks[] = array( 'label' => 'PDO MySQL database driver', 'ok' => $pdo, 'detail' => $pdo ? 'The pdo_mysql extension is enabled.' : 'The pdo_mysql extension is missing. Enable it in your PHP settings ' . '(cPanel: "Select PHP Version" → tick "pdo_mysql"; MAMP usually has it on).', 'fatal' => true, ); $canWriteConfig = file_exists($configPath) ? is_writable($configPath) : is_writable(dirname($configPath)); $checks[] = array( 'label' => 'config.php can be written', 'ok' => $canWriteConfig, 'detail' => $canWriteConfig ? (file_exists($configPath) ? 'config.php exists and is writable.' : 'The folder is writable, so config.php can be created.') : 'The web server cannot write to this folder. Set the folder\'s permissions to ' . '755 (and config.php to 644) via your host\'s File Manager or FTP client.', 'fatal' => true, ); $uploadsOk = is_dir($uploadsDir) ? is_writable($uploadsDir) : is_writable(dirname($uploadsDir)); $checks[] = array( 'label' => 'uploads/ folder is writable', 'ok' => $uploadsOk, 'detail' => $uploadsOk ? (is_dir($uploadsDir) ? 'uploads/ exists and is writable.' : 'uploads/ will be created during install.') : 'The uploads/ folder is not writable. Create a folder named "uploads" next to ' . 'this file and set its permissions to 755.', 'fatal' => false, ); $schemaOk = is_file($schemaPath) && is_readable($schemaPath); $checks[] = array( 'label' => 'schema.sql is present', 'ok' => $schemaOk, 'detail' => $schemaOk ? 'Found schema.sql (the database table definitions).' : 'schema.sql is missing. Re-upload it from the project files — the installer ' . 'needs it to create the database tables.', 'fatal' => false, ); $zip = class_exists('ZipArchive'); $checks[] = array( 'label' => 'Zip extension (optional)', 'ok' => $zip, 'detail' => $zip ? 'Zip uploads will work.' : 'The zip extension is off. Everything works except "Upload a zip"; you can ' . 'still upload files and folders. Enable php-zip later if you want it.', 'fatal' => false, ); return $checks; } /** Build the contents of config.php from validated values. */ function build_config_php($v) { $x = function ($s) { return var_export($s, true); }; $lines = array(); $lines[] = 'getMessage(); $code = $e->getCode(); if (stripos($msg, 'Access denied') !== false) { return 'The database refused the username or password. Double-check DB_USER and ' . 'DB_PASS. On cPanel the username is usually your account name plus an ' . 'underscore, like "myacct_code".'; } if (stripos($msg, 'Unknown database') !== false) { return 'The database name was not found. Create the database first (cPanel → ' . '"MySQL Databases"), or tick "Create the database if it does not exist" below ' . 'if your user is allowed to create databases.'; } if (stripos($msg, 'Connection refused') !== false || stripos($msg, "Can't connect") !== false || $code === 2002) { return 'Could not reach the database server at that host. Try "localhost" (most ' . 'shared hosts and MAMP). If MAMP uses a custom port, use "127.0.0.1:8889".'; } if (stripos($msg, 'getaddrinfo') !== false || stripos($msg, 'php_network_getaddresses') !== false) { return 'The database host name could not be resolved. Check DB_HOST for typos; it ' . 'is usually "localhost".'; } return 'Database error: ' . $msg; } // -------------------------------------------------------------------------- // Handle the install submission. // -------------------------------------------------------------------------- $errors = array(); $warnings = array(); $success = false; $results = array(); // Sticky form values (also used to prefill on first load). $f = array( 'db_host' => 'localhost', 'db_name' => '', 'db_user' => '', 'db_pass' => '', 'admin_user' => '', 'base_path' => detect_base_path(), 'max_file' => 25, 'max_zip' => 200, ); if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['do_install'])) { foreach (array('db_host','db_name','db_user','db_pass','admin_user','base_path') as $k) { if (isset($_POST[$k])) { $f[$k] = trim($_POST[$k]); } } $f['max_file'] = isset($_POST['max_file']) ? max(1, (int) $_POST['max_file']) : 25; $f['max_zip'] = isset($_POST['max_zip']) ? max(1, (int) $_POST['max_zip']) : 200; $adminPass = isset($_POST['admin_pass']) ? (string) $_POST['admin_pass'] : ''; $adminPass2 = isset($_POST['admin_pass2']) ? (string) $_POST['admin_pass2'] : ''; $createDb = !empty($_POST['create_db']); $importSchema = !empty($_POST['import_schema']); // Normalise base path: ensure a single leading slash, no trailing slash, or ''. $bp = str_replace('\\', '/', $f['base_path']); $bp = '/' . trim($bp, '/'); $f['base_path'] = ($bp === '/') ? '' : $bp; // ---- Field validation ------------------------------------------------- if ($f['db_name'] === '') { $errors[] = 'Database name is required.'; } if ($f['db_user'] === '') { $errors[] = 'Database username is required.'; } if ($f['admin_user'] === '') { $errors[] = 'Admin username is required.'; } if (strlen($adminPass) < 8) { $errors[] = 'Admin password must be at least 8 characters. Pick something long and hard to guess.'; } elseif ($adminPass !== $adminPass2) { $errors[] = 'The two admin passwords do not match. Re-type them carefully.'; } // ---- Database connection & optional creation -------------------------- $pdo = null; if (!$errors) { try { if ($createDb) { // Connect without a database, create it, then select it. $dsn = 'mysql:host=' . $f['db_host'] . ';charset=utf8mb4'; $pdo = new PDO($dsn, $f['db_user'], $f['db_pass'], array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)); $safe = str_replace('`', '', $f['db_name']); $pdo->exec('CREATE DATABASE IF NOT EXISTS `' . $safe . '` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'); $pdo->exec('USE `' . $safe . '`'); $results[] = 'Database "' . h($f['db_name']) . '" is ready.'; } else { $dsn = 'mysql:host=' . $f['db_host'] . ';dbname=' . $f['db_name'] . ';charset=utf8mb4'; $pdo = new PDO($dsn, $f['db_user'], $f['db_pass'], array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)); $results[] = 'Connected to the database successfully.'; } } catch (PDOException $e) { $errors[] = friendly_db_error($e); } } // ---- Import the schema ------------------------------------------------ if (!$errors && $pdo && $importSchema) { if (!is_file($SCHEMA_PATH)) { $errors[] = 'schema.sql is missing, so the tables could not be created. Re-upload it and try again.'; } else { try { $sql = file_get_contents($SCHEMA_PATH); $sql = preg_replace('/^\s*--.*$/m', '', $sql); // strip comment lines $parts = array_filter(array_map('trim', explode(';', $sql))); foreach ($parts as $stmt) { $pdo->exec($stmt); } $results[] = 'Database tables created (repositories, files, folders).'; } catch (PDOException $e) { $errors[] = 'Could not create the tables: ' . h($e->getMessage()) . ' — you can also import schema.sql manually via phpMyAdmin.'; } } } // ---- Write config.php ------------------------------------------------- if (!$errors) { $vals = array( 'db_host' => $f['db_host'], 'db_name' => $f['db_name'], 'db_user' => $f['db_user'], 'db_pass' => $f['db_pass'], 'admin_user' => $f['admin_user'], 'admin_hash' => password_hash($adminPass, PASSWORD_DEFAULT), 'base_path' => $f['base_path'], 'site_url' => detect_site_url(), 'max_file' => $f['max_file'], 'max_zip' => $f['max_zip'], ); $written = @file_put_contents($CONFIG_PATH, build_config_php($vals)); if ($written === false) { $errors[] = 'Could not write config.php. Check the folder is writable (permissions 755) ' . 'and try again. Nothing else was changed.'; } else { @chmod($CONFIG_PATH, 0644); $results[] = 'config.php written with your settings.'; } } // ---- Make sure uploads/ exists --------------------------------------- if (!$errors) { if (!is_dir($UPLOADS_DIR)) { if (@mkdir($UPLOADS_DIR, 0755, true)) { $results[] = 'Created the uploads/ folder.'; } else { $warnings[] = 'Could not create the uploads/ folder automatically. Create a folder ' . 'named "uploads" next to install.php (permissions 755) before uploading files.'; } } } // ---- Best-effort: match .htaccess RewriteBase to BASE_PATH ------------ if (!$errors && is_file($HTACCESS) && is_writable($HTACCESS)) { $ht = file_get_contents($HTACCESS); $rewriteBase = ($f['base_path'] === '') ? '/' : $f['base_path'] . '/'; $newHt = preg_replace('/^(\s*RewriteBase\s+).*$/m', '${1}' . $rewriteBase, $ht, 1, $count); if ($count > 0 && $newHt !== null && $newHt !== $ht) { if (@file_put_contents($HTACCESS, $newHt) !== false) { $results[] = 'Updated .htaccess RewriteBase to "' . h($rewriteBase) . '".'; } else { $warnings[] = 'Could not update .htaccess. If pretty URLs 404, set "RewriteBase ' . h($rewriteBase) . '" in .htaccess by hand.'; } } } elseif (!$errors && is_file($HTACCESS)) { $rewriteBase = ($f['base_path'] === '') ? '/' : $f['base_path'] . '/'; $warnings[] = '.htaccess is not writable, so its RewriteBase was left as-is. If pretty URLs ' . '404, set "RewriteBase ' . h($rewriteBase) . '" in .htaccess by hand.'; } if (!$errors) { $success = true; } } // Public login / home URLs for the success screen. $base = $f['base_path']; $loginUrl = ($base === '' ? '' : $base) . '/admin/login.php'; $homeUrl = ($base === '' ? '/' : $base . '/'); $checks = preflight_checks($CONFIG_PATH, $SCHEMA_PATH, $UPLOADS_DIR); $fatalBlock = false; foreach ($checks as $c) { if ($c['fatal'] && !$c['ok']) { $fatalBlock = true; } } $alreadyInstalled = is_file($CONFIG_PATH) && !$success; ?>
NestEggCode is set up. Here is what the installer did:
Go to the admin login → View the public site →Run through this to confirm everything works. Once it all checks out,
delete install.php (the dashboard will offer to do this for you).
demo,
name Demo). It should appear in the list.README.md; it should render
as formatted text on the repo's front page (headings, code blocks, links).= h(($base === '' ? '' : $base)) ?>/config.php
directly — you should get a "Forbidden" error, not the file contents.install.php via FTP/File Manager.Tip: also add config.php, uploads/ and *.zip
to a .gitignore before pushing this project anywhere public.
This sets up your self-hosted code browser. Have your database name, username and password ready (create the database in your host's control panel first if you can).
NestEggCode installer · delete install.php when you're done.