PHP-Installer-Wizard

PHP Installer Wizard Logo # PHP Project Installer Wizard **A modern, professional, browser-based PHP installation wizard — framework-agnostic, modular, and inspired by the WordPress/Softaculous installation experience.** [![PHP](https://img.shields.io/badge/PHP-8.0%2B-777BB4?style=flat-square&logo=php&logoColor=white)](https://php.net) [![MDB UI Kit](https://img.shields.io/badge/MDB_UI_Kit-v9-1266F1?style=flat-square&logo=bootstrap&logoColor=white)](https://mdbootstrap.com) [![Bootstrap Icons](https://img.shields.io/badge/Bootstrap_Icons-1.11-7952B3?style=flat-square&logo=bootstrap&logoColor=white)](https://icons.getbootstrap.com) [![License: MIT](https://img.shields.io/badge/License-MIT-10b981?style=flat-square)](LICENSE) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen?style=flat-square)](CONTRIBUTING.md) [![Stars](https://img.shields.io/github/stars/amanprojects-ops/PHP-Installer-Wizard?style=flat-square&color=fbbf24)](https://github.com/amanprojects-ops/PHP-Installer-Wizard) ### 🎬 Live Demo ![Installer Welcome Step](https://raw.githubusercontent.com/amanprojects-ops/PHP-Installer-Wizard/main/docs/screenshots/welcome.png)

📋 Table of Contents


✨ Features

🧙 7-Step Guided Wizard

Step Description
1️⃣ Welcome Project logo, name, version, description, system overview tiles
2️⃣ Server Requirements 20+ automated checks — PHP, extensions, memory, permissions
3️⃣ Database Configuration MySQL/MariaDB setup with AJAX Test Connection
4️⃣ Application Settings Name, URL, environment, debug mode, timezone, locale
5️⃣ Admin Account Name, email, username, password with live strength meter
6️⃣ Installation Real-time SSE progress bar — watch each task run live
7️⃣ Finished Success summary, action buttons, security reminder, confetti 🎉

🔌 Modular Plugin System

🛡 Enterprise-Grade Security

🎨 Premium UI


🛠 Tech Stack

Layer Technology
Backend Pure PHP 8.0+ · OOP · PSR-4 Autoloading
UI Framework MDB UI Kit v9 (Material Design Bootstrap)
Icons Bootstrap Icons 1.11
Fonts Inter via Google Fonts
Database MySQL / MariaDB via PDO
Real-time Server-Sent Events (SSE)
Server Apache (with .htaccess) / Nginx
Styling Custom CSS — glassmorphism, CSS variables, animations

📸 Screenshots

Welcome Step
Step 1 — Welcome
Server Requirements
Step 2 — Server Requirements
Database Configuration
Step 3 — Database Configuration
Admin Account
Step 5 — Admin Account
Live Progress
Step 6 — Live Installation Progress
Installation Complete
Step 7 — Finished

🚀 Quick Start

Prerequisites

Installation

Option 1 — Drop-in (No Composer)

# Clone the repository into your web root
git clone https://github.com/amanprojects-ops/PHP-Installer-Wizard.git LaravelProjectsInstaller

# Navigate to the folder in your browser
# http://localhost/LaravelProjectsInstaller/

The built-in PSR-4 autoloader handles everything automatically — no composer install required.

Option 2 — With Composer

git clone https://github.com/amanprojects-ops/PHP-Installer-Wizard.git LaravelProjectsInstaller
cd LaravelProjectsInstaller
composer install

Then open:

http://localhost/LaravelProjectsInstaller/

Directory Permissions

chmod -R 755 storage/
chmod -R 755 storage/logs/
chmod -R 755 storage/sessions/

On Windows (XAMPP) no permission changes are needed.


📁 Project Structure

php-installer-wizard/
│
├── index.php                        ← Entry point & PSR-4 autoloader
├── composer.json
├── .htaccess                        ← Apache URL rewriting & security headers
├── install.lock                     ← Created after successful install (blocks re-run)
│
├── installer/
│   ├── Config/
│   │   ├── installer.php            ← Master config (branding, steps, languages)
│   │   └── requirements.php         ← Declarative server requirement definitions
│   │
│   ├── Core/
│   │   ├── Application.php          ← Front controller + DI container
│   │   ├── Request.php              ← HTTP request abstraction
│   │   ├── Response.php             ← Redirect / JSON / SSE helpers
│   │   ├── Session.php              ← Namespaced session manager + CSRF tokens
│   │   ├── View.php                 ← PHP template renderer (fetch/render)
│   │   ├── Validator.php            ← Fluent input validator
│   │   ├── Logger.php               ← Timestamped file logger
│   │   └── Security.php             ← XSS, sanitize, key generation, bcrypt
│   │
│   ├── Database/
│   │   ├── Connection.php           ← PDO wrapper with testConnection()
│   │   └── DatabaseManager.php      ← CREATE DB, importSQL, migrate, seed
│   │
│   ├── Checkers/
│   │   ├── RequirementsChecker.php  ← 20+ automated server checks
│   │   └── EnvironmentChecker.php   ← Auto-detect project type & parse .env
│   │
│   ├── Modules/                     ← Plugin system
│   │   ├── ModuleInterface.php      ← Contract all modules must implement
│   │   ├── ModuleLoader.php         ← Scans modules/ and resolves active module
│   │   ├── LaravelModule.php        ← Laravel tasks (key:generate, migrate, etc.)
│   │   ├── CorePhpModule.php        ← CodeIgniter 4 tasks (spark migrate)
│   │   └── GenericModule.php        ← Generic PHP fallback
│   │
│   ├── Steps/                       ← One class per wizard step
│   │   ├── StepInterface.php
│   │   ├── WelcomeStep.php
│   │   ├── RequirementsStep.php
│   │   ├── DatabaseStep.php
│   │   ├── AppSettingsStep.php
│   │   ├── AdminAccountStep.php
│   │   ├── InstallationStep.php
│   │   └── FinishedStep.php
│   │
│   └── Installation/
│       ├── Task.php                 ← Named closure wrapper (status/output/elapsed)
│       ├── TaskRunner.php           ← SSE streaming task executor
│       └── InstallationLog.php      ← JSON installation report writer
│
├── modules/                         ← Drop your own modules here
│   ├── laravel/module.json
│   ├── codeigniter/module.json
│   └── generic/module.json
│
├── views/
│   ├── layout.php                   ← Master HTML shell (MDB UI Kit)
│   ├── partials/
│   │   └── stepper.php
│   └── steps/
│       ├── welcome.php
│       ├── requirements.php
│       ├── database.php
│       ├── app_settings.php
│       ├── admin_account.php
│       ├── installation.php
│       ├── finished.php
│       └── locked.php
│
├── assets/
│   ├── css/installer.css            ← Glassmorphism + MDB complement styles
│   ├── js/
│   │   ├── installer.js             ← Theme, Toasts, AJAX DB test, MDB init
│   │   ├── progress.js              ← SSE EventSource client
│   │   └── password-strength.js     ← Strength meter + character checklist
│   └── images/logo.svg
│
├── lang/
│   ├── en.php                       ← English strings
│   └── fr.php                       ← French strings
│
└── storage/
    ├── logs/
    │   ├── installer.log            ← Runtime log
    │   └── installation.json        ← Post-install JSON report
    └── sessions/

🏗 Placing the Installer in Your Project

The installer is a standalone subdirectory that lives alongside your PHP project. It runs before your application and sets it up, then locks itself permanently after a successful install.

The key principle: the installer sits next to your project, not inside it.

/htdocs/                        ← your web server's document root
├── my-laravel-app/             ← your main Laravel/PHP project
│   ├── app/
│   ├── public/
│   └── ...
│
└── LaravelProjectsInstaller/  ← this installer (sibling directory)
    ├── index.php
    ├── installer/
    ├── modules/
    └── ...

You open the installer first → complete all 7 steps → installer locks itself → then you access your main app.


🔷 Laravel Project Integration

Step 1 — Directory Placement

Clone or copy the installer as a sibling to your Laravel project in the web root:

/htdocs/
├── my-laravel-app/         ← existing Laravel project
│   ├── app/
│   ├── bootstrap/
│   ├── config/
│   ├── database/
│   ├── public/
│   ├── resources/
│   ├── routes/
│   ├── storage/
│   ├── artisan              ← auto-detected by LaravelModule
│   └── .env.example
│
└── LaravelProjectsInstaller/  ← installer (same level as Laravel root)
    ├── index.php
    ├── installer/
    │   └── Modules/
    │       └── LaravelModule.php
    ├── modules/
    │   └── laravel/
    │       └── module.json
    └── ...

Step 2 — Auto-Detection

The LaravelModule auto-detects Laravel by checking:

// installer/Modules/LaravelModule.php

public function detect(): bool
{
    return file_exists($this->projectRoot . '/artisan')        // artisan file exists
        && is_dir($this->projectRoot . '/app/Http');           // AND app/Http dir exists
}

$this->projectRoot is automatically set to dirname(INSTALLER_ROOT) — i.e., the parent directory of the installer — which is your Laravel root.

Step 3 — What the Laravel Module Does (in order)

# Task What Happens
1 Generate .env Copies .env.example.env, populates APP_NAME, APP_ENV, APP_DEBUG, APP_URL, DB_* from wizard form data
2 Generate App Key Runs php artisan key:generate --force. Falls back to writing APP_KEY directly if artisan fails
3 Create Database Connects without database → runs CREATE DATABASE IF NOT EXISTS with your chosen charset/collation
4 Run Migrations Runs php artisan migrate --force and streams output
5 Seed Database Runs php artisan db:seed --force
6 Storage Link Runs php artisan storage:link
7 Optimize Runs config:clear, cache:clear, route:clear, view:clear, optimize in sequence
8 Lock Writes install.lock to disable the installer permanently

Step 4 — Branding Customisation

Edit installer/Config/installer.php to match your Laravel project:

'project' => [
    'name'        => 'My Laravel SaaS',          // shown on Welcome step
    'version'     => '2.1.0',
    'description' => 'A powerful SaaS built on Laravel.',
    'author'      => 'Your Company Name',
    'login_url'   => 'http://localhost/my-laravel-app/public/login',
    'docs_url'    => 'https://docs.example.com',
],

// Force the Laravel module (skip auto-detection)
'module' => 'laravel',

For a clean URL, configure a virtual host pointing to the installer:

# httpd-vhosts.conf
<VirtualHost *:80>
    DocumentRoot "C:/xampp/htdocs/LaravelProjectsInstaller"
    ServerName installer.myapp.local
</VirtualHost>

<VirtualHost *:80>
    DocumentRoot "C:/xampp/htdocs/my-laravel-app/public"
    ServerName myapp.local
</VirtualHost>

Then visit http://installer.myapp.local/ → complete the wizard → visit http://myapp.local/.

Step 6 — Verify storage/ Write Permissions (Linux/Mac)

# Run from your Laravel project root (NOT installer root)
chmod -R 775 storage/
chmod -R 775 bootstrap/cache/
chown -R www-data:www-data storage/ bootstrap/cache/

The requirements checker (Step 2) will confirm these are writable before installation proceeds.

Step 7 — Post-Installation Cleanup

After the wizard finishes:

  1. Delete the installer directory from your server:
    rm -rf /htdocs/LaravelProjectsInstaller/
    
  2. Or restrict web access via Apache:
    <Directory "/htdocs/LaravelProjectsInstaller">
        Require all denied
    </Directory>
    

🟠 CodeIgniter Project Integration

Step 1 — Directory Placement

/htdocs/
├── my-codeigniter-app/         ← CodeIgniter 4 project
│   ├── app/
│   ├── public/
│   ├── writable/
│   ├── spark                   ← auto-detected by CorePhpModule
│   └── env                     ← CI4 env template
│
└── LaravelProjectsInstaller/  ← installer (sibling)

Step 2 — Auto-Detection

The CorePhpModule detects CodeIgniter 4 by checking for the spark file:

public function detect(): bool
{
    return file_exists($this->projectRoot . '/spark');
}

Step 3 — What the CodeIgniter Module Does (in order)

# Task What Happens
1 Set up .env Copies env.env, sets CI_ENVIRONMENT, app.baseURL, database.* values from wizard form
2 Create Database Runs CREATE DATABASE IF NOT EXISTS via PDO
3 Run Migrations Runs php spark migrate --all
4 Set Permissions chmod 775 writable/ on Linux/Mac
5 Lock Writes install.lock

Step 4 — Branding for CodeIgniter

// installer/Config/installer.php
'project' => [
    'name'    => 'My CodeIgniter App',
    'version' => '4.5.0',
],
'module' => 'codeigniter',   // force module; or leave null for auto-detect

⚪ Core PHP / Generic Project Integration

For any custom PHP project that is not Laravel or CodeIgniter.

Step 1 — Directory Placement

/htdocs/
├── my-php-app/               ← your custom PHP project
│   ├── index.php
│   ├── config/
│   ├── database.sql          ← optional: auto-imported during install
│   └── ...
│
└── LaravelProjectsInstaller/ ← installer (sibling)

Step 2 — Drop a SQL File (Optional)

If your project has a database schema, place it in the installer root:

LaravelProjectsInstaller/
└── database.sql              ← auto-detected and imported by GenericModule

The GenericModule checks for this file and imports it if found:

$sqlFile = INSTALLER_ROOT . '/database.sql';
if (file_exists($sqlFile)) {
    $dbManager->importSqlFile($sqlFile);
}

Step 3 — What the Generic Module Does (in order)

# Task What Happens
1 Create config.php Writes config.php in the installer root with all app + DB settings
2 Generate .env Writes a .env file with APP_KEY, DB_*, APP_* values
3 Create Storage Dirs Creates storage/, storage/logs/, storage/cache/, storage/sessions/, storage/uploads/
4 Setup Database Creates the database and imports database.sql if present
5 Set Permissions chmod 755 storage/* (Linux/Mac only)
6 Create Admin Writes admin.php with bcrypt-hashed password (⚠ delete after setup)
7 Lock Writes install.lock

Step 4 — Force the Generic Module

// installer/Config/installer.php
'module' => 'generic',

Step 5 — Reading Generated Config in Your App

After installation, your PHP app can read the generated config.php:

// your-app/bootstrap.php
$config = require __DIR__ . '/../LaravelProjectsInstaller/config.php';

define('DB_HOST', $config['db_host']);
define('DB_NAME', $config['db_name']);
// ...

Or read the .env file:

// Parse the .env using a simple parser
$env = parse_ini_file(__DIR__ . '/../LaravelProjectsInstaller/.env');
$_ENV = array_merge($_ENV, $env);

🔌 Module System

The installer uses a plugin-based module system. Each module defines the installation tasks for a specific PHP project type.

Built-in Modules

Module Detects Tasks
laravel artisan file + app/Http/ directory .env from .env.example, key:generate, create DB, migrate, db:seed, storage:link, optimize
codeigniter spark file .env config, create DB, spark migrate, set permissions
generic Fallback for anything else config.php, .env, storage dirs, DB create, SQL import, admin user, lock

Module Auto-Detection

modules/laravel/module.json  → Checks for artisan + app/Http → LaravelModule
modules/codeigniter/module.json → Checks for spark → CorePhpModule  
modules/generic/module.json  → Always matches as fallback → GenericModule

🧩 Creating a Custom Module

Add support for any PHP framework by creating two files:

1. modules/myphp/module.json

{
    "name": "myphp",
    "version": "1.0.0",
    "description": "My Custom PHP Framework Installer",
    "class": "MyPhp\\Installer",
    "detect": ["bootstrap/app.php"]
}

2. modules/myphp/Installer.php

<?php
namespace MyPhp;

use Installer\Modules\ModuleInterface;
use Installer\Installation\Task;

class Installer implements ModuleInterface
{
    public function getName(): string        { return 'myphp'; }
    public function getVersion(): string     { return '1.0.0'; }
    public function getDescription(): string { return 'My framework installer.'; }
    public function getRequirements(): array { return []; }
    public function getDefaults(): array     { return []; }

    public function detect(): bool
    {
        return file_exists(dirname(INSTALLER_ROOT) . '/bootstrap/app.php');
    }

    public function getTasks(array $installData): array
    {
        return [
            new Task(
                id:          'setup',
                name:        'Setting Up Application',
                description: 'Running custom setup steps.',
                callback:    function () use ($installData): string {
                    // Your custom installation logic here
                    return 'Setup complete!';
                }
            ),
            new Task(
                id:          'finalize',
                name:        'Finalizing',
                description: 'Writing lock file.',
                callback:    function (): string {
                    file_put_contents(LOCK_FILE, json_encode([
                        'installed_at' => date('Y-m-d H:i:s'),
                        'module'       => 'myphp',
                    ]));
                    return 'Done.';
                }
            ),
        ];
    }
}

That’s it! The ModuleLoader auto-discovers it on the next request.


⚙️ Configuration

Edit installer/Config/installer.php to customise:

return [
    // Project branding
    'project' => [
        'name'        => 'My PHP Application',
        'version'     => '2.0.0',
        'description' => 'An awesome PHP application.',
        'author'      => 'Your Name',
        'login_url'   => 'https://myapp.com/admin/login',
        'docs_url'    => 'https://myapp.com/docs',
    ],

    // Force a specific module (null = auto-detect)
    'module' => null,  // or 'laravel', 'codeigniter', 'generic'

    // Features
    'features' => [
        'multi_language' => true,
        'theme_switcher' => true,
        'license_check'  => false,
    ],

    // Default values pre-filled in forms
    'defaults' => [
        'app_env'      => 'production',
        'db_host'      => '127.0.0.1',
        'db_port'      => '3306',
        'db_charset'   => 'utf8mb4',
    ],
];

🔒 Security

Threat Mitigation
CSRF Double-submit token validated on every POST
XSS All output passed through htmlspecialchars()
SQL Injection PDO prepared statements everywhere
Session Fixation Session ID regenerated on step advance
Re-installation install.lock file blocks all access after success
Password Storage bcrypt with cost factor 12
File Access .htaccess blocks direct access to .lock, .json, .log, .env
Sensitive Logs Passwords stripped from installation.json before writing
Cookies httponly, samesite=Lax, secure on HTTPS

⚠️ After installation: Delete or restrict web access to the installer directory. Even with install.lock, removing the directory is the safest option.


🌍 Multi-Language Support

Currently bundled languages:

Code Language
en 🇬🇧 English
fr 🇫🇷 French

Adding a New Language

  1. Copy lang/en.php to lang/de.php (or your language code)
  2. Translate all values
  3. Register it in installer/Config/installer.php:
'languages' => [
    'en' => 'English',
    'fr' => 'Français',
    'de' => 'Deutsch',   // ← add here
],

The language switcher in the header appears automatically.


🎨 Theming

The installer ships with dark mode (default) and light mode, toggled via the ☀️ / 🌙 button in the header. The preference is saved in localStorage.

To change the default theme, edit installer/Config/installer.php:

'default_theme' => 'light',   // 'dark' or 'light'

All colours are CSS custom properties in assets/css/installer.css:

:root {
  --ins-primary:   #6366f1;
  --ins-secondary: #8b5cf6;
  --ins-accent:    #06b6d4;
  /* ... */
}

📡 SSE Installation Progress

Step 6 uses Server-Sent Events (SSE) for real-time progress updates — no page refresh, no polling loops.

Browser                           PHP Server
   │                                  │
   │── GET ?action=run-install ───────▶│
   │                                  │  [TaskRunner starts]
   │◀── event: start  ────────────────│  { total: 7 }
   │◀── event: task   ────────────────│  { status: 'running', progress: 14% }
   │◀── event: task   ────────────────│  { status: 'done',    progress: 28% }
   │         ...                      │
   │◀── event: complete ──────────────│  { message: 'Done!', failed: 0 }
   │                                  │  [install.lock created]
   │  [Shows "Continue" button]       │

The EventSource client lives in assets/js/progress.js.


📝 Installation Log

After every installation run, a detailed JSON report is saved to:

storage/logs/installation.json

Example:

{
    "installed_at": "2026-07-09 15:45:32",
    "php_version": "8.2.12",
    "elapsed_s": 3.847,
    "install_data": {
        "app_name": "My App",
        "app_env": "production",
        "db_name": "my_app_db"
    },
    "tasks": [
        { "id": "create_env",  "name": "Generating .env File",    "status": "done", "elapsed": 0.012 },
        { "id": "create_db",   "name": "Creating Database",        "status": "done", "elapsed": 0.231 },
        { "id": "run_migrations", "name": "Running Migrations",    "status": "done", "elapsed": 2.914 },
        { "id": "finalize",    "name": "Finalizing Installation",  "status": "done", "elapsed": 0.005 }
    ]
}

Passwords are always stripped from this log before writing.


🐛 Troubleshooting

Blank Page / 500 Error

# Check PHP syntax
php -l index.php
php -l installer/Core/Application.php

# Check PHP error log
tail -f storage/logs/installer.log

“Session could not be started”

SSE Progress Bar Doesn’t Move

Database Connection Fails

“Requirements Not Met” Even Though Extensions Are Installed

Reinstalling After lock File

# Delete the lock file to re-enable the installer
rm install.lock

⚠️ This will allow re-installation — use with caution.


🤝 Contributing

Contributions are welcome and appreciated!

# 1. Fork the repository
# 2. Create your feature branch
git checkout -b feature/amazing-module

# 3. Make your changes and commit
git commit -m "feat: add Symfony installer module"

# 4. Push and open a Pull Request
git push origin feature/amazing-module

Ideas for Contributions


📄 License

This project is licensed under the MIT License — see the LICENSE file for details.

MIT License — Copyright (c) 2026 PHP Installer Wizard Contributors

🙏 Acknowledgements


**Made with ❤️ for the PHP community** [![GitHub Stars](https://img.shields.io/github/stars/amanprojects-ops/PHP-Installer-Wizard?style=social)](https://github.com/amanprojects-ops/PHP-Installer-Wizard) [![GitHub Forks](https://img.shields.io/github/forks/amanprojects-ops/PHP-Installer-Wizard?style=social)](https://github.com/amanprojects-ops/PHP-Installer-Wizard)