> ## Documentation Index
> Fetch the complete documentation index at: https://docs.n3wmedia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Get FrankPHP Running

> Install FrankPHP on shared hosting, configure it with a standard .env file, and build your first protected page in under 15 minutes.

# Get FrankPHP Running

By the end of this guide you'll have FrankPHP installed on a subdomain, configured with a standard `.env` file, be logged in as a real user, and have built your first protected page. That's enough to know the framework is working and to get a feel for how it thinks.

Your host needs **PHP 8.x** and **MySQL**. Nothing else. No Composer, no Node, no build tools.

***

## Step 1: Create your MySQL database

Log in to your hosting control panel and create a new, empty MySQL database. Most shared hosts do this through **phpMyAdmin** or a dedicated "MySQL Databases" section (cPanel, Plesk, etc.).

Note down the following — you'll need them in the next step:

* Database name
* Database username
* Database password
* Host (almost always `localhost` on shared hosting)

***

## Step 2: Configure FrankPHP locally with `.env`

Download the latest release from FrankPHP's [GitHub Releases page](https://github.com/EdStivala/frankphp-framework/releases) — grab the **Source code** zip (or `git clone` the repo directly). Either way you'll get a single project folder containing both `framework/` and `app/` as siblings — a complete working starting point.

FrankPHP uses a standard `.env` file for environment-specific credentials. This keeps secrets out of your main config structure and makes FrankPHP feel more familiar if you've used other PHP frameworks before.

Copy `app/env.example` to `app/.env`. The example file documents every key the application supports — including mail credentials needed for password reset — so it's the safer starting point than creating one from scratch.

Open `app/.env` and add your database credentials and core app settings:

```env theme={null}
DB_HOST=localhost
DB_NAME=your_db_name
DB_USER=your_db_user
DB_PASS=your_db_password
APP_TIMEZONE=Europe/London
APP_NAME="My App"
```

<Note>
  The `.env` file is for environment-specific settings only. Keep framework defaults and non-sensitive application settings in the normal config structure (`app/Config/config.php`). Use `.env` for things like database credentials, mail credentials, and other secrets.
</Note>

<Warning>
  Do not upload your local `.env` to a public web root. It lives at `app/.env`, outside the browser-accessible `app/public/` folder.
</Warning>

***

## Step 3: Set up your directory structure on the server

Connect to your hosting account via FTP and navigate to your root directory. Create a project folder — name it whatever makes sense for what you're building (your product's own name, or just `/frankphp`). This guide uses `/yourproject`.

```
/yourproject   ← FrankPHP lives here
/www           ← your marketing site, if you have one (point www.yoursite.com here)
/docs          ← user documentation, if needed (point docs.yoursite.com here)
```

<Note>
  `/www` and `/docs` are optional and outside the scope of this guide — they're here to show how a typical FrankPHP-based project organizes a hosting account. What matters right now is creating `/yourproject`.
</Note>

<Note>
  Some FrankPHP-based products also add a `/platform` folder — a super-user layer that sits above individual tenant admins. This is a newer, still-emerging pattern and not yet part of the core framework release. Mention it here only if it's relevant to what you're building; most projects won't need it yet.
</Note>

***

## Step 4: Upload FrankPHP into your project directory

Upload the entire contents of your downloaded FrankPHP folder into `/yourproject`. You should end up with two sibling folders inside it:

```
/yourproject
  /framework   ← the FrankPHP framework itself
  /app         ← your application
```

<Warning>
  **`framework/` is not yours to edit.** It's replaced wholesale on every framework update, so any changes you make inside it will be silently lost the next time you update. All of your own code, views, and configuration live in `app/` — see `framework/codebase.md` for the full framework/application ownership boundary.
</Warning>

**Check:** `app/bootstrap.php` and `framework/bootstrap.php` should each sit at the root of their own folder, not inside a subfolder. Your `.env` file should sit at `app/.env`.

***

## Step 5: Point your subdomain to the public folder

This is the most important configuration step, and it's worth understanding why.

FrankPHP's web root is `app/public/` — a subdirectory inside `app/`, itself a subdirectory of `/yourproject`. This is the **only folder your subdomain should point to**. Your hosting control panel will have a setting for "Document Root" or "Web Root" when you create the subdomain — set it to:

```
/yourproject/app/public
```

So your full structure looks like this:

```
/yourproject
  /framework
    bootstrap.php
    codebase.md
    ...
  /app
    /.env               ← your environment credentials live here, outside the web root
    /public             ← app.yoursite.com points HERE
      index.php
      /assets
    /Config
      config.php        ← framework/app config, not secrets
    /Controllers
    /Views
    bootstrap.php
```

<Warning>
  **Why this matters:** By pointing your domain to `app/public` only, your `.env` file and all framework/application source files stay outside the web root. That means your credentials cannot be requested directly in the browser. This is a simple, effective security layer and follows the same broad approach used by larger PHP frameworks.
</Warning>

Once the subdomain is configured, point `app.yoursite.com` to `/yourproject/app/public`.

***

## Step 6: Let FrankPHP initialise the database

Nothing to do here by hand. The first time you visit the site, FrankPHP checks whether its core tables exist and, if not, creates them automatically from `framework/sql/schema.core.sql` and seeds two starter accounts so you can log in immediately.

<Note>
  If you'd rather review the schema before your first visit, you can still import `framework/sql/schema.core.sql` manually through phpMyAdmin's **Import** tab, or by pasting its contents into the **SQL** command window. This is entirely optional — FrankPHP creates it automatically either way.
</Note>

***

## Step 7: Open FrankPHP in your browser

Navigate to `https://app.yoursite.com` in your browser.

You should see the FrankPHP login screen.

<Check>
  If you see a login form, FrankPHP is installed correctly. Everything worked.
</Check>

If you see a blank page or a PHP error, the most common causes are:

* `.env` has incorrect database credentials
* The subdomain is pointing to `/yourproject/app` instead of `/yourproject/app/public`
* Your database user doesn't have permission to create tables (needed for the automatic first-boot schema setup)
* File permissions — your host may require folders to be `755` and files `644`

***

## Step 8: Log in

The schema seeds two accounts inside **Tenant 1** — one owner-level, one standard user:

| Role          | Email               | Password   |
| ------------- | ------------------- | ---------- |
| Admin / Owner | `owner@tenant1.com` | `password` |
| User          | `user@tenant1.com`  | `password` |

Log in with the **owner** account. After login you'll land on the default dashboard at:

```
/tenant/1/dashboard
```

<Warning>
  **"Forgot my password"** is visible on the login screen but will not work yet — FrankPHP needs email credentials configured before it can send password reset links. When mail support is enabled, these credentials should also live in `.env`.
</Warning>

<Warning>
  Change these passwords before you share the URL with anyone. They are seed credentials for development only.
</Warning>

***

## Step 9: Build your first page

FrankPHP is running. Now let's prove it properly by building something — a simple protected page that only a logged-in user can see. This touches every layer of the framework in the most straightforward way possible.

### 9a. Register the route

Open `app/bootstrap.php` and find the route definitions. Add one new line inside the existing authenticated routes block:

```php theme={null}
$router->add('GET', '/tenant/{tenant_id}/hello', 'HelloController@index', [$tm, $auth]);
```

This tells the router: when a GET request comes in for this URL, run `HelloController@index`, and run the Tenant and Auth middleware first.

### 9b. Create the controller

Create a new file: `app/Controllers/HelloController.php`

```php theme={null}
<?php

namespace App\Controllers;

use Frank\Core\BaseController;
use Frank\Core\Request;
use Frank\Core\Response;

class HelloController extends BaseController
{
    public function index(Request $request, array $params): mixed
    {
        $tenant = $request->tenant;
        $user   = $request->user;

        return $this->view('hello/index', [
            'title'  => 'Hello, FrankPHP',
            'tenant' => $tenant,
            'user'   => $user,
        ]);
    }
}
```

### 9c. Create the view

Create a new file: `app/Views/hello/index.php`

```php theme={null}
<?php
$title = $title ?? 'Hello';
ob_start();
?>

<div class="card">
    <div class="card-header-modern">
        <h5>Hello, FrankPHP</h5>
    </div>
    <div class="card-body p-3">
        <p>You are logged in as <strong><?= htmlspecialchars($user['name']) ?></strong>.</p>
        <p>Your tenant is <strong><?= htmlspecialchars($tenant['name']) ?></strong>.</p>
        <p>If you can read this, FrankPHP is working correctly.</p>
    </div>
</div>

<?php
$content = ob_get_clean();
require APP_VIEWS_DIR . '/layouts/app-main.php';
?>
```

### 9d. Upload and visit

Upload just the two new files via FTP — no need to re-upload the whole framework.

Then visit:

```
https://app.yoursite.com/tenant/1/hello
```

You should see your new page, inside the app shell, with your user's name and tenant displayed.

<Check>
  If you can see the page, you've just built your first FrankPHP feature. The route, middleware, controller, and view are all working together correctly.
</Check>

***

## What just happened

It's worth pausing to understand what FrankPHP did when you visited that URL:

1. `app/public/index.php` loaded `framework/bootstrap.php`, which immediately called `Env::load()` to read `app/.env` into `$_ENV` before anything else ran
2. The database connection was established using credentials from `$_ENV`, framework container bindings and framework routes were registered, then `framework/bootstrap.php` hard-required `app/bootstrap.php` as its final step to register your application's own routes and bindings — returning the router
3. The router dispatched the request, matched `/tenant/1/hello`, and identified your controller
4. **TenantMiddleware** ran first — it read `1` from the URL, loaded the tenant from the database, and attached it to the request
5. **AuthMiddleware** ran second — it checked your session, loaded your user, confirmed they belong to tenant 1, and attached them to the request
6. `HelloController@index` received the request with `$request->tenant` and `$request->user` already populated
7. The view rendered inside `app-main.php`, the authenticated layout

No magic. Every step is explicit and traceable. That's the FrankPHP way.

***

## Why `.env` is worth using

Moving credentials into `.env` gives FrankPHP a cleaner separation between code and secrets.

It also brings three practical benefits:

1. **Better security** — credentials live outside the public web root and outside your main config file
2. **Cleaner deployments** — each environment can have its own `.env` without editing framework code
3. **More familiar workflow** — developers coming from Laravel, Symfony, and other PHP frameworks will immediately understand the pattern

This keeps FrankPHP aligned with common modern PHP practices while still staying true to the framework's philosophy: no dependencies, no hidden machinery, and no complex setup pipeline.

***

## Next steps

* **Add a form** — learn how to handle POST requests and save data to the database
* **Understand middleware** — see the full middleware cheatsheet and when to use each combination
* **Start your application** — work through [Working with MYAPP.md](/getting-started/working-with-myapp) to record your app's name, tables, and routes as you build
* **Configure version management** — use the release tooling to track versions, changes, and packaged framework-only updates
