Fuel Starter

Web Frameworks

Web Frameworks are software tools which support the construction of web sites by offering a variety of higher-level features such as Web frameworks are often grouped together with Content Management Systems (CMS) or Blog packages like Drupal, Joomla, or Wordpress. However the focus of a CMS or Blog is less on programming and more on configuration of pre-existing structures, something which makes them more suitable to the non-programmer. Both Web Frameworks and CMSs can be used to automate the building of large complex web sites, but a CMS is ultimately less flexible than a Web Framework.

The "bar-setter" for web frameworks has traditionally been Ruby-on-Rails. Ruby-on-Rails attracted so much attention because of the plethora of development tools it provided as well as the ease of the creation of CRUD features for a web application through a mechanism called scaffolding. There are several excellent textbooks and many IDEs (including NetBeans) provide good support for it.

Web frameworks using Php as the underlying language are numerous, for example: Akelos, CakePHP, CodeIgniter, FuelPhp, FUSE, Horde, Kohana, Laravel, LISA, Midgard, Phalcon, Qcodo, Symfony, Zend. In all of these frameworks, no matter how much you know of Php, you have to learn a whole new set of operations, as well as other "sub-languages" such as template languages.

FuelPhp

Our choice du jour is FuelPhp which has the home page:
http://fuelphp.com/
At the time of writing, the version in use is 1.8.0. FuelPhp is very simple (as far as web frameworks go) to get started. We will make use of the Smarty Template integration in FuelPhp.

With a few standard modifications, much of the Smarty template code that you have created in Php can be used directly within FuelPhp. As usual with these bleeding-edge technologies, finding documentation, explanations and simple examples can be tricky. The FuelPhp docs are here:
http://fuelphp.com/docs
They are organized quite well, but not presented in a pedagogical, tutorial-like fashion, and so it is easy to become overwhelmed with the information.

Apache Rewrite Configuration

If you are using Apache/Php via XAMPP, you can ignore this section, the necessary Apache configuration is already in place.

If, however, you are using the native Apache/Php, it usually requires some configuration to permit the framework's usage. In particular, Apache's rewrite module is needed to force the framework-related URLs through the front controller script.

Windows

Edit the Apache configuration file, httpd.conf. Look for the "rewrite_module" line. It is probably commented out:
#LoadModule rewrite_module modules/mod_rewrite.so
If so, uncomment it by removing the initial "#" character. Then restart Apache: open an administrative command shell, navigate to C:\Apache24\bin and do:
httpd -k restart

Mac

Examine the Apache configuration file, /etc/apache2/httpd.conf, and make sure that this line is present and uncommented:
LoadModule rewrite_module libexec/apache2/mod_rewrite.so
If this line is commented out, edit the file and uncomment the line by removing the initial "#" character and then restart Apache:
$ sudo apachectl restart

Ubuntu Linux

On Ubuntu, activate the module as follows:
$ sudo a2enmod rewrite
Then reload Apache:
$ sudo service apache2 reload

The NetBeans FuelPhp Plugin

In addition to the Php and Smarty plugins, it is crucial to also install the FuelPhp plugin. From NetBeans,
  1. Select Tools ⇾ Plugins from the menus
  2. Click the Available Plugins tab, scroll down and check these names in the Php category:
    PHP FuelPHP Framework
    Then click the Install button.

Project Installation

We will assume that: Download the source archive FuelStarter.zip. and extract the archive into your NetBeansProjects folder. For Windows users, I highly recommend downloading the zip archive first then extracting it using 7-zip.
  1. Create FuelStarter as a NetBeans Php Project from Existing Sources. On the run configuration step, make the Project URL be:
    http://localhost/default/FuelStarter/public
    
  2. Like Smarty-related projects, specific subfolders need to be writable by Apache for logging, caching and holding other temporary information. On MAC/Linux, open a terminal shell and navigate to the FuelStarter directory and run:
    $ php oil r install
    
    You should see feedback about 4 directories made writable.
  3. Double-check and, if necessary, reset the RewriteBase in public/.htaccess which is currently set to this:

    public/.htaccess
    RewriteBase  /default/FuelStarter/public
  4. Enable the FuelPhp features for the project. Right-click on the FuelStarter project in the Projects window and select Properties. Locate FuelPhp in Frameworks check the enabled checkbox. Afterwards, you should see this altered listing in the Projects window:
    FuelStarter 
      Source Files
      Important Files
      Include Path
      controller
      model
      views
      tasks
      assets
      modules
    
  5. Enable FuelPHP code completion in NetBeans. This is important, don't skip it. Right-click on the FuelStarter project and select from the menu:
    FuelPHP ⇾ generate auto-completion file
    The effect can be seen through the Files menu, by opening the nbproject directory in FuelStarter. The added file is fuel_autocompletion.php.

Check that it works

You should be able to run the project by the NetBeans run button, or by activating the URL:
http://localhost/default/FuelStarter/public
The true test of whether you've gotten the setup correct is to add "/home/index" onto the URL:
http://localhost/default/FuelStarter/public/home/index
With XAMPP Apache/Php, this is surely likely to succeed. With a Native Apache/Php, if something fails, it probably means something is not correct with your rewrite setup: Apache maintains an error.log file (in directories varying by the Apache/Php and operating system installation). If something fails, often information appears in the last entries in this log file.

Installation Details

Application file structure

If you examine this directory you will see, among other parts, the file layout:
fuel/
  app/        Application-specific files
    classes/  Fuel classes
    views/    Fuel view scripts, e.g., Smarty templates
  core/       FuelPhp-required code
  packages/   Optional pacakges
oil
public/       The web-accessible application root
  index.php   The front controller
  .htaccess   Apache rewrite rules to make application URLs activate the front controller.
  assets/
    css/      Stylesheets
    js/       JavaScript files
    img/      Images
The so-called front controller, public/index.php, is the brain of a web framework. Its job is to parse the requested URL and to construct the "program" to run from one or more parts of files within the fuel file structure.

The NetBeans project, with FuelPHP enabled, presents top-level "psuedo-directories" which are commonly accessed when programming:
FuelStarter 
  Source Files
  Important Files  = (files from fuel/app/config)
  Include Path
  controller       = (the directory fuel/app/classes/controller)
  model            = (the directory fuel/app/classes/model)
  views            = (the directory fuel/app/views)
  tasks            = (the directory fuel/app/tasks)
  assets           = (the directory public/assets)
  modules

How Apache rewrites work

Apache directs URLs through the front controller with this code (enumeration added):

public/.htaccess
(1)   RewriteEngine on
(2)   RewriteBase /default/FuelStarter/public
(3)   RewriteCond %{REQUEST_FILENAME} !-f
(4)   RewriteCond %{REQUEST_FILENAME} !-d
(5)   RewriteRule ^(.*)$ index.php/$1 [L]
Reading by the numbers:
  1. Turn on rewrites.
  2. Set the base url for rewrites.
  3. If the request is not to a specific file within public
  4. and if the request is not to a specific directory within public
  5. then pass the URL (removing the RewriteBase) to the front controller
For the key RewriteRule step, the complete URL (removing the RewriteBase) matches ^(.*)$. The $1 in the subsequent rule-application refers to the first parenthesized group, i.e. the whole thing. The final [L] tells Apache that this is the Last (and only in this case) rule to use, i.e., do not apply any further rewrite rules found in the .htaccess file.

The controller/action URL

The FuelPhp Framework, like many others, interprets URLs with two components added to the base like this:
http://APPLICATION-BASE/ctrl/act
The ctrl part corresponds programmatically to a Php class defined within a file which, for FuelPhp, is this:
fuel/app/classes/controller/ctrl.php
Within the ctrl.php file the class name is:
class Controller_Ctrl { ... }
The act part of the URL corresponds to a member function within that controller class which, for FuelPHP is the action_act function:
class Controller_Ctrl { 
  public function action_act() {
  }
}
The code within the body of action_act in simplest terms, represents the "top Php part" of the code used in our Php/Smarty applications. The corresponding view script is activated explicitly by a call. This script name is often related to the ctrl/act URL:
class Controller_Ctrl extends Controller {
  public function action_act() {
    return Response::forge(View::forge('ctrl/act.tpl'));
  }
}
In this case the Smarty view script activated would be in this file:
fuel/app/views/ctrl/act.tpl

Convention over Configuration

The process whereby a URL is rendered into a Php script follows many naming conventions for files, classes and member functions instead of relying on configuration information about which ones to use. Preferring convention over configuration was, perhaps, championed in the Ruby-on-Rails framework in response to earlier frameworks in which configuration information was mostly required, often in XML file format.

Controllers, Models and Scaffolding

Separate controllers are thought to make separations between distinct portions of the web applications and the actions create the behaviors within these separate portions. A common theme when databases are used is to have each model (often a database table) correspond to a controller, and the member functions correspond to the CRUD operations we want to perform on that model.

Web Frameworks usually support a technique called scaffolding, where some built-in script, written in the server-side language, reads the model (table information) and automatically creates a complete controller, its action methods, and the corresponding view scripts for all standard CRUD operations applicable to this model.

Explore the root URL

Starting from the application's web root
http://localhost/default/FuelStarter/public
There is some configuration information used to assign this to a controller/action pair. The information is found in the file:

fuel/app/config/routes.php
return array(
    '_root_'  => 'home/index',  // The default route
    ...
);
meaning that the root URL translates to:
http://localhost/default/FuelStarter/public/home/index
Due to yet another convention, the assumed action is index, and so this URL also presents the same thing:
http://localhost/default/FuelStarter/public/home

Smarty Template Usage

FuelPhp supports a number of template systems, one of which is the Smarty templates added as a plugin. Once Smarty is installed there are several necessary configuration steps which are described in the Creating FuelStarter from scratch section below. This application has the same basic presentation as the one described in the Php Web Starter document.

One complication of using Smarty within FuelPhp is that you have to make use of FuelPhp functions written as Smarty extension functions, which means that you have to know something about which FuelPhp functions to use and how to use them within Smarty templates. Here is the key file used by FuelPhp which associates FuelPhp functions to Smarty Functions:
FuelPhp Smarty Extensions
Fortunately, we do not need many of these and the usage, once you see it, is very standard.

The home controller, index script and layout

These scripts hold the content of the application:
fuel/app/classes/controller/home.php  
fuel/app/views/home/index.tpl  
fuel/app/views/layout.tpl  
fuel/app/views/links.tpl  
The significant difference between the code in the Smarty template files from that in our Php/Smarty version is the usage of 4 FuelPhp/Smarty functions:

The Smarty Extensions

Starting with the base_url function, the relevant line in the Smarty_Fuel_Extension constructor is:
$smarty->registerPlugin('function', 'base_url', array('Uri', 'base'));
The translation is that the base_url Smarty function uses the FuelPhp function:
echo Uri::base()
See the relevant FuelPhp doc page. In our case it generates the string:
http://localhost/default/FuelStarter/public/
from which the surrounding functions extract the "FuelStarter" portion as the default page title.

Next the asset_css function. The relevant code in the Smarty_Fuel_Extension class is:
$smarty->registerPlugin('function', 'asset_css', array($this, 'asset_css'));
...
public function asset_css($params) {
  if (!isset($params['refs'])) {
    throw new \UnexpectedValueException("The refs parameter is required.");
  }
  $group = isset($params['group']) ? $params['group'] : null;
  $attrs = isset($params['attrs']) ? $params['attrs'] : array();
  $raw = isset($params['raw']) ? $params['raw'] : false;
  return Asset::css($params['refs'], $attrs, $group, $raw);
}
Thus, for example, our Smarty call:
{asset_css refs='layout.css'}
translates to the FuelPhp call:
echo Asset::css('layout.css');
This and other Asset static functions are used to obtain public content from the public/asset folder for use in the web page. Look at the relevant FuelPhp doc page.

The outcome is the code:
<link type="text/css" rel="stylesheet" 
      href="BASE/assets/css/layout.css?1427155449" />
where BASE is outcome of echo Uri::base(). Used in this way, the Smarty call generates a link to a CSS stylesheet.

The numeric query string after the file is the modification timestamp of the file. Because browser caching mechanisms take the entire URI into account, this technique ensures that the a cached version of the file will not be used after it is modified.

Next, the asset_img function. The relevant code in the Smarty_Fuel_Extension class is:
$smarty->registerPlugin('function', 'asset_img', array($this, 'asset_img'));
...
public function asset_img($params) {
  if (!isset($params['refs'])) {
     throw new \UnexpectedValueException("The refs parameter is required.");
  }
  $group = isset($params['group']) ? $params['group'] : null;
  $attrs = isset($params['attrs']) ? $params['attrs'] : array();
  return Asset::img($params['refs'], $attrs, $group);
}
Thus, for example, our Smarty call:
{asset_img refs='header.png'}
translates to the FuelPhp call:
echo Asset::img('header.png', array(), null);
The outcome is the code:
<img src="BASE/assets/img/header.png?1427155399" alt="" />
where BASE is outcome of echo Uri::base().

Finally, the html_anchor function. The relevant code in the Smarty_Fuel_Extension class is:
$smarty->registerPlugin('function', 'html_anchor', array($this, 'html_anchor'));
...
public function html_anchor($params) {
  if (!isset($params['href'])) {
    throw new \UnexpectedValueException("The href parameter is required.");
  }
  if (!isset($params['text'])) {
    throw new \UnexpectedValueException("The text parameter is required.");
  }
  $attrs = isset($params['attrs']) ? $params['attrs'] : array();
  $secure = isset($params['secure']) ? $params['secure'] : null;
  return Html::anchor($params['href'], $params['text'], $attrs, $secure);
}
Thus, for example, our Smarty call:
{html_anchor href='/home/sample' text='Sample'}
translates to the FuelPhp call:
echo Html::anchor('/home/sample', 'Sample', array(), null);
See the relevant FuelPhp doc page. The outcome is the code:
<a href="BASE/home/sample">Sample</a>
where BASE is outcome of echo Uri::base().

Errors on missing assets

The Smarty asset_css and asset_img, calling the FuelPhp Asset::css and Asset::img functions both generate errors if the stylesheet/image file is missing. This is especially useful for stylesheets because there is normally no feedback when it is missing.

Generating absolute references

Note how the FuelPhp application uses absolute paths for stylesheets, images, and hyperlinks in contrast to our previous Php applications in which the paths were all relative. It is important to note that: Therefore, for portability, it is necessary to use the Smarty/FuelPHP functions which programmatically generate the base and prepend it onto the URL information we provide.

View generation

The view generation starts from the controller class:

fuel/app/classes/controller/home.php
class Controller_Home extends Controller {
 
  public function action_index() {
    $data = [];
    return Response::forge(View::forge('home/index.tpl', $data));
  }
}
The static forge function is effectively a constructor, so we could just as well write, in place of the current return statement:
    return new Response(new View('home/index.tpl', $data));
The Response is a wrapper around the content returned. In some cases we need change it's default features, but mostly not. Thus, it works just as well in most cases to omit the Response usage:
    return View::forge('home/index.tpl', $data);

Sending data to view script

The $data array map is used just like in our previous Php/Smarty applications. Its purpose is to send variables from the controller to the view script. For example,

fuel/app/classes/controller/home.php
class Controller_Home extends Controller {
  ...
  public function action_sample() {
    $data = [
        'foo' => "FOO",
        'bar' => "<b>BAR</b>",
    ];
    return View::forge('home/sample.tpl', $data);
  }
}

fuel/app/views/home/sample.tpl
{extends file="layout.tpl"}
 
{block name="content"}
<h2>Sample</h2>
 
foo: {$foo}
<br />
bar: {$bar}
{/block}
The output illustrates that FuelPhp automatically sanitizes data variables sent to the template script; there is no need to use the Smarty "escape:'html'" filter:
{$bar | escape: 'html'}
There are circumstances in which you want the variables not to be sanitized. In that case, you have to explicitly tell FuelPhp not to do so. The sample1 action illustrates one possibility:

fuel/app/classes/controller/home.php
class Controller_Home extends Controller {
  ...
  public function action_sample1() {
    $data = [
        'foo' => "FOO",
    ];
    $view = View::forge('home/sample1.tpl', $data);
    $view->set('bar', "<b>BAR</b>");
    $view->set_safe('baz', "<b>BAZ</b>");  // set('baz', "<b>BAZ</b>", false);
    return $view;
  }
}

fuel/app/views/home/sample1.tpl
{extends file="layout.tpl"}
 
{block name="content"}
<h2>Sample1</h2>
 
foo: {$foo}
<br />
bar: {$bar}
<br />
baz: {$baz}
{/block}
The controller code illustrates an alternative way to assign variables to a view. The $view is generated like this:
$view = View::forge('home/sample1.tpl', $data);
We can pass additional data from variables assigned through the set member function. To avoid the content being sanitized, say, for $baz, we can use a variant
$view->set_safe('baz', "<b>BAZ</b>");
Alternatively we can use an additional boolean argument in the regular set function:
$view->set('baz', "<b>BAZ</b>", false);
You can apply the same approach and avoid sanitizing all of $data
$view = View::forge('home/sample1.tpl', $data, false);

Creating FuelStarter from scratch

This section is written for posterity so as to record all the steps necessary to take the raw FuelPhp project into the version used by our applications. We have made no attempt to have this description work on a Windows system. It is possible that other features are required as well, such as the php-json package if you're doing this on Ubuntu.

Initial commands

The easiest way to create an Fuel App is to first install the oil executable. On Linux:
$ curl https://get.fuelphp.com/oil | sh
This will attempt to install oil as /usr/local/bin/oil. You will also need an installation of composer.
$ cd /PATH/TO/NetBeansProjects
$ oil create FuelStarter
Now we need to download the Smarty plugin code. The composer.phar Php script is part of the installation package and this can be used to make further updates and additions.

Edit composer.json (e.g., use nano on a MAC). Locate the "require" group and add the line:
...
"require": {
    "smarty/smarty": "*",
    ...    
Install it by running:
$ ./composer.phar self-update
$ ./composer.phar update
Complete the remaining command-line portion:
$ php oil r install

Activate NetBeans

The remaining steps have to do with editing/creating files which can be done through NetBeans. So install FuelStarter as a Php Project with Existing Sources (see details above).

First edit public/.htaccess, setting the RewriteBase:
RewriteBase /default/FuelStarter/public
Running it at this point gives a Welcome message.

Set the Timezone

Edit the folllowing configuration file. Locate the indicated section and set a specific value for the "default_timezone" setting. Also, set the "locale" entry to the empty string:

fuel/app/config/config.php
    /**
     * DateTime settings
     *
     * server_gmt_offset   in seconds the server offset from gmt 
     *                     timestamp when time() is used
     *
     * default_timezone	   optional, if you want to change the 
     *                     server's default timezone
     */
    // 'server_gmt_offset'  => 0,
    //'default_timezone'   => null,
    'default_timezone'   => "America/New_York",
The documentation indicates that it's optional, but some systems do not have a "server default."

Set to auto-load packages

You have to make the parser and orm packages be loaded. Edit the following file. Look for the lines near the bottom of the file:

fuel/app/config/config.php
...
return array(
    ...
    // 'always_load'  => array(
        ...
        // 'packages'  => array(
        //  //'orm',
        // ),
        ...
    // ),
 
);
Change the code by uncommenting these lines and adding one:
...
return array(
   ...
   'always_load'  => array(
        ...
        'packages'  => array(
           'parser',
           'orm',    // used later for database
        ),
        ...
    ),
 
);

Create the Smarty Extension class


fuel/app/classes/myextensions.php
<?php
class MyExtensions {
  public function __construct(Smarty $smarty) {
    $session = Session::forge();
    $smarty->assign('session', $session);
  }
}
select

Configure parser

Configure the parser to use ".tpl" as the Smarty extension instead of the default, ".smarty". Also register your user-defined extensions class. Create the file:

fuel/app/config/parser.php
<?php
 
return [
  'extensions' => [
    'smarty' => ['class' => 'View_Smarty', 'extension' => 'tpl'],
  ],
  'View_Smarty' => [
    'extensions' => [
      'MyExtensions',
    ],
  ],
];
select

ORM query bug fix

On line 798 of the following file you should see this plus 3 other lines:

fuel/packages/orm/classes/query.php
<?php
...
if ( ! is_null($this->limit) )
{
   $query->limit($this->limit);
}
...
Replace that first line by:
<?php
...
if ( ! is_null($this->limit) && $type != 'update' && $type != 'delete' )
...

Add the content files

Download and extract the archive FuelStarterFiles.zip. Move the files and directories to the indicated locations:
home.php  ⇾ fuel/app/classes/controller/
layout.tpl, links.tpl, home/  ⇾ fuel/app/views/
bootstrap.min.css, layout.css  ⇾ public/assets/css/
bootstrap.min.js, jquery.min.js  ⇾ public/assets/js/
header.png  ⇾ public/assets/img/

Reset the default route

Reset the initial default route by editing:
fuel/app/config/routes.php
Change
   '_root_'  => 'welcome/index',  // The default route
to
   '_root_'  => 'home/index',  // The default route

Extend session timeout

The FuelPhp session has a built-in expiration timeout after a certain amount of inactivity. In development mode it is useful to have this timeout to be very long to avoid, say, having to frequently log back in to run tests on code changes. Toward this end, create the file:

fuel/app/config/development/session.php
<?php 
return array (
  'expiration_time' => 86400,
);
select
The session will expire after 1 day (86400 seconds) when the app runs in (default) development mode.

A Real Installation

This section is written for posterity; it's not necessary for development puposes. The main issue is that only the public folder within the top level directory is the part meant to be web-accessible. We could attempt to enforce blocked access to the other files and folders, but a better way is to make the changes so that
http://localhost/FuelStarter
becomes the application root URL, mapping to the public folder, making everything else invisible to a browser.

You have to prevent the effect of the public/.htaccess file. The best way is to simply move this file into the top level folder where it has no effect. In a MAC/UNIX shell in the FuelStarter directory:
$ cd /PATH/TO/FuelStarter  
$ mv public/.htaccess .
The application need not reside in the NetBeansProjects directory. If you want move it to some folder which is not already web-accessible. Edit a suitable Apache system initialization file such as httpd.conf and add this code, setting /FULL/PATH/TO appropriately:
Alias /FuelStarter /FULL/PATH/TO/FuelStarter/public
<Directory "/PATH_TO/FuelStarter/public">
# SetEnv FUEL_ENV production
 
  Require all granted
 
  RewriteEngine on
  RewriteBase /FuelStarter
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule ^(.*)$ index.php/$1 [L]
</Directory>
The lines prior to the Rewrite settings may not be necessary; uncomment as needed. Afterwards, restart Apache. After doing so, the test is that both of these should give the home page: Going a step further, the commented SetEnv line in the Apache config file makes FuelPhp run in production mode. One of the qualities of this mode is that the details of server errors are not revealed, making the site more secure.

If you want to use a FuelPhp installation as your web root, i.e.,
http://localhost
it is a different story. You would have to make changes at a more fundamental level of the Apache configuration files; it is quite different for different operating system Apache installations.


© Robert M. Kline