Skip to content

FW Framework: The Human Guide

Welcome to the FW Framework. FW is designed to be highly deterministic, strictly typed, and driven by a clean declarative design.

This guide walks you through the practical, day-to-day operation of an FW application.

Table of Contents

  1. Configuration (.env)
  2. CLI Commands Reference
  3. Routing & Handlers (Example Endpoint)
  4. Database & ORM Usage
  5. The Development Loop
  6. Rate Limiting
  7. The AI-First Task Scheduler
  8. Project Health & Analysis

1. Configuration (.env)

FW uses the modern standard .env configuration, powered by vlucas/phpdotenv.

Environment Loading Context: Because FW is designed for persistent async workers (Swoole/RoadRunner), the .env file is loaded exactly once when the server boots (public/server.php).

This means:

  • Reading $_ENV['APP_NAME'] or getenv('APP_NAME') inside your application logic takes 0 file-IO overhead.
  • If you change a value in your .env file, you must restart the worker (docker compose restart app).
env
# Example .env snippet
APP_ENV=local
DB_CONNECTION=sqlite
DB_DATABASE=database/database.sqlite

2. CLI Commands Reference

FW is a compiled framework. You define structure via Data (YAML) and Code, then compile it.

In your application directory, run: php vendor/bin/fw <command>

  • build:api: Reads api/*.yaml and outputs strict DTOs, Handler classes, and the static build/routes.php matrix.
  • build:db: Reads database/schema.yaml and outputs native PHP Readonly Models, Repository Interfaces, and Base Repositories containing mapped SQL.
  • build:migrations: Creates simple CREATE TABLE .sql scripts in database/migrations/.
  • build:container: Scans your codebase using Reflection and generates a raw switch/case instantiation matrix in build/container.php, eliminating runtime DI container reflection.

3. Routing & Handlers

FW uses Declarative APIs. You do not write routes imperative.

Step A: Define the YAML

Create api/post.yaml:

yaml
apiVersion: fw/v1
entity: Post
endpoints:
  - method: GET
    path: /posts/{id}
    action: get

Step B: Run the Compiler

Run php vendor/bin/fw build:api.

Step C: Write the Handler

FW automatically generated modules/Post/GetPostHandler.php. You just fill in the __invoke logic.

php
<?php
declare(strict_types=1);

namespace Modules\Post;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class GetPostHandler
{
    // FW's compile-time DI container will automatically inject this!
    public function __construct(
        private PostRepositoryInterface $postRepository
    ) {}

    public function __invoke(Request $request): JsonResponse
    {
        // For dynamic params, you'd extract from the Request object.
        $post = $this->postRepository->findById(1);
        
        return new JsonResponse([
            'data' => $post
        ]);
    }
}

4. Database & ORM Usage

FW leverages a Strict Compile-Time Data Mapper instead of the traditional Active Record pattern. This ensures typed data structures and explicit query building.

Step A: Define the Schema

Create database/schema.yaml:

yaml
models:
  Post:
    id: int @primary
    title: string
    content: string

Step B: Compile the ORM

Run php vendor/bin/fw build:db.

FW just generated:

  1. PostModel: A readonly class DTO containing only typed properties for id, title, and content.
  2. BasePostRepository: A class containing pre-written raw SQL mapping functions.
  3. PostRepository: Your concrete implementation extending the Base class.

Step C: Use the Database safely and asynchronously

php
<?php
// ... inside a Handler
public function __invoke() {
    // findById was automatically generated!
    // It checked out a PDO connection from the Swoole memory pool, executed the SQL asynchronously, and hydrated a strict DTO!
    $postModel = $this->postRepository->findById(1);
    
    // Auto-complete works perfectly. It is strictly typed.
    echo $postModel->title; 
    
    // This will error! `$postModel->comments` does not exist on the DTO. 
    // You must explicitly define and call `$this->postRepository->findWithComments(1)`
}

5. The Development Loop

Because FW is compiled and runs in persistent memory, the standard workflow is:

  1. Edit your YAML or PHP logic.
  2. Run the provided developer script: ./bin/dev.sh(This sequence compiles the APIs, the DB, the DI Container, runs fw analyze, and gracefully restarts the Swoole worker).
  3. Your fast, strictly-typed application is live.

6. Rate Limiting

FW includes a high-performance, Redis-backed rate limiter that is purely declarative.

How to use:

In your api/*.yaml, add a ratelimit directive:

yaml
endpoints:
  - path: /users
    method: post
    ratelimit: 5/60  # 5 requests per 60 seconds

FW handles the rest:

  • Injected X-RateLimit-Limit and X-RateLimit-Remaining headers.
  • Returns a clean 429 Too Many Requests JSON response when exceeded.
  • Uses Psr\SimpleCache\CacheInterface (Redis by default in the skeleton).

7. The AI-First Task Scheduler

Unlike traditional PHP frameworks that rely on external Cron jobs, FW runs its own persistent scheduler loop inside the Swoole worker memory.

How to schedule tasks:

In config/dependencies.php, you can define tasks on the Scheduler instance:

php
'Fw\Framework\Task\Scheduler' => [
    'factory' => "function(\$container) {
        \$scheduler = new \Fw\Framework\Task\Scheduler(\$container, \$container->get(\Psr\Log\LoggerInterface::class));
        
        // Schedule a task every 60 seconds
        \$scheduler->schedule('cleanup', function() {
            // Your logic here
        }, 60);

        return \$scheduler;
    }"
],

Tasks are executed asynchronously and do not block the request loop.


8. Project Health & Analysis

Before deploying to production, always run the framework's self-analysis tool:

php vendor/bin/fw analyze

What it checks:

  • Build Artifacts: Ensures DI container and routes are compiled (crucial for performance).
  • Configuration: Verifies .env presence.
  • Static Analysis: Runs PHPStan (Level 5) on all modules to catch type-mismatch bugs early.
  • Health Score: A benchmark of your project's technical debt and production readiness.

Engineered for Agents. Released under the MIT License.