Skip to main content
Version: CANARY 🚧

Concept and Architecture

Introduction​

BowPHP is a lightweight PHP framework designed to provide a clear and modular structure. When a request comes in, it follows a well-defined path called the request lifecycle. This cycle is organized to separate responsibilities, make the code easy to maintain, and improve performance.

The request lifecycle explained​

Every request goes through several stages before generating a response for the user.

Here are the main stages:

  1. HTTP Request: The user sends a request through their browser or an API.
  2. Request: The framework captures this request and extracts the necessary data (URL, parameters, body, etc.).
  3. Kernel: The Kernel is the heart of the framework. It:
  • Loads the configuration files located in the config folder (such as config/database.php).
  • Initializes the modules required by the application.
  1. Application: It starts the Kernel and prepares the environment to handle the request.
  2. Router:
  • The router detects the route matching the request (for example, /users/42 or /api/login).
  • It associates this route with a specific controller and method, defined directly in the route code.
  • If middlewares are defined for this route, they are executed before calling the controller.
  1. Middleware: Middlewares perform specific tasks, such as checking authentication or validating data.
  2. Controller: Once the route is validated, the controller runs the business logic (for example, retrieving data or performing an action).
  3. Model: If data is needed, the model interacts with the database to retrieve or update it.
  4. View: The controller passes the retrieved data to the View component, which generates the final content (HTML, JSON, etc.).
  5. HTTP Response: The response is sent to the user.

The role of the router in detail​

The router is a key component of BowPHP. It acts as a "compass" that directs each request to the right controller and the right method.

Route detection​

The router examines the request URL and determines which route matches. Routes are defined in dedicated files.

Example of a route definition:

routes/app.php
$app->get('/users/:id', 'UserController::show');
$app->post('/api/login', 'AuthController::login');
tip

Here:

  • The GET /users/42 request is associated with the show method of UserController.
  • The POST /api/login request is associated with the login method of AuthController.

Bow also supports PHP 8 attribute-based routing, which lets you declare routes directly on the controller class without a centralized routes file:

use Bow\Router\Attributes\{Controller, Get};

#[Controller(prefix: '/api/users', middleware: ['auth'])]
final class UserController
{
#[Get('/:id', name: 'show', where: ['id' => '\d+'])]
public function show(int $id) { /* ... */ }
}

// Dans routes/app.php
$app->register(UserController::class);

See Routing > Attribute-based routing for details on the available attributes.

Association with the controller​

Once the route is detected, the router:

  • Validates the URL parameters (such as :id).
  • Runs the middlewares defined for this route (if any).
  • Calls the controller and method associated with the route.

A modular architecture​

tip

BowPHP is built on a modular architecture. Each module is independent, has its own configuration, and serves a specific role. This makes the application easy to manage and extend. The router is one example; the Database module, which manages database connections, is another.

The contract: Bow\Configuration\Configuration​

All modules follow the same contract: each one exposes a class that extends Bow\Configuration\Configuration and implements two methods:

  • create(Loader $config) β€” declares the bindings in the container
  • run() β€” starts the module (useful for eager resources)

The Kernel (your app/Kernel.php, which extends Bow\Configuration\Loader) lists these providers in its configurations() method. At startup, the Kernel instantiates them, loads the config/*.php files, then calls create() followed by run() on each one.

Example: The Database module​

Let's take the case of the Database module, which manages connections to the database:

Configuration file: config/database.php

This file contains the parameters needed to connect to the database. Here is a simple example:

config/database.php
return [
'default' => 'mysql',
'connections' => [
'mysql' => [
'driver' => 'mysql',
'host' => '127.0.0.1',
'port' => 3306,
'database' => 'bowphp_app',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
],
],
];

The Bow\Database\DatabaseConfiguration class

This class (which extends Bow\Configuration\Configuration) reads config/database.php, configures the PDO connections, and exposes db / database in the container.

Registration in the Kernel

app/Kernel.php
namespace App;

use Bow\Configuration\Loader;
use Bow\Database\DatabaseConfiguration;

class Kernel extends Loader
{
public function configurations(): array
{
return [
DatabaseConfiguration::class,
// ... other providers (Cache, Mail, Queue, etc.)
];
}
}

At startup, the Kernel loads config/database.php, instantiates DatabaseConfiguration, then calls its create() and run() methods. The connection is then ready to be used by controllers, Barry models, and the other components.

Visualization with a diagram​

Here is a visual representation to better understand the request lifecycle and modular integration (example with Database):

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Framework Bow β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Request │───▢│ Kernel │───▢│ Routeur │───▢│ Middleware β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β–² β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ Charge les β”‚ β”‚
β”‚ β”‚ β”‚ modules β–Ό β”‚
β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ β–Ό β”‚ ContrΓ΄leur β”‚ β”‚
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚ Config β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β”‚ β–Ό β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ RΓ©ponse │◀───────────────────────────────────▢│ ModΓ¨le β”‚ β”‚
β”‚ β”‚ HTTP β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β–² β”‚ β”‚
β”‚ β”‚ β–Ό β”‚
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ └─────────│ Vue │◀─────────────────────│ Base de donnΓ©es β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Flux : Request → Kernel → Routeur → Middleware → Contrôleur → Modèle → Vue → Réponse

Summary of the key points​

Summary
  • Structured lifecycle: A request follows a precise path (Request β†’ Kernel β†’ Router β†’ Middleware β†’ Controller β†’ Model β†’ View β†’ Response).
  • Role of the router: The router detects the route matching the request and calls the associated controller and method.
  • Kernel: Orchestrates the loading of configurations from the config folder and initializes the modules.
  • Modularity: Each component (router, database, etc.) is independent, which simplifies management and improves maintainability.

Thanks to the contributors​

Thank you for considering contributing to BowPHP! The contribution guide can be found in the documentation.

Is something missing?

If you run into problems with the documentation or have suggestions to improve the documentation or the project in general, please open an issue for us, or send a tweet mentioning the Twitter account @bowframework or directly on github.