Tutorial Fat Free Framework

Part 1: Introduction & Getting Started

Your first steps with a lightweight PHP micro-framework

Imagine you want to build a simple web app — maybe a contact form, a blog, or a small API. You search for "PHP framework" and find Laravel or Symfony. You read the docs, and suddenly you're looking at artisan commands, service providers, middleware pipelines, and a dozen folders to learn before you write a single line of code.

What if there was another way? A framework that lets you write Hello, world in 5 lines — not 5 files?

That's Fat Free Framework. And this blog series will teach you how to use it.

What is Fat Free Framework?

Fat Free Framework (F3) is a PHP micro-framework that weighs in at just ~90 kilobytes. It's small enough to fit in your pocket, but has every tool you actually need to build real web applications.

Think of it like a Swiss Army knife. A full-size kitchen knife (Laravel) is great for professional chefs building large restaurants. But if you just need to slice an apple, the Swiss Army knife gets the job done — and you can carry it anywhere. F3's smaller footprint also means cheaper hosting: shared hosting with limited resources can run F3 apps that would choke on Laravel's memory requirements.

Under the Hood
F3 was created by Bong Cosca in 2009 as a reaction against "enterprise bloat." The entire framework lives in a single file: base.php. No service container, no dependency injection container, no complex bootstrapping.

Instead, F3 uses a registry pattern — a simple key-value store accessed via $f3->set() and $f3->get(). For senior engineers: this trades the type-safety and testability of DI for simplicity. F3's registry works, but you lose compile-time checks and the ability to easily mock dependencies in tests. Current version: 3.9.x (PHP 8.0+).

What Can You Build with F3?

F3 isn't just for "Hello World" demos. It ships with a full toolkit that handles real work:

FeatureWhat It DoesExample Use
Routing EngineMaps URLs to code/blog/my-post shows that post
Template EngineRenders HTML with logicDynamic pages, email templates
SQL MapperTalks to databases without raw SQLUser accounts, blog posts
Cache EngineSpeeds up repeated requestsPage cache, API responses
i18nMultilingual supportSpanish, French, Japanese sites
Audit & SecurityDNSBL spam blocking, input validationContact forms, login pages

People have built: REST APIs, content management systems, e-commerce sites, URL shorteners, image galleries, and internal business tools.

Why Choose a Micro-framework?

A micro-framework gives you the essentials and gets out of your way. Here's what that means in practice:

Benefits
  • Fast to learn — You can be productive in hours, not weeks. No need to memorize dozens of framework-specific CLI commands or config file formats.
  • Fast to install — One composer require and you're done. No php artisan key:generate, no .env setup, no config files.
  • Fast to run — ~90kb loads instantly. For context: framework boot time (~50-100ms for Laravel) is rarely the bottleneck in real apps. F3's advantage is a simpler mental model and less memory overhead, not measurably faster TTFB.
  • No boilerplate — Write only the code your app needs. Delete what you don't use.
  • Flexible — Use native PHP, Twig, Smarty, or F3's own template engine. Your choice.
When NOT to Use F3
F3 isn't for every project. If you need: team-enforced architecture (like Laravel's conventions), a large ecosystem of first-party packages, or enterprise features like job queues and WebSocket servers — a full framework might be better.

For senior engineers: F3 has community plugins, but a smaller ecosystem than Laravel's Packagist packages. If your project depends on specific Laravel-only packages (Socialite, Horizon, Nova), factor that into your decision. F3 is best for small-to-medium projects, APIs, rapid prototyping, and teams who prefer freedom over conventions.
FrameworkCore SizeLearning CurveBest For
Fat Free~90kbEasySmall apps, APIs, prototyping
Slim~200kbEasyAPIs, microservices
CodeIgniter 4~1.5MBEasyTraditional MVC, legacy migration
Laravel~10MBModerateFull-stack apps, teams
Symfony~30MBSteepEnterprise systems

Installation

F3 has exactly one requirement: PHP 8.0 or later (for version 3.9). No Apache modules required. No vendor lock-in.

Note: F3 doesn't bundle database drivers — you'll still need PHP extensions like pdo_mysql or pdo_sqlite installed if your app uses a database. Check with php -m | grep pdo.

Check Your PHP Version
Run this in your terminal:

php -v

F3 3.9 requires PHP 8.0+. If you're using F3 3.8, PHP 7.2+ works. Check the system requirements for your version.

Option A: Composer (Recommended)

Composer is PHP's package manager. If you don't have it, install it first.

# Create your project folder
mkdir my-app && cd my-app

# Install F3
composer require bcosca/fatfree-core

This creates a vendor/ folder and sets up autoloading automatically. When you need F3, just require the autoloader:

require 'vendor/autoload.php';
Under the Hood
Composer's autoloader supports PSR-4, PSR-0, and classmap autoloading. For F3, it uses PSR-4 to map the Base class to vendor/bcosca/fatfree-core/base.php. When you call \Base::instance(), PHP checks Composer's autoload registry, loads the file, and returns the singleton. No manual require statements needed.

Option B: Manual Download (No Composer)

If you prefer not to use Composer, download the framework directly:

  • Go to github.com/bcosca/fatfree-core
  • Click the green "Code" button → "Download ZIP"
  • Extract the ZIP and find base.php
  • Copy base.php into your project folder

Hello World in 5 Lines

Create a file called index.php and paste this:

<?php
$f3 = \Base::instance();

$f3->route('GET /', function() {
    echo 'Hello, world!';
});

$f3->run();
Line-by-Line Breakdown
Line 1: \Base::instance() — Creates (or returns) the single F3 object. This is where all routes, config, and data live.

Line 3-5: $f3->route(...) — Tells F3: "When someone visits / with a GET request, run this function."

Line 7: $f3->run() — Starts F3. It looks at the current URL, finds the matching route, runs the function, and sends the response.
Under the Hood: The Routing Engine
When you call $f3->route(), F3 doesn't run anything yet. It stores the route in an internal array called $f3->routes. Each route has three parts: HTTP method, URL pattern, and callback.

When $f3->run() is called, F3 reads $_SERVER['REQUEST_METHOD'] and $_SERVER['REQUEST_URI'], then iterates through $f3->routes to find the first match. The matched callback receives the F3 instance and any captured URL parameters.
Try It Now
1. Save the code above as index.php

2. Run the built-in PHP server:
php -S localhost:8000

3. Open your browser to http://localhost:8000

Expected output: A blank page with the text "Hello, world!"

How a Request Flows Through F3

When you visit a URL, here's what happens inside F3:

         Your Browser
              │
              ▼
    ┌─────────────────┐
    │   HTTP Request   │   GET http://localhost:8000/
    └────────┬────────┘
             │
             ▼
    ┌─────────────────┐
    │   index.php      │   PHP script starts
    └────────┬────────┘
             │
             ▼
    ┌─────────────────┐
    │ Base::instance() │   Create F3 singleton
    └────────┬────────┘
             │
             ▼
    ┌─────────────────┐
    │  $f3->route()    │   Register route (store in array)
    └────────┬────────┘
             │
             ▼
    ┌─────────────────┐
    │  $f3->run()      │   Start the framework
    └────────┬────────┘
             │
             ▼
    ┌─────────────────┐
    │  Match route?    │   GET /  →  Yes, match found
    └────────┬────────┘
             │
             ▼
    ┌─────────────────┐
    │  Run callback    │   echo "Hello, world!"
    └────────┬────────┘
             │
             ▼
    ┌─────────────────┐
    │  Send response   │   HTTP 200 + "Hello, world!"
    └────────┬────────┘
             │
             ▼
         Your Browser
         sees the page

Project Structure: Start Simple, Grow Later

For now, one index.php file is all you need. But as your app grows, here's a structure that works well:

my-app/
├── index.php              # Entry point - all routes here
├── vendor/                # Composer packages
├── src/
│   ├── controllers/       # Route handlers (later)
│   └── models/            # Database classes (later)
├── views/                 # HTML templates (later)
└── public/                # CSS, JS, images

Don't create all these folders now. Start with index.php. When complexity grows — multiple route handlers, shared logic, or the file becomes hard to navigate — split routes into a controllers/ folder. For experienced devs: refactor based on complexity and coupling, not just line count.

Under the Hood: MVC
F3 doesn't enforce MVC (Model-View-Controller), but it supports it. Models handle data (database queries). Views handle presentation (HTML templates). Controllers connect them (route handlers). Many F3 apps skip the strict separation — and that's fine for small projects. For senior engineers: F3 gives freedom, but consistent MVC separation pays dividends on team projects and long-term maintenance. The framework lets you choose; use as much or as little structure as your project needs.

System Requirements

F3 is intentionally minimal. Here's everything you need:

RequirementMinimumRecommended
PHP Version8.08.3
Web ServerNone (built-in works)Apache, Nginx, Caddy
DatabaseNone (optional)MySQL, SQLite, PostgreSQL, MongoDB
ExtensionsNonepdo_mysql, pdo_sqlite, or pdo_pgsql (depending on database)

You can run F3 with PHP's built-in development server — no Apache or Nginx needed for development:

# Run on port 8000 (default)
php -S localhost:8000

# Run on a different port
php -S localhost:3000

# Run on all interfaces (development only!)
php -S 0.0.0.0:8000
Production Tip
The built-in PHP server is for development only. For production, use Apache, Nginx, or Caddy. F3 works with all of them — see the server configuration guide.

Troubleshooting

Running into issues? Here are the most common problems:

ErrorCauseFix
"php" is not recognizedPHP not installed or not in PATHInstall PHP or use full path: /usr/bin/php
Port already in useAnother service using that portUse a different port: php -S localhost:3000
Blank pagePHP error is hiddenAdd ini_set('display_errors', 1); at top of index.php
404 Not FoundRoute doesn't match URLCheck your route definition matches the URL exactly
Composer command not foundComposer not installedInstall from getcomposer.org
Permission denied (port 80)Ports below 1024 need rootUse a higher port (8000) for dev. For production, use Nginx as reverse proxy.
Debug Tip
⚠️ Development only! Remove this in production — exposing errors leaks file paths, database info, and stack traces.

Add this at the top of your index.php during development:

ini_set('display_errors', 1); error_reporting(E_ALL);

This shows all PHP errors in the browser instead of a blank page. For production, log errors to a file instead.

What's Next?

You now have F3 running. In Part 2: Routing & Controllers, we'll go deeper:

  • Route parameters — /user/42 and capturing values
  • HTTP methods — GET, POST, PUT, DELETE
  • Route groups — shared prefixes and filters
  • Controllers — organizing code into classes
  • Before/after filters — authentication, logging

The best way to learn is to do. You've already built your first F3 app. Now let's build something real.

Resources