Skip to content

Quickstart

This guide takes you from zero to a running MVC app in five steps.

1. Scaffold the app

vendor/bin/mvc create-app ./src/MyApp --name=MyApp --namespace=App\\MyApp

This generates:

src/MyApp/
├── Controllers/
├── Views/
├── assets/
│   ├── i18n/
│   │   └── en.json
│   ├── scripts/
│   │   └── main.js
│   └── styles/
│       └── main.css
├── MyAppApp.php
├── MyAppBootstrap.php
└── mvc.config.json
  • MyAppApp.php — extends MvcWebApp; wire routes here.
  • MyAppBootstrap.php — composition root; register settings, PDO, and services on the container.
  • mvc.config.json — feature flags and paths (see Configuration Reference).

2. Define a controller

<?php

declare(strict_types=1);

namespace App\MyApp\Controllers;

use PhpMvc\Controllers\Controller;
use PhpMvc\Actions\Responses\ActionResponse;
use PhpMvc\Actions\Responses\View;

final class HomeController extends Controller
{
    public function index(): ActionResponse
    {
        return $this->view();
    }

    public function show(int $id): ActionResponse
    {
        $model = (object) ['id' => $id, 'title' => "Item {$id}"];
        return $this->view(model: $model);
    }
}

3. Register routes

Open MyAppApp.php and register routes in the router() method:

<?php

declare(strict_types=1);

namespace App\MyApp;

use PhpMvc\MvcWebApp;
use PhpMvc\Routes\Router;
use PhpMvc\Routes\Route;
use PhpMvc\Routes\RouteMethod;
use PhpMvc\Routes\Path;
use App\MyApp\Controllers\HomeController;

final class MyAppApp extends MvcWebApp
{
    protected function router(): Router
    {
        $router = new Router();

        $router->register(Route::create(
            RouteMethod::Get,
            Path::create('/'),
            HomeController::class,
            'index',
        ));

        $router->register(Route::create(
            RouteMethod::Get,
            Path::create('/items/{int:id}'),
            HomeController::class,
            'show',
        ));

        return $router;
    }
}

4. Create a view

Create src/MyApp/Views/Home/index.html:

<!DOCTYPE html>
<html>
<head><title>My App</title></head>
<body>
  <h1>Hello from PHP MVC</h1>
  <a href="/items/1">View item 1</a>
</body>
</html>

And src/MyApp/Views/Home/show.html:

<!DOCTYPE html>
<html>
<head><title>Item {{model->id}}</title></head>
<body>
  <h1>{{model->title}}</h1>
</body>
</html>

5. Create the entrypoint

Create public/index.php:

<?php

declare(strict_types=1);

require_once __DIR__ . '/../vendor/autoload.php';

use DI\Container;
use PhpMvc\ResponseEmitter;
use Psr\Http\Message\ServerRequestFactoryInterface;
use App\MyApp\MyAppApp;
use App\MyApp\MyAppBootstrap;

$container = new Container();
MyAppBootstrap::register($container, __DIR__ . '/../');
$app = new MyAppApp(container: $container, basePath: __DIR__ . '/../');

$server = $_SERVER;
$method = is_string($server['REQUEST_METHOD'] ?? null) ? $server['REQUEST_METHOD'] : 'GET';
$scheme = (!empty($server['HTTPS']) && 'off' !== $server['HTTPS']) ? 'https' : 'http';
$host = is_string($server['HTTP_HOST'] ?? $server['SERVER_NAME'] ?? null)
    ? ($server['HTTP_HOST'] ?? $server['SERVER_NAME'])
    : 'localhost';
$rawUri = $server['REQUEST_URI'] ?? null;
$uri = $scheme.'://'.$host.(is_string($rawUri) ? $rawUri : '/');

/** @var ServerRequestFactoryInterface $requestFactory */
$requestFactory = $container->get(ServerRequestFactoryInterface::class);
$request = $requestFactory->createServerRequest($method, $uri, $server);

ResponseEmitter::emit($app->handle($request));

6. Run the built-in server

php -S localhost:8080 -t public

Open http://localhost:8080 — you should see Hello from PHP MVC.

Next steps