PoC PHP MVC Framework with Custom Router, IoC/DI Container (PSR-compliant)

πŸ”¬ Proof of Concept of the MVC in PHP

PoC PHP MVC Framework with Custom Router, IoC/DI Container (PSR-compliant)

This project is a Proof of Concept (PoC) for a custom-built PHP MVC framework that adheres to several PHP-FIG standards, including PSR-1, PSR-2, PSR-4, and PSR-11. It demonstrates how to build a lightweight MVC structure with custom routing and dependency injection (IoC/DI) while following best practices for modern PHP development.

Table of Contents

Introduction

In this PoC, we will build a small MVC application from scratch using PSR-compliant components. This includes a custom router, a basic dependency injection container, and an organized controller/view structure.

The goal of this PoC is to demonstrate the importance of clean, maintainable architecture by implementing industry-standard interfaces and coding practices. We will also use Composer to handle autoloading, complying with the PSR-4 standard.

PSRs in Use

The project complies with the following PSRs:

Directory Structure

The project follows a clear structure that separates concerns into Controllers, Views, and configuration:

/project-root
β”‚
β”œβ”€β”€β”€app
β”‚   β”œβ”€β”€β”€Config
β”‚   β”‚       BundleRegistration.php       # Handles the registration of static bundles (CSS, JS, etc.)
β”‚   β”‚       Registration.php             # Registers application services and controllers
β”‚   β”‚       
β”‚   β”œβ”€β”€β”€Controllers                      # Contains all application controllers
β”‚   β”‚       AboutController.php          # Controller for the About page
β”‚   β”‚       ApiController.php            # Handles general API endpoints
β”‚   β”‚       AuthController.php           # Handles authentication─related actions
β”‚   β”‚       ContactController.php        # Manages the Contact form
β”‚   β”‚       HomeController.php           # Handles the home and sandbox pages
β”‚   β”‚       UsersApiController.php       # API Controller for user─related endpoints
β”‚   β”‚       UsersController.php          # Controller for user management
β”‚   β”‚       
β”‚   β”œβ”€β”€β”€Models                           # Contains the application's models
β”‚   β”‚       ContactModel.php             # Model representing the contact form data
β”‚   β”‚       UserModel.php                # Model representing a user
β”‚   β”‚       
β”‚   └───Views                            # Contains the views rendered in response to requests
β”‚       β”œβ”€β”€β”€About
β”‚       β”‚       index.php                # About page view
β”‚       β”‚       
β”‚       β”œβ”€β”€β”€Auth
β”‚       β”‚       login.php                # Login page view
β”‚       β”‚       
β”‚       β”œβ”€β”€β”€Contact
β”‚       β”‚       index.php                # Contact form view
β”‚       β”‚       
β”‚       β”œβ”€β”€β”€Home
β”‚       β”‚       docs.php                 # API documentation view
β”‚       β”‚       index.php                # Home page view
β”‚       β”‚       sandbox.php              # Sandbox view for testing API requests
β”‚       β”‚       sections.php             # View demonstrating the use of sections in layouts
β”‚       β”‚       
β”‚       β”œβ”€β”€β”€Shared
β”‚       β”‚       layout.php               # Shared layout file for consistent structure
β”‚       β”‚       
β”‚       └───Users
β”‚               index.php                # View listing all users
β”‚               show.php                 # View showing user details
β”‚               
β”œβ”€β”€β”€nginx                                # NGINX configuration for running the application
β”‚       Dockerfile                       # Dockerfile to build NGINX environment
β”‚       nginx.conf                       # NGINX configuration file
β”‚       
β”œβ”€β”€β”€public                               # Publicly accessible directory (web root)
β”‚   β”‚   index.php                        # Application entry point
β”‚   β”‚   
β”‚   └───assets                           # Static assets
β”‚           scripts.js                   # Custom JS for the application
β”‚           styles.css                   # Custom styles for the application
β”‚           
β”œβ”€β”€β”€src                                  # Core application logic
β”‚   β”œβ”€β”€β”€Container
β”‚   β”‚       DIContainer.php              # Dependency Injection Container
β”‚   β”‚       
β”‚   β”œβ”€β”€β”€Controller
β”‚   β”‚       ApiBaseController.php        # Base controller for API endpoints
β”‚   β”‚       BaseController.php           # Main base controller for all standard controllers
β”‚   β”‚       
β”‚   β”œβ”€β”€β”€Core
β”‚   β”‚       Application.php              # Main Application class that bootstraps the framework
β”‚   β”‚       BundleManager.php            # Manages static asset bundles (CSS, JS)
β”‚   β”‚       SessionManager.php           # Manages session functionality
β”‚   β”‚       
β”‚   └───Router
β”‚           Router.php                   # Router class for handling routes
β”‚           
β”œβ”€β”€β”€tests                                # Unit and integration tests for the framework
β”‚   β”‚   phpunit.xml                      # PHPUnit configuration file
β”‚   β”‚   
β”‚   β”œβ”€β”€β”€Integration
β”‚   β”‚       FullAppTest.php              # Integration test covering full application behavior
β”‚   β”‚       HomeControllerTest.php       # Integration test for HomeController
β”‚   β”‚       
β”‚   └───Unit
β”‚           DIContainerTest.php          # Unit test for DI Container
β”‚           RouterTest.php               # Unit test for Router
β”‚           
β”œβ”€β”€β”€vendor                               # Composer dependencies
β”œβ”€β”€composer.json                         # Composer configuration file
└──README.md                             # Project documentation

What We Will Build

We will build a minimalistic PHP framework with the following components:

  1. Routing: A custom router that maps incoming HTTP requests (GET, POST, etc.) to controllers and actions.
  2. Dependency Injection: A basic IoC container (compliant with PSR-11) that resolves controller dependencies.
  3. MVC Pattern: We will adhere to the MVC (Model-View-Controller) architecture, separating logic into Controllers and Views.
  4. PSR-4 Autoloading: The project structure is set up for PSR-4 compliant autoloading using Composer.

Key Features:

  1. Routing:
    • Custom router that supports dynamic routes with parameters.
    • Automatic route registration using attributes.
    • Handles static files and supports trailing slashes.
  2. Dependency Injection (DI):
    • Custom DI container to manage class dependencies.
    • Services and controllers are registered through the Registration.php file.
  3. Static Bundling:
    • Bundles CSS, JS, and other assets using the BundleManager class.
  4. Session Management:
    • Custom session handling using the SessionManager.

API Documentation

The project also includes a simple REST API for user management, located at /api/v1/users/. You can find detailed documentation for this API in the API Docs view.

Example Code

The example below is a simple HomeController that renders a view:

app/Controllers/HomeController.php

<?php

namespace GuiBranco\PocMvc\App\Controllers;

class HomeController
{
    public function index()
    {
        return $this->view('home', ['title' => 'Home Page']);
    }

    public function about()
    {
        return $this->view('home', ['title' => 'About Us']);
    }
}

The front controller dispatches incoming HTTP requests:

public/index.php

<?php

use GuiBranco\PocMvc\App\Config\BundleRegistration;
use GuiBranco\PocMvc\App\Config\Registration;
use GuiBranco\PocMvc\Src\Core\Application;

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

$app = new Application(); // The core/main application class.
$registration = new Registration($app); // The user-defined registration class. Register routes, add services (DI/IoC), register API controllers.
$registration->addServices();
$registration->registerRoutes();
$registration->registerApiControllers();
$bundleRegistration = new BundleRegistration(); // The user-defined bundle registration. Use this to register assets in bundles to be rendered in the views.
$bundleRegistration->registerBundles();
$app->run(); // Run the application. Accept requests.

Finally, the IoC container and Router registration resolves the controller dependencies and the routes:

app/Config/Registration.php

<?php

use GuiBranco\PocMvc\App\Controllers\HomeController;
use GuiBranco\PocMvc\Src\Container\DIContainer;
use GuiBranco\PocMvc\Src\Router\Router;

$container = new DIContainer();
$container->set(HomeController::class, function() { return new HomeController(); });

$router = new Router();
$router->add('GET', '/', [HomeController::class, 'index']);
$router->add('GET', '/about', [HomeController::class, 'about']);

Requirements

Getting Started

  1. Clone the Repository:

    git clone https://github.com/GuilhermeStracini/poc-php-mvc.git
    cd poc-php-mvc
    
  2. Install Dependencies: Run the following command to install the required dependencies via Composer:

    composer install
    
  3. Run the Application: Start the built-in PHP server:

    php -S localhost:8000 -t public
    
  4. Access the Application: Open your browser and navigate to:

Expected Outcome

Once the application is running:

Deploy to Vercel

You can deploy this project to Vercel with just one click! Vercel is a great platform for hosting PHP projects with zero configuration. Follow the steps below to deploy this example to Vercel.

Deploy to Vercel

Steps to Deploy

  1. Click the β€œDeploy to Vercel” button above or go to Vercel Import.
  2. Import your GitHub repository by linking your GitHub account.
  3. Configure your project settings:
    • Ensure the Root Directory is set to the project root (leave this blank).
    • Set the Output Directory to public/, since this is where the index.php resides.
  4. Deploy your project and your PHP MVC framework will be live in a few seconds.

Vercel Configuration

To ensure that Vercel handles your PHP app properly, you need to add a vercel.json configuration file in your project’s root directory:

vercel.json

{
  "version": 2,
  "builds": [
    {
      "src": "public/index.php",
      "use": "@vercel/php"
    }
  ],
  "routes": [
    {
      "src": "/(.*)",
      "dest": "public/index.php"
    }
  ]
}

Running the Project with Docker

This project can be run using Docker, and we provide two variants for running the PHP 8.3 environment:

Prerequisites

Ensure you have Docker and Docker Compose installed on your machine.

Running with Apache

To run the project with PHP 8.3 and Apache, follow these steps:

  1. Build and run the containers:

    docker compose up --build
    
  2. Access the application: After the container is up and running, you can access the application in your browser at http://localhost:8080.

  3. Stopping the containers: To stop the running containers, use:

    docker compose down
    

Running with NGINX and PHP-FPM

To run the project with PHP 8.3, NGINX, and PHP-FPM, follow these steps:

  1. Ensure you have the nginx.conf file in the nginx directory: The NGINX configuration is required for routing PHP requests to PHP-FPM. The file should be located at nginx/nginx.conf.

  2. Build and run the containers:

    docker compose -f docker-compose-nginx.yml up --build
    
  3. Access the application: Once the containers are up and running, access the application in your browser at http://localhost:8080.

  4. Stopping the containers: To stop the running containers, use:

    docker compose -f docker-compose-nginx.yml down
    

Tests

Unit and integration tests are provided to validate the core functionality. Run the tests with PHPUnit:

vendor/bin/phpunit tests

Tests are organized into two directories: