Advertisement

Creating a PHP5 Framework - Part 1

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

With websites becoming more and more dynamic and interactive, developers often look to frameworks to help build websites and web apps rapidly. While there are a number of great frameworks available, a custom framework gives you the ability to easily customize the framework as you need, and to build with it even quicker - as you would already know the ins and outs of it before working with it. In this series of tutorials, we are going to build our very own PHP5 Development Framework! Let's start with planning our framework and put the basic structure into place.

*Note - Running the demo should display "PCA Framework version 0.1 the HTML output" - which demonstrates the first stage of our framework.

Step 1: This Series

In this series, we will create our own PHP5 Framework from scratch. The Framework will include basic content management features, and we will use it to build a web-based directory for a small organization.

Hopefully, as we go along, you will improve, build up and optimize this framework to suite your needs better, however in these tutorials we are aiming to get a simple framework up and running and powering a website.

Over the next few weeks, during these tutorials we will cover:

  • Creating authentication handler, database abstraction layer and template manager
  • Bringing these objects together
  • Using the framework to manage content, and power our site
  • Creating a fantastic front end design.
  • Designing for the login process by storyboarding
  • How the Framework can be extended and expanded

Step 2: Design Patterns and their use within our Framework

When creating any large computer-based system, be it a desktop application, a distributed network system, or a web application there will always be architectural challenges associated with its implementation.

Design patterns address some of these common problems, and we will make use of a number of design patterns as we create our Framework, to ensure that we have a quality, flexible, robust and usable framework, fit for any purpose! In particular, in this tutorial we are going to look at the Singleton pattern and the Registry pattern.

Step 3: Files and Folders

The first thing to get started creating our Framework, is to create a suitable file structure, such that everything has its place. As well as the files powering the framework, we also need to make provisions for files relating to the website we are going to create with our framework. Most websites and web applications have:

  • Some commonly used functions / objects.
  • Business logic.
  • A design

This gives us a great starting point, files relating to each of those three categories should be grouped together in their own section. Take a look at the directory structure below, we will then discuss the reasons for the structure.

Note that the .settings folder and the .project file were created by the IDE I use, and don't need to be present in your application

Core functions and objects, such as database access, authentication, template handling, email sending objects, email parsing objects, should be stored within a folder called objects, within the PCARegistry folder. This allows us to separate the logic from the Registry (which we will look at shortly) from the objects stored within the Registry.

Next, we need somewhere to store our business logic which is the files we will create which make use of our framework. We should keep these files in a folder called controllers. Each major function that our website or web application does (e.g. business directory listing, providing and managing content, providing an image gallery, etc) would be a separate controller, stored within its own folder within the controllers folder. We are not going to use these in this tutorial, but it is important to have our directory structure in place, so we know how it will work.

The sites design and templates, should be stored within the skins folder. Since we may want different designs (either so users of the application/website we create with our framework can chose from a number of designs, or to change the design depending on season or special event), each skin will be contained within its own folder.

Step 4: The Registry

At the heart of our framework we will have the core functions, such as database access, user authentication, etc. By implementing the Registry design pattern, we can keep these objects stored centrally, within the Registry, making it easy for our Framework and any applications which utilize our framework to access.

The registry design pattern stores and retrieves references to objects, and works in a similar way to a telephone directory: storing and retrieving contacts. We will use it to store these core objects, system-wide settings, and later, any other data or information which needs to be shared across the system.

Because we are storing this information centrally, we only ever want one instance of this registry object to be available within our framework, if more than one were available, then we would have problems where we were expecting a certain piece of data, or a certain object to be in the registry, when it was in fact stored within another instance of the Registry object. To solve this problem, our Registry object will also implement the Singleton design pattern, which prevents more than a single instance of the object being available.

Below is the PHP code for the registry.class.php file, we will look into how it works shortly.

<?php
/**
 * The PCARegistry object
 * Implements the Registry and Singleton design patterns
 *
 * @version 0.1
 * @author Michael Peacock
 */
class PCARegistry {
	
	/**
	 * Our array of objects
	 * @access private
	 */
	private static $objects = array();
	
	/**
	 * Our array of settings
	 * @access private
	 */
	private static $settings = array();
	
	/**
	 * The frameworks human readable name
	 * @access private
	 */
	private static $frameworkName = 'PCA Framework version 0.1';
	
	/**
	 * The instance of the registry
	 * @access private
	 */
	private static $instance;
	
	/**
	 * Private constructor to prevent it being created directly
	 * @access private
	 */
	private function __construct()
	{
	
	}
		
	/**
	 * singleton method used to access the object
	 * @access public
	 * @return 
	 */
	public static function singleton()
	{
		if( !isset( self::$instance ) )
		{
			$obj = __CLASS__;
			self::$instance = new $obj;
		}
		
		return self::$instance;
	}
	
	/**
	 * prevent cloning of the object: issues an E_USER_ERROR if this is attempted
	 */
	public function __clone()
	{
		trigger_error( 'Cloning the registry is not permitted', E_USER_ERROR );
	}
	
	/**
	 * Stores an object in the registry
	 * @param String $object the name of the object
	 * @param String $key the key for the array
	 * @return void
	 */
	public function storeObject( $object, $key )
	{
		require_once('objects/' . $object . '.class.php');
		self::$objects[ $key ] = new $object( self::$instance );
	}
	
	/**
	 * Gets an object from the registry
	 * @param String $key the array key
	 * @return object
	 */
	public function getObject( $key )
	{
		if( is_object ( self::$objects[ $key ] ) )
		{
			return self::$objects[ $key ];
		}
	}
	
	/**
	 * Stores settings in the registry
	 * @param String $data
	 * @param String $key the key for the array
	 * @return void
	 */
	public function storeSetting( $data, $key )
	{
		self::$settings[ $key ] = $data;


	}
	
	/**
	 * Gets a setting from the registry
	 * @param String $key the key in the array
	 * @return void
	 */
	public function getSetting( $key )
	{
		return self::$settings[ $key ];
	}
	
	/**
	 * Gets the frameworks name
	 * @return String
	 */
	public function getFrameworkName()
	{
		return self::$frameworkName;
	}
	
	
}

?>

So, how does the Registry object work, and how does it keep our objects stored nicely?

  • Objects are stored within an array.
  • When a new object is stored within the Registry, the class file is included, the object is instantiated and then it is stored in the array.
  • Objects are retrived by passing the objects "key" to the getObject method.

How does it prevent another copy of the Registry object being created?

  • The constructor is private, preventing the object from being created directly.
  • Cloning the object triggers an error.
  • If we need to access the object from within our Framework, and it is not directly available to the file we are working in, we can call the static method singleton ( PCARegistry::singleton() ) to get the instance of the Registry.

Step 5: index.php

With the structure in place ready for the core functionality which we will add in a further tutorial, let's look at how we will access the Registry, and start work on our Frameworks single point of access, our index.php file.

Friendly URLs are commonly available in all forms of dynamic websites and web applications, and one of the simplest ways to do this (and to manage the control of information through our Framework) is to ensure all of our page requests go through the index.php file. In a later tutorial, we will create a .htaccess file to redirect requests from a nice, friendly format, into a format that our index.php file can understand.

The index.php file's code is below. It doesn't do a lot at the moment, but it allows us to get things in place.

<?php
/**
 * PCAFramework
 * Framework loader - acts as a single point of access to the Framework
 *
 * @version 0.1
 * @author Michael Peacock
 */
 
// first and foremost, start our sessions
session_start();

// setup some definitions
// The applications root path, so we can easily get this path from files located in other folders
define( "APP_PATH", dirname( __FILE__ ) ."/" );
// We will use this to ensure scripts are not called from outside of the framework
define( "PCAFW", true );

/** 
 * Magic autoload function
 * used to include the appropriate -controller- files when they are needed
 * @param String the name of the class
 */
function __autoload( $class_name )
{
	require_once('controllers/' . $class_name . '/' . $class_name . '.php' );
}

// require our registry
require_once('PCARegistry/pcaregistry.class.php');
$registry = PCARegistry::singleton();

// print out the frameworks name - just to check everything is working
print $registry->getFrameworkName();

exit();

?>

So…what does our index.php file do at the moment? It:

  • Calls start_session right away, to ensure we can use sessions throughout the Framework (this must be called before any output.
  • It creates a definition of the current file path, so we can reference the frameworks root directory from elsewhere, and it creates a definition that we will use to ensure all of the Frameworks files are being called from the Framework itself, and that someone isn't trying to call one of the files directly
  • It uses the autoload function to determine where any classes may be located. In this case, it points to the controllers directory, as this is where our business logic will be stored.
  • It includes the registry class (this is needed as the class is not within the controllers folder where the autoload function would find it, and references the instance of the Registry to the variable $registry.
  • Finally, it prints out the name of the framework, to demonstrate a simple function from within the Registry.

We can see in more detail how the Registry will work within our Framework - by creating some dummy class files for it to use. With a template class in a new template.class.php file in the PCARegsitry/objects folder, we can demonstrate this, by adding some additional code to our index.php file.

On the line after we first reference $registry, we could add:

$registry->storeObject('template','template');

If our template class had a method contained within it, such as generateOutput, we could call this method from index.php like so:

$registry->getObject('template')->generateOutput();

Our Framework now has a basic structure to it, and we have a Registry in place for our core functionality, in the next tutorial, we will look at creating the objects which our Registry will store, starting with a basic database abstraction layer and a security manager.

Advertisement