Skip to main content

Configuring the Fat-Free Framework

Configuring the Fat-Free Framework requires:

  • htaccess – Routes page requests to index.php
  • index.php – Gets the instance of Fat-Free and executes it
  • config.ini – Configuration information and site data
  • routes.ini – Sets the controller for each page
  • controllers – Code that generates the response to requests

htaccess

htaccess routes requests for pages to index.php.

# Enable rewrite engine and route requests to framework

php_flag short_open_tag Off

RewriteEngine On

# Some servers require you to specify the `RewriteBase` directive
# In such cases, it should be the path (relative to the document root)
# containing this .htaccess file
#
RewriteBase /

RewriteRule ^lib - [R=404]

RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php [L,QSA]
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

Note: These instructions are placed after redirects for https & www (or no www), and changed and deleted pages.

What’s happening:

htaccess tells an Apache server to:

  • check if the request contains the string tmp/ or ends in .ini – if it does, reject the request with a 404 Page not found response
    • The tmp directory and the ini files are private to Fat-Free
  • check if the request is for an existing symbolic link, file, or directory – if it is, Apache will process the request
    • %{REQUEST_FILENAME} – is a server variable for the name of the requested file (or link or directory)

If the request is not for an existing link, file, or directory (the conditions), then the request is passed to index.php.

php_flag short_open_tag off – needs to be set to off. Having it set to on can cause problems with the opening <?xml tag for the site map. Some servers default to having short_open_tag set to on.

index.php

index.php sets up the framework and processes the request for the desired page.

<?php
// Retrieve instance of the framework
$f3=require('lib/base.php');

// Load configuration
$f3->config('app/config.ini');

// Define routes
$f3->config('app/routes.ini');

// Define combined variable for scheme, host, and base
$schemeHost = $f3->get('SCHEME').'://'.$f3->get('HOST').$f3->get('BASE');
$f3->set('schemeHost',$schemeHost);

// Define error response
$f3->set('ONERROR','UtilitiesController->error');

$f3->run();

What’s happening:

index.php gets an instance of the framework, loads the configuration and route information, sets the schemeHost variable and the function for error handling, and runs the instance of the framework.

  • schemeHost – I set a variable for the site address as I’m running the same code on two systems: a localhost version for development and the demo site, and it’s a little easier to call one variable rather than building the site address every time I need it.

config.ini

config.ini tells Fat-Free where to find the files it needs and defines site variables.

[globals]
; Where the framework autoloader will look for app files
AUTOLOAD=app/
; Change the next line to 0 for production
DEBUG=3
; Where the language files are located
LOCALES=dict/
; Where errors and other information is logged
LOGS=logs/
; Where the framework will look for templates and HTML-support files
UI=app/ui/
; Site owner / company name
siteOwner="SBF Consulting"
; Site name
siteName="Site Construction"
; Year site was created (use for copyright text in the footer)
yearBuilt="2018"
; Email address for the footer and for messages from the contact form
contactAddress = "customer.service@mywebsite.com"
; Limit for # of characters in the message for a contact form
contactMessageLimit = 500
; CSS / JavaScript revision date (change whenever a file is updated)
cacheUpdate = "20240827"
----- There is more in the file on GitHub -----

What’s happening:

Site values, such as the email address, are defined in config.ini. Values specific to one page are defined in that page’s controller or HTML file.

To add a new page to the site:

  • Add the page slug and title to the DropdownMenu list
  • Add the page to the routes.ini file
  • Add the page to the PageController file
  • Create the page HTML content file
  • Repeat for each language

To add an additional language:

  • Add the language to the menu list
  • Define the menus for the new language
  • Create the routes
  • Create the controllers
  • Create the content pages
  • Create a dict file

routes.ini

Routes direct a page request to the function that will provide the page based on the address.

[routes]
;GET name address = class->function to render the page
GET /=UtilitiesController->getLanguage
GET /sitemap.xml=CommonPageController->getSitemap

GET @en_home: /en/home=enPageController->RenderHome
GET @en_details1: /en/bootstrap=enPageController->renderDetails1
GET @en_details2: /en/fatfree=enPageController->renderDetails2
GET @en_details3: /en/ffconfig=enPageController->renderDetails3
GET @en_details4: /en/templates=enPageController->renderDetails4
GET @en_details5: /en/details=enPageController->renderDetails5
GET @en_contactx: /en/contact=CommonPageController->contactNew
POST @en_contactx: /en/contact=UtilitiesController->contactPost
GET @en_termsx: /en/terms=CommonPageController->renderTerms

What’s happening:

Each line defines: method (GET or POST), route name (used by the template engine for links to that page from other pages); URL address; and the controller function that will create the page.

Requests for index files

If a user includes an index file as part of a page address, they’ll be rerouted to the root directory, rather than receiving a Page not found error:

GET /en/index.htm=UtilitiesController->indexOverride
GET /en/index.html=UtilitiesController->indexOverride
GET /en/index.php=UtilitiesController->indexOverride

Controller.php & PageController.php

Controller.php defines the overall controller class. That class is extended with the PageController classes (e.g. enPageController).

In addition to the function called by the route definition, Fat-Free also calls a beforeroute and an afterroute function. The same before and after functions are used for all languages.

beforeroute – Controller.php

// HTTP route pre-processor
function beforeroute($f3) {
    $f3->set('googleNoIndex',false); // Set to true when needed
    $f3->set('isHomePage',false); // Set to true for home pages
    $f3->set('isDetailPage',false); // Set to true for detail pages
    $f3->set('pageCSS',false); // Add file name if needed
    $f3->set('highlighterJavaScript',false); // Set to true for code sections
    $f3->set('pageJavaScript',false); // Add file name if needed
}

What’s happening:

Set flags for things that are not required for most pages and change the flag in that page’s controller function when needed:

  • googleNoIndex: normally false – the only pages not indexed are the Contact and Terms of Use pages and the error pages (404 and 500).
  • isHomePage: normally false – flag for the home page (it has different functionality).
  • isDetailPage: normally false – flag for the detail pages (just need for the navigation template).
  • pageCSS: normally false – if CSS is required for just one or two pages, the CSS can be loaded by setting the value of pageCSS to the CSS file name. The code only supports one file, so if two CSS files are needed, the files should be combined into one file.
  • highlighterJavaScript: normally false – flag for loading highlight.js to highlight code syntax.
  • pageJavaScript: normally false – if a special JavaScript function is required for just one page, the JavaScript can be loaded by setting the value of pageJavaScript to the template file name. I use this for loading the JavaScript for the special effects on the 404 page not found page and the 500 server error page. The code only supports one file, so if two JavaScript files are needed, the files should be combined into one file.

Route Function – PageController.php

function loadCommonSettings($f3) {
    $f3->set('LANGUAGE','en');
    $f3->set('langSubdirectory','en');
}
                    
function RenderHome($f3) {
    $this->loadCommonSettings($f3);
    $f3->set('pageTitle','Learning Frameworks');
    $f3->set('pageDescription','A frontend / backend setup for a simple,
        multi-language website using Bootstrap 5 with the Fat-Free PHP
        framework, but no database.');
    $f3->set('isHomePage',true);
    $f3->set('loadID',
        function($array,$count) {
            reset($array);
            for ($x = 0; $x < $count; $x++) {
                next($array);
            }
            return key($array);
        }
    );
    // Set the content source
    $f3->set('pageContent',$f3->get('ALIAS').'.html');
}

What’s happening:

Set page specific items in the route function (data that’s not contained in the content page):

  • loadCommonSettings – a local function to set values that are common to all pages for that language:
    • LANGUAGE – set the Fat-Free system variable to the language code for the current language. Fat-Free uses this value for selecting the language dictionary.
    • langSubdirectory – This is the language subdirectory.
  • pageTitle – for the title tag and for social media
  • pageDescription – for the description meta tag and for social media
  • loadID – A custom function for pulling the slug value from the config.ini menus for the single page menu (only for the home page)
    • The slug values could have been manually included in the template, but I wanted experience with using a custom function.
  • pageContent – set the file for the page content – the file name is based on the route’s alias (which is the same text string as the name of the content file)

afterroute – Controller.php

// HTTP route post-processor
function afterroute($f3) {
    // Sorted the languages for the language menu
    $lang_array = $f3->get('languages');
    uasort($lang_array, function ($a,$b){
        return ($a[2] < $b[2]) ? -1 : 1;
    });
    
    // If a regular route, use the alias, otherwise
    // point the language menu to the home page (e.g. for error pages)
    if ($f3->exists('ALIAS',$alias_base)) {
        $alias_base = substr($alias_base,2); // the part after the lang prefix
    } else {
        $alias_base = '_home';
    }

    $language_links_array = [];
    foreach($lang_array as $key=>$details) {
        array_push($language_links_array,
            [$key.$alias_base,$key,$details[0],$details[2]]
        );
    }
    $f3->set('languageLinks',$language_links_array);
    $pageLanguage = explode(',',$f3->get('LANGUAGE'));
    $f3->set('langCode',$pageLanguage[0]); // Use langCode for the html lang tag
    $lang_prefix = $f3->get('langSubdirectory'); // for local use in this function
    $f3->set('mainMenu', $f3->get($lang_prefix.'HomeMenu'));
    $f3->set('dropdownMenu', $f3->get($lang_prefix.'DropdownMenu'));
    
    // Render HTML layout
    echo \Template::instance()->render('layout.html','text/html');

What’s happening:

Set page specific items in the after function:

  • Setup an array of the corresponding language pages for the preflang links and the language switcher:
    • lang_array – get the array of languages from config.ini – the default language is first and then the other languages (the order doesn’t matter)
    • uasort – PHP sort function – sort the array in Unicode order based on the language name (uasort preserves the key values)
    • alias_base – Get the base portion of the alias (e.g. en_details1 => _details1) – combine it with the other language prefixes to generate the route names to the other language versions of the same page
    • language_links_array – Create a new array to be passed to the template engine
    • loop through the lang_array, pushing an array for each language to the language_links_array – each array contains the route name for the language versions, the language prefix (used for the language flag), and the language name.
    • languageLinks – pass the just created array to Fat-Free to be used for the language menu
  • langCode – set the value for the lang attribute in the html tag.
  • Set the mainMenu – combine the langCode with “HomeMenu” to generate the language specific menu (this does require that each menu have the same naming structure).
  • Set the dropdownMenu – combine the langCode with “DropdownMenu” to generate the language specific menu (this does require that each menu have the same naming structure).
  • Echo the results from the template engine to generate the page content.

CommonPage­Controller.php

The PageControllers display the general site content. CommonPageController.php extends Controller.php to display the pages that are common across all languages or that are used for site management:

  • Contact
  • Terms of Use | Privacy Policy
  • 404 Page not found
  • Other errors (e.g. 500 Internal server error)

Utilities­Controller.php

The UtilitiesController.php handles:

  • getLanguage – requests for the root directory are redirected to the appropriate language subdirectory.
  • indexOverride – requests for index files (e.g. index.html) are redirected to the root directory, and then the user will be redirected again to the appropriate language subdirectory.
  • Post contact
  • Sitemap