Configuring the Fat-Free Framework requires:
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.
htaccess
tells an Apache server to:
tmp/
or ends in .ini
– if it does, reject the request with a 404 Page not found
response
%{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
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();
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.
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 -----
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:
To add an additional language:
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
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.
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
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.
// 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
}
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.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');
}
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.pageTitle
– for the title tag and for social mediapageDescription
– for the description meta tag and for social medialoadID
– A custom function for pulling the slug value from the config.ini menus for the single page menu (only for the home page)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)// 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');
Set page specific items in the after function:
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 pagelanguage_links_array
– Create a new array to be passed to the template enginelanguageLinks
– pass the just created array to Fat-Free to be used for the language menulangCode
– set the value for the lang attribute in the html tag.mainMenu
– combine the langCode with “HomeMenu” to generate the language specific menu (this does require that each menu have the same naming structure).dropdownMenu
– combine the langCode with “DropdownMenu” to generate the language specific menu (this does require that each menu have the same naming structure).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:
The UtilitiesController
handles: