Written by Wolfgang Disch Friday, 19 December 2008 10:34
What does it mean: A Joomla! application?
Well, Joomla! is a famous content management system but since its release 1.5 it is more than just a ready-to-use CMS application. Joomla! defines a framework for PHP developers who want to write their own application with the whole diversity of features like usage of templates, easy database access, administration interface, components and modules, and user and contact management right out of the box.
To show how this all works I coded a tiny application called Fishbone, which shows how to use the framework and how to structure such an application. All the example code here is drawn from Joomla!, mainly from the installation and the administration part of the CMS.
In our example we use a template to show a simple text output and provide different languages.
The name of the application is fishbone, so our first task is to create a folder with this name. This defines the starting point of the application and contains the main file index.php which loads the libraries and executes the application object.
If you installed Joomla! on your local computer, you can call fishbone with http://localhost//fishbone.
Lets have a look at the index.php file:
/**
* Fishbone - a Joomla! Application
* created by Wolfgang Disch
*/
define( '_JEXEC', 1 );
define( 'JPATH_BASE', dirname( __FILE__ ) );
define( 'DS', DIRECTORY_SEPARATOR );
require_once ( JPATH_BASE .DS.'includes'.DS.'defines.php' );
require_once ( JPATH_BASE .DS.'includes'.DS.'framework.php' );
// create the mainframe object
$mainframe =& JFactory::getApplication('fishbone');
// initialise the application
$mainframe->initialise();
// render the application
$mainframe->render();
// return the response
echo JResponse::toString();
From this file we can see the structure of a Joomla! application: The application folder contains a folder named includes, which contains the application specific files: defines.php, framework.php, application.php and others. The file defines.php specifies the globally used filepaths:
// no direct access
defined( '_JEXEC' ) or die( 'Restricted access' );
//Joomla framework path definitions
$parts = explode( DS, JPATH_BASE );
array_pop( $parts );
define( 'JPATH_ROOT', implode( DS, $parts ) );
define( 'JPATH_SITE', JPATH_ROOT );
define( 'JPATH_CONFIGURATION', JPATH_ROOT );
define( 'JPATH_ADMINISTRATOR', JPATH_ROOT.DS.'administrator' );
define( 'JPATH_XMLRPC', JPATH_ROOT.DS.'xmlrpc' );
define( 'JPATH_LIBRARIES', JPATH_ROOT.DS.'libraries' );
define( 'JPATH_PLUGINS', JPATH_ROOT.DS.'plugins' );
define( 'JPATH_INSTALLATION', JPATH_ROOT.DS.'installation' );
define( 'JPATH_THEMES', JPATH_BASE.DS.'templates' );
define( 'JPATH_CACHE', JPATH_ROOT.DS.'cache' );
// the new application
define( 'JPATH_FISHBONE', JPATH_ROOT.DS.'fishbone' );
In framework.php the files of the framework are loaded:
// no direct access
defined( '_JEXEC' ) or die( 'Restricted access' );
/*
* Joomla! system checks
*/
error_reporting( E_ALL );
@set_magic_quotes_runtime( 0 );
@ini_set('zend.ze1_compatibility_mode', '0');
/*
* Joomla! system startup
*/
// System includes
require_once( JPATH_LIBRARIES.DS.'joomla'.DS.'import.php');
// Installation file includes
define( 'JPATH_INCLUDES', dirname(__FILE__) );
/*
* Joomla! framework loading
*/
// Include object abstract class
require_once(JPATH_SITE.DS.'libraries'.DS.'joomla'.DS.'utilities'
.DS.'compat'.DS.'compat.php');
// Joomla! library imports
jimport( 'joomla.database.table' );
jimport( 'joomla.user.user');
jimport( 'joomla.environment.uri' );
jimport( 'joomla.user.user');
jimport( 'joomla.html.parameter' );
jimport( 'joomla.utilities.utility' );
jimport( 'joomla.language.language');
jimport( 'joomla.utilities.string' );
Back in index.php we see how the application is created:
// create the mainframe object
$mainframe =& JFactory::getApplication('fishbone');
The factory class searches in /fishbone/includes directory where we have to provide a file named as application.php. This file contains the definition of the JFishbone object, which is a class derived from JApplication. Here is the constructor of the class:
// no direct access
defined( '_JEXEC' ) or die('Restricted access' );
/**
* Joomla! Application class
*
* @final
*/
class JFishbone extends JApplication
{
/**
* Class constructor
*
* @access protected
* @param array An optional associative array of
* configuration settings
* Recognized key values include 'clientId'
* (this list is not meant to be comprehensive).
*/
function __construct($config = array())
{
<strong>// OUR APPLICATION ID</strong>
$config['clientId'] = <strong>4;</strong>
parent::__construct($config);
//Set the root in the URI based on the application name
JURI::root(null, str_replace('/'.$this->getName(), '',
JURI::base(true)));
}
It defines a client ID for our application. But the creation of the new instance will fail! This is due to the fact, that deep in the parent object JApplication there is a call to a helper class
$info =& JApplicationHelper::getClientInfo($client, true);
The purpose of the call is to get information about registered applications. Unfortunately the mapping is done hardcoded in JApplicationHelper class in the Joomla! libraries. So we have to hack the core file /libraries/joomla/application/helper.php:
class JApplicationHelper
{
/**
* Gets information on a specific client id.
* This method will be useful in
* future versions when we start mapping applications in the database.
*
* @access public
* @param int $id A client identifier
* @param Boolean $byName If True, find the client by it's name
* @return mixed Object describing the client or false if not known
* @since 1.5
*/
function &getClientInfo($id = null, $byName = false)
{
static $clients;
// Only create the array if it does not exist
if (!is_array($clients))
{
$obj = new stdClass();
// Site Client
$obj->id = 0;
$obj->name = 'site';
$obj->path = JPATH_SITE;
$clients[0] = clone($obj);
// Administrator Client
$obj->id = 1;
$obj->name = 'administrator';
$obj->path = JPATH_ADMINISTRATOR;
$clients[1] = clone($obj);
// Installation Client
$obj->id = 2;
$obj->name = 'installation';
$obj->path = JPATH_INSTALLATION;
$clients[2] = clone($obj);
// XMLRPC Client
$obj->id = 3;
$obj->name = 'xmlrpc';
$obj->path = JPATH_XMLRPC;
$clients[3] = clone($obj);
// Fishbone Client /**/
$obj->id = 4; /**/
$obj->name = 'fishbone'; /**/
$obj->path = JPATH_FISHBONE; /**/
$clients[4] = clone($obj); /**/
}
...
We add the highlighted entries to register our application and so the creation of our application object will be successful !
Now the application chain is executed: After creating the Object through the factory class JFactory, the methods initialise() and render() are executed. There might be additional methods for routing and dispatching, but we will keep it simple here.
The initialise method sets the language of the application:
/**
* Initialise the application.
*
* @access public
*/
function initialise( $options = array())
{
// Give the user English
if (!empty($options['language'])) {
$options['language'] = 'en-GB';
}
// One last check to make sure we have something
if ( ! JLanguage::exists($options['language']) ) {
$options['language'] = 'en-GB';
}
parent::initialise($options);
}
In our example we can switch to German language if we call the method with a language parameter:
// initialise the application
$mainframe->initialise(array('language' => 'de-DE'));
Now comes the kernel of the application. The $mainframe->render method loads the template and renders it. The result is written to the body of the JResponse object. The name and path of the template is passed to the render method of the document, which is of type JDocumentHTML.
$params = array(
'template' => 'default',
'file' => 'index.php',
'directory' => JPATH_THEMES
);
$data = $document->render(false, $params);
JResponse::setBody($data);
Rendering a template will substitute statements by the content of the named buffer component, which is set by a setBuffer method:
$document->setBuffer( $contents, 'component');
Here we see the segmentation into template on one side and inner part on the other side which actually is a component. To keep the example simple we will set the contents directly from a dummy component, which we include into the render method.
Lets see how the render method looks like now:
/**
* Render the application
*
* @access public
*/
function render()
{
$document =& JFactory::getDocument();
$user =& JFactory::getUser();
$document->setTitle(JText::_('PAGE_TITLE'));
// Define component path
define('JPATH_COMPONENT', JPATH_BASE.DS.'content');
// Execute the component
ob_start();
require_once(JPATH_COMPONENT.DS.'hello.php');
$contents = ob_get_contents();
ob_end_clean();
$params = array(
'template' => 'default',
'file' => 'index.php',
'directory' => JPATH_THEMES
);
$document->setBuffer( $contents, 'component');
$data = $document->render(false, $params);
JResponse::setBody($data);
}
The component in hello.php is just a place holder to demonstrate how the application can make use of different components here to render in the template. Its content finally is assigned to $contents.
After rendering the contents of the page, the result is enclosed in the object JResponse. At the last line, JResponse is sent to the client.
// return the response
echo JResponse::toString();
Now we are finished. The result looks very simple but you should remember what we achieved:
We developed an application with the full support of the Joomla! Framework libraries
If you are interested you can download the contents of the fishbone folder as a archive file fishbone.zip here.
Here is a screenview of our application so far:
Wolfgang is a seasoned IT professional working long years as head of software development and member of the executive team of a mobile phone distribution company. In these years he perceived the change from desktop software to web based applications. His vision is to support people with useful internet based services on mobile handsets which are easily available wherever they are.
More about Wolfgang DischThank you, Ercan. You are right, no need to hack the core!
The best place seems to be in the JFishbone constructor right before calling the parent constructor.
Thank you for your assistance
Very good post to start using Joomla for application development and understanding the application chain.
We have a team that works on the blogs presented on this site. Below you will find all present members who are actively working on blogs on this site.
Please contact us if you are interested in helping us out with the creation of the blogs.
jfoobar has readers from all over the world and in many languages. If you create a translation of one of our posts and link to it than please let us know so we can add a link back to the translation at the original post.
Copyright © 2008 jfoobar - All Rights Reserved - Joomla! is a registered trademark of Open Source Matters, Inc - Disclaimer
# 1 - Posted by: Ercan Özkaya on 2008-12-20 22:37:17
It's really a nice post. I like using Joomla! as an application and it sure has lots of potential and not known so much.
One thing though, you don't need to hack core to include your application.
Note that if you don't pass any arguments to JApplicationHelper::getClientInfo it will return the client list by reference so that means we can add our class to the static variable that keeps app info.
Then it's a matter of adding a class to the array like this before calling JFactory::getApplication:
//Set the application information
jimport( 'joomla.application.helper' );
$info =& JApplicationHelper::getClientInfo();
$obj = new stdClass();
$obj->id = 4;
$obj->name = 'appname';
$obj->path = JPATH_APPPATH;
$info[4] = $obj;
unset($obj);