Tutorial Fat Free Framework

Part 6: Real-World Tips

Production advice, honest comparisons, and deployment checklist

You've learned the core of Fat Free Framework across this series: routing, templates, databases, configuration, and utilities. Now it's time for the honest conversation — when should you actually use F3, and when should you choose something else?

This final part covers practical advice for production: an honest comparison with Laravel and Symfony, performance tips, testing with F3's built-in tools, and a deployment checklist.

Series Recap
Part 1 → Getting Started
Part 2 → Routing & Controllers
Part 3 → Views & Templates
Part 4 → Database & ORM
Part 5 → Configuration & Utilities

1. When to Use F3 (and When Not To)

F3 isn't the right tool for every job, but it excels in specific scenarios. Here's an honest assessment.

Use F3 When

  • Building small-to-medium apps — blogs, internal tools, dashboards, REST APIs with under 50 routes
  • Rapid prototyping — when you need to validate an idea in hours, not days
  • Minimal dependencies matter — shared hosting, limited RAM, or containers with size constraints
  • You want freedom — no enforced structure, use closures or controllers, your choice
  • Team knows PHP — no need to learn Blade, Twig, or Artisan conventions

Don't Use F3 When

  • Large enterprise with 20+ developers — you need enforced conventions and extensive tooling
  • Need built-in queues, WebSockets, or admin panels — Laravel's Horizon, Echo, and Nova don't have F3 equivalents
  • Package ecosystem is critical — if your project depends on specific Laravel packages, use Laravel
  • Client/team expects a certain framework — sometimes the business decision outweighs the technical one
Senior Engineers: The Honest Truth
F3 trades ecosystem size and opinionation for simplicity and speed. You get: faster boot time, less memory, no vendor lock-in. You give up: enforced architecture, massive package library, and a large talent pool of "Laravel developers."

For greenfield projects with small teams who value simplicity, F3 is a legitimate choice. For projects with complex domain logic or many integrations, the time you save on setup may be lost reinventing what Laravel provides.

2. F3 vs Laravel vs Symfony

An honest, side-by-side comparison for decision-making:

AspectF3LaravelSymfony
Core sizeSingle file (~90kb)Large (~10MB)Very large (~30MB)
Boot timeVery fastModerateModerate-slow
Memory usageLowModerateHigher
Learning curveHoursDays-weeksWeeks-months
OpinionationMinimalStrongVery strong
EcosystemSmallLargeLarge
ORMSQL MapperEloquentDoctrine
TemplatesF3/Twig/anyBladeTwig
Best forAPIs, small appsFull-stack, teamsEnterprise
Framework Size Isn't Everything
Laravel's larger size includes batteries: queues, WebSockets, mail, notifications, broadcasting. If you need those, the extra megabytes are worth it. If you don't, you're making your users wait for code they'll never use.

3. Performance Tuning

F3 is already fast, but here's how to optimize for production.

Essential Production Settings

// config/prod.ini
[globals]
DEBUG = 0
CACHE = true
TZ = UTC
HIGHLIGHT = false

Cache Strategy

  • Route caching — Cache static pages (about, contact) with TTL 3600+
  • Query caching — Cache expensive lookups (categories, config data)
  • Memory backend — Use APC or Memcached instead of filesystem
  • Disable in development — Turn off CACHE while iterating
// Route caching example
$f3->route('GET /about', 'Page->about', 3600);  // Cache 1 hour
$f3->route('GET /contact', 'Page->contact', 86400);  // Cache 1 day

// Query caching example
$categories = $db->exec('SELECT * FROM categories', null, 3600);
Don't Optimize Prematurely
F3 is lightweight and fast in practice — fast boot time, low memory overhead. If your app is slow, the bottleneck is probably your database, not the framework. Profile first with Xdebug or Blackfire before optimizing PHP code.

4. Profiling & Debugging

Useful Debug Settings

// Development settings
$f3->set('DEBUG', 3);           // Maximum verbosity
$f3->set('HIGHLIGHT', true);   // Syntax-highlighted stack traces
$f3->set('HALT', false);       // Don't stop on warnings

SQL Query Logging

// After running queries, check the log
$db = $f3->get('DB');
echo '<pre>' . $db->log() . '</pre>';

// Or log to file
file_put_contents(
    'logs/queries.log',
    $db->log(),
    FILE_APPEND
);

Profiling Tools

  • Xdebug — Step-through debugging, profiling, cache grind
  • Blackfire — Production-safe profiling (commercial)
  • Blackfire-like alternative — Use microtime(true) around critical sections
// Simple profiling
$start = microtime(true);

// Your code here
$results = $db->exec('SELECT * FROM large_table');

$elapsed = round((microtime(true) - $start) * 1000, 2);
echo "Query took {$elapsed}ms";

5. Error Handling & Logging

Proper error handling separates hobby projects from production applications.

Production Error Handler

$f3->set('ONERROR', function($f3) {
    // Log the error
    $logger = new \Log('logs/errors.log');
    $code = $f3->get('ERROR.code');
    $text = $f3->get('ERROR.text');
    $trace = $f3->get('ERROR.trace');

    $logLine = sprintf(
        "[%s] [%d] %s in %s:%d\n",
        date('Y-m-d H:i:s'),
        $code,
        $text,
        $trace[0]['file'] ?? 'unknown',
        $trace[0]['line'] ?? 0
    );
    $logger->write($logLine);

    // Show user-friendly error page
    http_response_code($code);
    echo \Template::instance()->render('errors/' . $code . '.htm');
});
Logging Best Practices
• Log errors to a file, never to the browser in production
• Rotate logs daily to prevent disk filling
• Include timestamp, error code, message, and file location
• Set proper file permissions on log directory (not world-writable)

6. Testing with F3

F3 includes a built-in Test class for unit testing. It's simple but effective for verifying your code works.

Basic Testing

<?php
require 'vendor/autoload.php';
$f3 = \Base::instance();

$test = new \Test;

// Test a function exists
$test->expect(
    is_callable('my_function'),
    'my_function() is callable'
);

// Test return value
$result = my_function();
$test->expect(
    $result === 42,
    'my_function() returns 42'
);

// Display results
foreach ($test->results() as $r) {
    echo ($r['status'] ? 'PASS' : 'FAIL') . ': ' . $r['text'] . "\n";
}

Testing Routes with Mock Requests

F3 can simulate HTTP requests without a browser, making it easy to test routes:

// Suppress route output during test
$f3->set('QUIET', true);

// Simulate a GET request to /about
$f3->mock('GET /about');

// Check the response
$response = $f3->get('RESPONSE');
$test->expect(
    strpos($response, 'About') !== false,
    'About page contains "About"'
);

// Test a route with parameters
$f3->mock('GET /user/42');
$id = $f3->get('PARAMS.id');
$test->expect(
    $id === '42',
    'User ID parameter is captured correctly'
);

// Test POST with form data
$f3->mock('POST /login', ['email' => '[email protected]']);
$email = $f3->get('POST.email');
$test->expect(
    $email === '[email protected]',
    'POST data is accessible'
);

// Re-enable output
$f3->set('QUIET', false);
$f3->clear('ERROR');
When to Use F3 Test vs PHPUnit
F3's Test class is great for:
• Quick smoke tests
• Verifying route behavior
• Simple assertions without dependencies

Use PHPUnit when:
• You need mocking, data providers, or test suites
• Running CI/CD pipelines that expect PHPUnit output
• Testing complex class hierarchies

7. Security Checklist

Before deploying any F3 application, verify these security measures:

CheckActionWhy
DEBUGSet to 0Prevents stack trace leaks
SQL injectionUse parameterized queries onlyPrevents data theft
XSSRely on auto-escaping (templates)Prevents script injection
CSRFCheck POST with token or originPrevents forged requests
PasswordsUse password_hash()/password_verify()Never md5 or sha1
SessionsRegenerate ID after loginPrevents session fixation
File uploadsValidate type and sizePrevents malicious uploads
HTTPSRedirect all traffic to HTTPSEncrypts data in transit
// Session fixation prevention (after login)
session_regenerate_id(true);
$f3->set('SESSION.user_id', $user->id);

// CSRF protection example
$f3->route('POST /delete', function($f3) {
    if ($f3->get('POST.csrf_token') !== $f3->get('SESSION.csrf_token')) {
        $f3->error(403, 'Invalid CSRF token');
        return;
    }
    // Safe to process delete
});

8. Deployment Checklist

Use this checklist before deploying to production:

  • DEBUG = 0 — No stack traces in production
  • CACHE enabled — Use APC or Memcached if available
  • HTTPS configured — SSL certificate installed and forced
  • Session security — Secure, HttpOnly, SameSite cookies
  • Error logging — Logs go to file, not browser
  • File permissions — config files not world-readable, logs writable
  • Database credentials — Not hardcoded, use environment variables
  • Timezone setTZ = UTC or your local timezone
  • Unused routes removed — No debug or test endpoints exposed
  • Web server configured — Apache or Nginx, not PHP built-in server

Sample Nginx Configuration

server {
    listen 80;
    server_name example.com;
    root /var/www/myapp;
    index index.php;

    location / {
        try_files $uri /index.php?$query_string;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    location ~ /\. {
        deny all;
    }
}

Sample Apache Configuration (.htaccess)

RewriteEngine On
RewriteBase /

# Redirect to HTTPS (optional)
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

# F3 front controller - send all requests to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]

Series Complete!

Congratulations — you've completed the Fat Free Framework tutorial series. Here's what you learned:

  • Part 1 — Installation, Hello World, project structure
  • Part 2 — Routing, parameters, controllers, filters
  • Part 3 — Template engine, includes, data passing
  • Part 4 — SQL Mapper, CRUD, relationships, raw SQL
  • Part 5 — Configuration, caching, sessions, i18n, plugins
  • Part 6 — Real-world tips, testing, deployment (you are here)

F3 won't be the right choice for every project, but it's a powerful tool to have in your belt. For small APIs, internal tools, and rapid prototypes, it's hard to beat a lightweight framework that ships fast and keeps things simple.

The best framework is the one that gets your project shipped. Now go build something.

Resources
Official documentation: fatfreeframework.com
GitHub: fatfree-core
Community: Stack Overflow, Matrix chat