Create a PHP Framework

Create a PHP5 Framework – Part 2

Sep 15th in PHP by Michael Peacock
With the basic structure for our Framework in place, it is time to start adding functionality to it. In this tutorial we will create a template manager and database handler, bringing us a step closer to a powerful Framework fit for use for almost any project. If you haven't already, be sure to review Part 1 of this series first!
PG

Author: Michael Peacock

Michael Peacock is a web developer from Newcastle, UK and has a degree in Software Engineering from the University of Durham. After meeting his business partner at Durham, he co-founded Peacock Carter, a Newcastle based creative consultancy specializing in web design, web development and corporate identity. Michael loves working on web related projects. When he is not working on client projects, he is often tinkering with a web app of his own. He has been involved with a number of books, having written two books himself (and working on his third): Selling online with Drupal e-Commerce, and Building websites with TYPO3. He has also acted as technical reviewer for two other books: Mobile Web Development, and Drupal Education & E-Learning. You can follow Michael on Twitter.

MVC: Tweak the structure

In the first part of this tutorial, we created a folder called controllers to store the business logic for our applications. As daok pointed out in a comment, this isn't the best place for all of the business logic, and that a model should be used to store this logic. Previously, I have always used the database itself as the model in the majority of my applications, however, seperating this out a little more will make our framework even more powerful, and easier to extend.

So, what is MVC? MVC is a design pattern (as was the Singleton and Registry patterns we looked at in part 1), and it stands for Model View Controller, and the aim of this pattern is to seperate the business logic, user interface actions and the user interface from one another. Although we are not going to do anything with our models and controllers just yet, let's update our frameworks folder structure to include the "models" folder. The model will contain the main business logic, and the controller will deal with user interaction (e.g. submitting data, such as a comment). NB: Our __autoload function does not need to be changed.

Database Handler

Most websites and web applications which make use of PHP also make use of a database engine, such as MySQL. If we keep all of our database related functions in the same place, then we can (in theory) easily change the database engine we use. We can also make certain operations easier, such as inserting records, updating records or deleting records from the database. It can also make it easier when dealing with multiple database connections.

So...what should our database handler do:

  • Manage connections to the database
  • Try to provide some level of abstraction from the database
  • Cache queries so we can use them later
  • Make common database operations easier

Let's look at the code for our database handler, then we will discuss it afterwards.

<?php

/**
 * Database management and access class
 * This is a very basic level of abstraction
 */
class database {
	
	/**
	 * Allows multiple database connections
	 * probably not used very often by many applications, but still useful
	 */
	private $connections = array();
	
	/**
	 * Tells the DB object which connection to use
	 * setActiveConnection($id) allows us to change this
	 */
	private $activeConnection = 0;
	
	/**
	 * Queries which have been executed and then "saved for later"
	 */
	private $queryCache = array();
	
	/**
	 * Data which has been prepared and then "saved for later"
	 */
	private $dataCache = array();
	
	/**
	 * Record of the last query
	 */
	private $last;
	
	
	/**
	 * Hello
	 */
    public function __construct()
    {
    	
    }
    
    /**
     * Create a new database connection
     * @param String database hostname
     * @param String database username
     * @param String database password
     * @param String database we are using
     * @return int the id of the new connection
     */
    public function newConnection( $host, $user, $password, $database )
    {
    	$this->connections[] = new mysqli( $host, $user, $password, $database );
    	$connection_id = count( $this->connections )-1;
    	if( mysqli_connect_errno() )
    	{
    		trigger_error('Error connecting to host. '.$this->connections[$connection_id]->error, E_USER_ERROR);
		} 	
    	
    	return $connection_id;
    }
    
    /**
     * Close the active connection
     * @return void
     */
    public function closeConnection()
    {
    	$this->connections[$this->activeConnection]->close();
    }
    
    /**
     * Change which database connection is actively used for the next operation
     * @param int the new connection id
     * @return void
     */
    public function setActiveConnection( int $new )
    {
    	$this->activeConnection = $new;
    }
    
    /**
     * Store a query in the query cache for processing later
     * @param String the query string
     * @return the pointed to the query in the cache
     */
    public function cacheQuery( $queryStr )
    {
    	if( !$result = $this->connections[$this->activeConnection]->query( $queryStr ) )
    	{
		    trigger_error('Error executing and caching query: '.$this->connections[$this->activeConnection]->error, E_USER_ERROR);
		    return -1;
		}
		else
		{
			$this->queryCache[] = $result;
			return count($this->queryCache)-1;
		}
    }
    
    /**
     * Get the number of rows from the cache
     * @param int the query cache pointer
     * @return int the number of rows
     */
    public function numRowsFromCache( $cache_id )
    {
    	return $this->queryCache[$cache_id]->num_rows;	
    }
    
    /**
     * Get the rows from a cached query
     * @param int the query cache pointer
     * @return array the row
     */
    public function resultsFromCache( $cache_id )
    {
    	return $this->queryCache[$cache_id]->fetch_array(MYSQLI_ASSOC);
    }
    
    /**
     * Store some data in a cache for later
     * @param array the data
     * @return int the pointed to the array in the data cache
     */
    public function cacheData( $data )
    {
    	$this->dataCache[] = $data;
    	return count( $this->dataCache )-1;
    }
    
    /**
     * Get data from the data cache
     * @param int data cache pointed
     * @return array the data
     */
    public function dataFromCache( $cache_id )
    {
    	return $this->dataCache[$cache_id];
    }
    
    /**
     * Delete records from the database
     * @param String the table to remove rows from
     * @param String the condition for which rows are to be removed
     * @param int the number of rows to be removed
     * @return void
     */
    public function deleteRecords( $table, $condition, $limit )
    {
    	$limit = ( $limit == '' ) ? '' : ' LIMIT ' . $limit;
    	$delete = "DELETE FROM {$table} WHERE {$condition} {$limit}";
    	$this->executeQuery( $delete );
    }
    
    /**
     * Update records in the database
     * @param String the table
     * @param array of changes field => value
     * @param String the condition
     * @return bool
     */
    public function updateRecords( $table, $changes, $condition )
    {
    	$update = "UPDATE " . $table . " SET ";
    	foreach( $changes as $field => $value )
    	{
    		$update .= "`" . $field . "`='{$value}',";
    	}
    	   	
    	// remove our trailing ,
    	$update = substr($update, 0, -1);
    	if( $condition != '' )
    	{
    		$update .= "WHERE " . $condition;
    	}
    	
    	$this->executeQuery( $update );
    	
    	return true;
    	
    }
    
    /**
     * Insert records into the database
     * @param String the database table
     * @param array data to insert field => value
     * @return bool
     */
    public function insertRecords( $table, $data )
    {
    	// setup some variables for fields and values
    	$fields  = "";
		$values = "";
		
		// populate them
		foreach ($data as $f => $v)
		{
			
			$fields  .= "`$f`,";
			$values .= ( is_numeric( $v ) && ( intval( $v ) == $v ) ) ? $v."," : "'$v',";
		
		}
		
		// remove our trailing ,
    	$fields = substr($fields, 0, -1);
    	// remove our trailing ,
    	$values = substr($values, 0, -1);
    	
		$insert = "INSERT INTO $table ({$fields}) VALUES({$values})";
		$this->executeQuery( $insert );
		return true;
    }
    
    /**
     * Execute a query string
     * @param String the query
     * @return void
     */
    public function executeQuery( $queryStr )
    {
    	if( !$result = $this->connections[$this->activeConnection]->query( $queryStr ) )
    	{
		    trigger_error('Error executing query: '.$this->connections[$this->activeConnection]->error, E_USER_ERROR);
		}
		else
		{
			$this->last = $result;
		}
		
    }
    
    /**
     * Get the rows from the most recently executed query, excluding cached queries
     * @return array 
     */
    public function getRows()
    {
    	return $this->last->fetch_array(MYSQLI_ASSOC);
    }
    
    /**
     * Gets the number of affected rows from the previous query
     * @return int the number of affected rows
     */
    public function affectedRows()
    {
    	return $this->$this->connections[$this->activeConnection]->affected_rows;
    }
    
    /**
     * Sanitize data
     * @param String the data to be sanitized
     * @return String the sanitized data
     */
    public function sanitizeData( $data )
    {
    	return $this->connections[$this->activeConnection]->real_escape_string( $data );
    }
    
    /**
     * Deconstruct the object
     * close all of the database connections
     */
    public function __deconstruct()
    {
    	foreach( $this->connections as $connection )
    	{
    		$connection->close();
    	}
    }
}
?>

Before discussing this in more detail, I should point out that this database handler is very basic. We could provide complete abstraction by not executing queries directly, but instead constructing queries based on paramaters to a query function, and then executing it.

Our delete, insert and update record methods make it easier to perform some common tasks (as I mentioned above we could extend this to do much much more), by only providing information such as the table name, an array of fields and coresponding values, limit values and conditions. Queries can also be "cached" so that we can do things with them later. I find this feature (as well as the ability to "cache" arrays of data) is very handy when combined with a template manager, as we can easily iterate through rows of data and populate it into our templates with little fuss, as you will see when we look at the template manager.

// insert record
$registry->getObject('db')->insertRecords( 'testTable', array('name'=>'Michael' ) );
// update a record
$registry->getObject('db')->updateRecords( 'testTable', array('name'=>'MichaelP' ), 'ID=2' );
// delete a record (well, upto 5 in this case)
$registry->getObject('db')->deleteRecords( 'testTable', "name='MichaelP'", 5 );

We can also work with multiple database connections relatively easily, so long as we switch between the appropriate connections when we need to (although this won't work when caching queries and retrieving them via our template manager without further work), for example, the code snippet below would allow us to delete records from two databases.

// our second database connection (let's assume we already have a connection to our main DB)
$newConnection = $registry->getObject('db')->newConnection('localhost', 'root', 'password', 'secondDB');
// delete from the primary db connection
$registry->getObject('db')->deleteRecords( 'testTable', "name='MichaelP'", 5 );
// change our active db connection, to allow future queries to be on the second connection
$registry->getObject('db')->setActiveConnection( $newConnection );
// delete from the secondary db connection
$registry->getObject('db')->deleteRecords( 'testTable', "name='MichaelP'", 5 );
// revert the active connection so future queries are on the primary db connection
$registry->getObject('db')->setActiveConnection( 0 );

How might we want to extend this class?

  • Full abstraction
  • Make use of inheritance, create an interface and have database classes inherit from it, each for different database engines
  • Store the connection ID's along with the query when caching queries
  • Improve data sanitizing, depending on the type of data we wish to sanitize

Template Manager

The template manager will handle all of the output, it needs to be able to work with various different template files, replace placeholders (I call them tags) with data and iterate through parts of the template with multiple rows of data from the database.

To make things easier, we will make use of a page class to contain the content related to the page, this also makes it easier for us to extend this and add features to it later. The template manager will manage this object.

<?php

// prevent this file being called directly
if ( ! defined( 'PCAFW' ) ) 
{
	echo 'This file can only be called via the main index.php file, and not directly';
	exit();
}

/**
 * Template manager class
 */
class template {

	private $page;
	
	/**
	 * Hello!
	 */
    public function __construct() 
    {
	    include( APP_PATH . '/PCARegistry/objects/page.class.php');
	    $this->page = new Page();

    }
    
    /**
     * Add a template bit onto our page
     * @param String $tag the tag where we insert the template e.g. {hello}
     * @param String $bit the template bit (path to file, or just the filename)
     * @return void
     */
    public function addTemplateBit( $tag, $bit )
    {
		if( strpos( $bit, 'skins/' ) === false )
		{
		    $bit = 'skins/' . PCARegistry::getSetting('skin') . '/templates/' . $bit;
		}
		$this->page->addTemplateBit( $tag, $bit );
    }
    
    /**
     * Put the template bits into our page content
     * Updates the pages content
     * @return void
     */
    private function replaceBits()
    {
	    $bits = $this->page->getBits();
	    foreach( $bits as $tag => $template )
	    {
		    $templateContent = file_get_contents( $bit );
		    $newContent = str_replace( '{' . $tag . '}', $templateContent, $this->page->getContent() );
		    $this->page->setContent( $newContent );
	    }
    }
    
    /**
     * Replace tags in our page with content
     * @return void
     */
    private function replaceTags()
    {
	    // get the tags
	    $tags = $this->page->getTags();
	    // go through them all
	    foreach( $tags as $tag => $data )
	    {
		    if( is_array( $data ) )
		    {
			  
			    if( $data[0] == 'SQL' )
			    {
				    // it is a cached query...replace DB tags
				    $this->replaceDBTags( $tag, $data[1] );
			    }
			    elseif( $data[0] == 'DATA' )
			    {
				     // it is some cached data...replace data tags
				    $this->replaceDataTags( $tag, $data[1] );
			    }
	    	}
	    	else
	    	{	
		    	// replace the content	    	
		    	$newContent = str_replace( '{' . $tag . '}', $data, $this->page->getContent() );
		    	// update the pages content
		    	$this->page->setContent( $newContent );
	    	}
	    }
    }
    
    /**
     * Replace content on the page with data from the DB
     * @param String $tag the tag defining the area of content
     * @param int $cacheId the queries ID in the query cache
     * @return void
     */
    private function replaceDBTags( $tag, $cacheId )
    {
	    $block = '';
		$blockOld = $this->page->getBlock( $tag );
		
		// foreach record relating to the query...
		while ($tags = PCARegistry::getObject('db')->resultsFromCache( $cacheId ) )
		{
			$blockNew = $blockOld;
			// create a new block of content with the results replaced into it
			foreach ($tags as $ntag => $data) 
	       	{
	        	$blockNew = str_replace("{" . $ntag . "}", $data, $blockNew); 
	        }
	        $block .= $blockNew;
		}
		$pageContent = $this->page->getContent();
		// remove the seperator in the template, cleaner HTML
		$newContent = str_replace( '<!-- START ' . $tag . ' -->' . $blockOld . '<!-- END ' . $tag . ' -->', $block, $pageContent );
		// update the page content
		$this->page->setContent( $newContent );
	}
    
	/**
     * Replace content on the page with data from the cache
     * @param String $tag the tag defining the area of content
     * @param int $cacheId the datas ID in the data cache
     * @return void
     */
    private function replaceDataTags( $tag, $cacheId )
    {
	    $block = $this->page->getBlock( $tag );
		$blockOld = $block;
		while ($tags = PCARegistry::getObject('db')->dataFromCache( $cacheId ) )
		{
			foreach ($tags as $tag => $data) 
	       	{
		       	$blockNew = $blockOld;
	        	$blockNew = str_replace("{" . $tag . "}", $data, $blockNew); 
	        }
	        $block .= $blockNew;
		}
		$pageContent = $this->page->getContent();
		$newContent = str_replace( $blockOld, $block, $pageContent );
		$this->page->setContent( $newContent );
    }
    
    /**
     * Get the page object
     * @return Object 
     */
    public function getPage()
    {
	    return $this->page;
    }
    
    /**
     * Set the content of the page based on a number of templates
     * pass template file locations as individual arguments
     * @return void
     */
    public function buildFromTemplates()
    {
	    $bits = func_get_args();
	    $content = "";
	    foreach( $bits as $bit )
	    {
		    
		    if( strpos( $bit, 'skins/' ) === false )
		    {
			    $bit = 'skins/' . PCARegistry::getSetting('skin') . '/templates/' . $bit;
		    }
		    if( file_exists( $bit ) == true )
		    {
			    $content .= file_get_contents( $bit );
		    }
		    
	    }
	    $this->page->setContent( $content );
    }
    
    /**
     * Convert an array of data (i.e. a db row?) to some tags
     * @param array the data 
     * @param string a prefix which is added to field name to create the tag name
     * @return void
     */
    public function dataToTags( $data, $prefix )
    {
	    foreach( $data as $key => $content )
	    {
		    $this->page->addTag( $key.$prefix, $content);
	    }
    }
    
    public function parseTitle()
    {
	    $newContent = str_replace('<title>', '<title>'. $page->getTitle(), $this->page->getContent() );
	    $this->page->setContent( $newContent );
    }
    
    /**
     * Parse the page object into some output
     * @return void
     */
    public function parseOutput()
    {
	    $this->replaceBits();
	    $this->replaceTags();
	    $this->parseTitle();
    }
    
    
    
}
?>

So, what exactly does this class do?

Creates our page object, and bases it from template files, the page object contains the content and information which is needed to make-up the HTML of the page. We then buildFromTemplate('templatefile.tpl.php', 'templatefile2.tpl.php') to get the initial content for our page, this method takes any number of template files as its arguments, and stitches them together in order, useful for header, content and footer templates.

Manages the content associated with the page by helping the page object maintain a record of data to be replaced into the page, and also additional template bits which need to be incorporated into the page (addTemplateBit('userbar','usertoolsbar.tpl.php')).

Adds data and content to the page by performing various replace operations on the page content, including retrieving results from a cached query and adding them to the page.

The template file needs to mark within itself where a cached query needs to be retrieved and the data from the query replaced. When the template manager encounters a tag to replace which is a query, it gets the chunk of the page where it needs to iterate through by calling getBlock('block') on the page object. This chunk of content is then copied for each record in the query, and has tags within it replaced with the results from the query. We will take a look at how this looks in the template later in this tutorial.

Template Manager: Page

The page object is managed by the template manager, and it used to contain all of the details related to the page. This leaves the template manager free to manage, while making it easier for us to extend the functionality of this at a later date.

<?php

/**
 * This is our page object
 * It is a seperate object to allow some interesting extra functionality to be added
 * Some ideas: passwording pages, adding page specific css/js files, etc
 */
class page {

	// room to grow later?
	private $css = array();
	private $js = array();
	private $bodyTag = '';
	private $bodyTagInsert = '';
	
	// future functionality?
	private $authorised = true;
	private $password = '';
	
	// page elements
	private $title = '';
	private $tags = array();
	private $postParseTags = array();
	private $bits = array();
	private $content = "";
	
	/**
	 * Constructor...
	 */
    function __construct() { }
    
    public function getTitle()
    {
    	return $this->title;
    }
    
    public function setPassword( $password )
    {
    	$this->password = $password;
    } 
    
    public function setTitle( $title )
    {
	    $this->title = $title;
    }
    
    public function setContent( $content )
    {
	    $this->content = $content;
    }
    
    public function addTag( $key, $data )
    {
	    $this->tags[$key] = $data;
    }
    
    public function getTags()
    {
	    return $this->tags;
    }
    
    public function addPPTag( $key, $data )
    {
	    $this->postParseTags[$key] = $data;
    }
    
    /**
     * Get tags to be parsed after the first batch have been parsed
     * @return array
     */
    public function getPPTags()
    {
	    return $this->postParseTags;
    }
    
    /**
     * Add a template bit to the page, doesnt actually add the content just yet
     * @param String the tag where the template is added
     * @param String the template file name
     * @return void
     */
    public function addTemplateBit( $tag, $bit )
    {
	    $this->bits[ $tag ] = $bit;
    }
    
    /**
     * Get the template bits to be entered into the page
     * @return array the array of template tags and template file names
     */
    public function getBits()
    {
	    return $this->bits;
    }
    
    /**
     * Gets a chunk of page content
     * @param String the tag wrapping the block ( <!-- START tag --> block <!-- END tag --> )
     * @return String the block of content
     */
    public function getBlock( $tag )
    {
		preg_match ('#<!-- START '. $tag . ' -->(.+?)<!-- END '. $tag . ' -->#si', $this->content, $tor);
		
		$tor = str_replace ('<!-- START '. $tag . ' -->', "", $tor[0]);
		$tor = str_replace ('<!-- END '  . $tag . ' -->', "", $tor);
		
		return $tor;
    }
    
    public function getContent()
    {
	    return $this->content;
    }
  
}
?>

How can this class be extended and improved?

  • PostParseTags: You may wish to have tags replaced after most of the page has been parsed, maybe content in the database contains tags which need to be parsed.
  • Passworded pages: Assign a password to a page, check to see if the user has the password in a cookie or a session to allow them to see the page.
  • Restricted pages (although we need our authentication components first!)
  • Altering the
  • Dynamically adding references to javascript and css files based on the page or application.

Load core objects

Now that we have some objects which our registry is going to store for us, we need to tell the registry which objects these are. I've created a method in the PCARegistry object called loadCoreObjects which (as it says) loads the core objects. This means can can just call this from our index.php file to load the registry with these objects.

public function storeCoreObjects()
{
	$this->storeObject('database', 'db' );
	$this->storeObject('template', 'template' );
}

This method can be altered later to encorporate the other core objects the registry should load, of course there may be objects which we want our registry to manage, but only depending on the application the framework is used for. These objects would be loaded outside of this method.

Some Data

So that we can demonstrate the new features added to our framework, we need a database to make use of the database handler, and some of the template management functions (where we replace a block of content with the rows in the database).

The demonstration site we will make with our framework by the end of this series of tutorials is a website with a members directory, so let's make a very basic database table for members profiles, containing an ID, name, and email address.

Obviously, we need a few rows of data in this table!

A quick template

In order for anything to be displayed, we need a basic template, where we will list the data from our members table.

<html>
<head>
	<title> Powered by PCA Framework</title>
</head>
<body>
<h1>Our Members</h1>
<p>Below is a list of our members:</p>
<ul>
<!-- START members -->
<li>{name} {email}</li>
<!-- END members -->
</ul>
</body>
</html>

The START members and END members HTML comments denote the members block (which is obtained via the getBlock() method on the page), this is where the template manager will iterate through the records in the database and display them.

Framework in use

Now, we need to bring this all together, with our index.php file:

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

// store those core objects
$registry->storeCoreObjects();

// create a database connection
$registry->getObject('db')->newConnection('localhost', 'root', '', 'pcaframework');

// set the default skin setting (we will store these in the database later...)
$registry->storeSetting('default', 'skin');

// populate our page object from a template file
$registry->getObject('template')->buildFromTemplates('main.tpl.php');

// cache a query of our members table
$cache = $registry->getObject('db')->cacheQuery('SELECT * FROM members');

// assign this to the members tag
$registry->getObject('template')->getPage()->addTag('members', array('SQL', $cache) );

// set the page title
$registry->getObject('template')->getPage()->setTitle('Our members');

// parse it all, and spit it out
$registry->getObject('template')->parseOutput();
print $registry->getObject('template')->getPage()->getContent();

If we now view this page in our web browser, the results of the query are displayed on the page:

Coming in part 3...

In part three we will take a slight detour from the development side of our Framework, and look at how to design with our framework in mind, and how to slice up HTML templates so that they are suitable for our framework. When we start to build our first application with our framework, we will look in more detail at some of the workings of these classes. Finally, thank you for your comments last time!

  • Subscribe to the NETTUTS RSS Feed for more daily web development tuts and articles.


Related Posts

Check out some more great tutorials and articles that you might like

Enjoy this Post?

Your vote will help us grow this site and provide even more awesomeness

Plus Members

Source Files, Bonus Tutorials and
More for $9 a month for all TUTS+
sites in one subscription.

Join Now

User Comments

( ADD YOURS )
  1. PG

    Bliss September 15th

    Cool !!! Can’t wait for next week…

    ( Reply )
  2. PG

    Jonathan September 15th

    This is getting better and better. I want part three now. laugh.
    Seriously, this is becoming one of the best tuts I’ve come across on creating a framework. Thumbs Up.

    ( Reply )
  3. PG

    Shane September 15th

    interesting and useful information, even for those who currently use a framework such as CakePHP or Codeigniter.

    thanks.

    ( Reply )
  4. PG

    Guillaume September 15th

    I am so looking forward to the third part. Great tutorial !

    ( Reply )
  5. PG

    emilio September 15th

    E-S-P-E-C-T-A-C-U-L-A-R!!!! (in spanish ;) )

    I can’t download the src, the system say me “Access Denied” :P
    can you fix this problem?

    ( Reply )
  6. PG

    Pelle September 15th

    Nice tutorial :)
    But wouldn’t it be easier to use Smarty as a template engine? Building your own template engine that will fit all your needs is a huge huge huge amount of work :)

    ( Reply )
  7. PG

    rodrigo September 15th

    hi, an question, i can execute duoble loop ?
    example:


    {name} {email}


    {name} {email}

    sample php ?

    thx.

    ( Reply )
  8. PG

    rodrigo September 15th

    hi, an question, i can execute duoble loop ?
    example:

    –START seccion –
    {name} {email}

    –START subseccion –
    {name} {email}
    –END subseccion –

    –END seccion –

    sample php ?

    ( Reply )
  9. PG

    curtis allen September 15th

    nice tutorial.

    ( Reply )
  10. PG

    MIchael Peacock September 15th

    Thanks for the comments!

    @Pelle, it would be easier, but it is down to the needs of the framework / project really, I prefer a custom template engine, but other projects may be better off using Smarty.

    @rodrigo you can, but it needs some expansion, drop me an email (email address is in the “Our Members” image above) and I’ll explain in detail. Basically, you change your subsection seperators, so they are unique e.g. – START subsection_{id} – and then use “postParseTags” which are not fully implemented in the code above.

    ( Reply )
  11. PG

    Lamin Barrow September 15th

    WOW.. this is getting so much better. Thanks for all of your time and effort putting together.

    ( Reply )
  12. PG

    Ben Griffiths September 15th

    Very very interesting, thanks! :D

    ( Reply )
  13. PG

    Patrick Moore September 15th

    I’ve always practiced avoiding “SELECT *” in MySQL queries…

    Since you don’t appear to use nor need the ID column in your results set, should the query not read
    SELECT name,email FROM members
    ?

    Are there advantages to using “SELECT *” that I’m not realizing (other than laziness)?

    ( Reply )
  14. PG

    Niklas September 15th

    Indeed interesting and very nice to see a somewhat more advanced PHP tutorial. Looks like this will be very useful! Thank you!

    ( Reply )
  15. PG

    Miles Johnson September 15th

    Your template engine is outdated…. there are many ways to not do things like {var} or [var].

    You could simply have php in your template like so:

    $page_title

    And then within the template class do something like:

    $temp->bind(’page_title’, ‘Hello’);

    The way you have it is horrible for runtime since you have to process and replace a ton of strings.

    As for the mysqli, I dont see any benefits to it. It would be better to learn postgresql since that will be the new database type in the near future.

    ———————

    Currently your framework tutorials are good, but you are teaching people the wrong ways, or technically ways that would be slow/heavy during runtime and development.

    ( Reply )
  16. PG

    Miles Johnson September 15th

    And after reading the comments about the template system and doing loops, your system is still not correct.

    Here would be my example template file (named “homepage”):

    <?php foreach($members as $member) {
    echo $member .”;
    } ?>

    And then my template class opens homepage and applies vars:

    $temp->open(’homepage.php’, ‘home’);
    $temp->bind(’home’, ‘pageTitle’, ‘Homepage’);
    $temp->bind(’home’, ‘members’, array(’Miles’, ‘Jon’, ‘Chris’));

    And the result:

    Homepage

    Miles
    Jon
    Chris

    ————–

    Take a look at extract: http://us3.php.net/extract

    You do know your php but there are ways to do it better.

    ( Reply )
  17. PG

    Miles Johnson September 15th

    ^^ The above comment had the html stripped, bah.

    ( Reply )
  18. PG

    MIchael Peacock September 15th

    Thanks for the comments Miles. Re: MySQLi – MySQL is still very popular and I’d imagine most people will be working with it, so it seems more appropriate.

    Regarding the template engine, I’ve used this method for a few years and have never really thought much about upgrading it, thanks for pointing out a better way to do it. I’m aiming with these tuts to show a method to build a framework, and hopefully give the readers a structure to create, and an idea of what to include – then hopefully they would use their own code to actually do it. The only thing I dislike with your method, is that I strongly dislike adding any form of PHP into template files, however – that is just me! The extract function however seems very interesting, I can’t believe I’ve not used it before – thanks for pointing it out to me.

    There are always ways to do things better!

    ( Reply )
  19. PG

    Tony September 15th

    Im confused, why is the member code in the index.php shouldnt it have a dedicated controller?

    ( Reply )
  20. PG

    Miles Johnson September 15th

    Yeah the only thing I dislike about using custom variables is that it causes a lot more load on runtime because of the parsing, and also the fact you have to build a new system to parse when you could still use php.

    I even had a template engine basically like yours, but after a while it got annoying and found ways to make it simpler. I have a template class that ill upload soon to show different ways.

    But cant wait for part 3 ;)

    ( Reply )
  21. PG

    Nate September 15th

    Wow… This is pretty intense but on the other hand I think it can be very useful to know. Thanks for sharing.

    ( Reply )
  22. PG

    mattems September 15th

    This is the best series of tuts i have seen yet. I use Codeigniter as a framework, but getting into the core of building one sheds light on so many things.

    Thanks for this tutorial. Much appreciated!

    cant wait for part 3 (like everyone else) :)

    ( Reply )
  23. PG

    Andrea September 15th

    Amazing! Thats really amazing. My “dream” from a little time is to make my own framwork with a lot of jQuery scripts, and this tuts are really great, to do something as you need, and the feeling of making something yours is even greater:)

    Hope part 3 come very soon…!!

    But how many parts will be there, will you do something also about security??
    I tryied joomla for 2 months and and today someone hacked me the entire system :( (!!

    I have a question about flash and page reloading, where could I post it??

    Bye

    ( Reply )
  24. PG

    sinned September 15th

    a long wait for part 2 is over. and hell its worth waiting for this series. thanks a lot.

    ( Reply )
  25. PG

    insic September 15th

    really nice guys. its worth the wait. thats y i keep on asking this before. thank you finally its here.

    ( Reply )
  26. PG

    Prabhjeet Singh September 15th

    This tut rocks ! Seriously I love it, Thanks for writing this.

    ( Reply )
  27. PG

    Adam Jackett September 15th

    Very informative. I’m a big fan of CodeIgniter, but recently I’ve decided to build my own “simple” framework also, and this certainly helps. It’s nice to see what others are doing, it shows that there really isn’t a right way to do things, which is why there are so many frameworks available. I’m just trying to make mine as lightweight as I can get it and easy to adapt to any project. Anyways, thanks for this, can’t wait for the next part(s).

    ( Reply )
  28. PG

    ANeuby September 16th

    Very nice! I also started to build my own framework for small projects based on this tutorials. I am working for many years with PHP but in both tutorials, part 1 and now part 2 are some interesting logic technics which are great.

    ( Reply )
  29. PG

    cx42net September 16th

    Hey ! :)
    Interesting tutorial.

    The original purpose of Php was a template engine, so using it in template files is almost correct. Almost because if you have an independant web designer (and he knows some php), he can do wathever he want with your web apps. Things he cannot with a “string based” template engine. The problem with “string based” template engine, is that you always get limited by the possibilities (switching between even an odd for row set, modifying string for better render (substr, str_replace, etc), …), or use a haevy template engine like smarty. It’s a dilem and in my opinion, I think the final choice will be made depending of the number of workers on the application. If you’re the only one, maybe use php as template engine (you won’t destroy your controllers work into your view :p), and if the view is made by your web designers, maybe use a “string based” template engine ?

    An other point I’d like to submit is about mysqli your using. From the structure of your class, I suppose that your using Php 5.0 or newer (using public/protected/private attribute, magic method, etc). So anyone will use your framework will have Php 5.0 and probably 5.2, with SPL and PDO. Why aren’t you using them ? Pdo gives you more flexibility (you can use even MySQL or Postgre/Oracle, etc) and security (with prepare/execute methods) and SPL gives you a better structure of your code (with spl_autoload or countable/iterate interfaces, for example).

    But nevertheless, nice article :)

    ( Reply )
  30. PG

    BlazS September 16th

    Great job! Keep up the good work!

    ( Reply )
  31. PG

    simplechris September 16th

    Really great tut.

    @admin: how many chapters are you planning for this series?

    I strongly believe developers should be capable of writing their own framework, not just using open-source frameworks. Sure ZEND, CakePHP, Symfony (etc..) are great – but imo no match for self produced classes.

    As an aside, I feel the NETTUTs really is producing better content then the giants like smashingmag recently, and am a *really* big fan. Cheers to Collis and the envato team!

    ( Reply )
  32. PG

    jdeveloper September 16th

    Very very great tutorial except the template system. Its just wasting CPU cycles. The only reason for using templates like this is for designers and in such case I would try to do tags more similar like JSTL or something like that and compile the templates to native php code.

    Finaly I have a little question. I’m working on my own framework wich I expect to release it at begining of next year under GPL license and would like to replace my database layer wich yours modifying it a litle bit. Am I allow to incorporate it in my framework? Which license is applied to this tutorial?

    Thanks and wonderfull tutorial

    ( Reply )
  33. PG

    Sascha September 16th

    Great tutorial again, but I believe this part has to much of the template/database (btw why not use the PDO for the database?) stuff and delivers less of the framework knowledge.

    ( Reply )
  34. PG

    Michael Peacock September 16th

    @Sascha – it really depends on the need of the framework, because of the environments most of my sites run in, I chose not to use PDO. Again, I’m trying to illustrate a method of fitting together a framework, and I’m hoping that everyone following it will be changing it to their needs and their preference.

    @Tony – Yes, it should have its own controller, but that won’t be until later in the series. This installment was focusing on getting database and template support for the framework, and the easiest way to demo it was to make use of the index.php file. Later we will look at taking advantage of MVC style. I probably should have been more clear in the tutorial.

    @Patrick, I used Select * because when I was initially working on the tutorial, I used all fields, however I too generally avoid it.

    ( Reply )
  35. PG

    LeanderD September 16th

    Great tutorials! Can’t wait for the 3rd part!

    ( Reply )
  36. PG

    Tim September 16th

    Have you ever thought of using PHPTAL (http://phptal.motion-twin.com) for your templating?
    I realise this series is about rolling your own everything, however it’s a pretty drastic paradigm shift and might show you other ways of templating that you haven’t necessarily thought of.
    I recently started using it in a small-ish blog engine I’m writing for myself, and it’s nice to read X/HTML without having a whole bunch of template-specific tags in my templates.

    ( Reply )
  37. PG

    Gabriel PREDA September 17th

    1.

    So, what is MVC? MVC is a design pattern (as was the Singleton and Registry patterns we looked at in part 1)

    MVC is an arhitectual pattern
    http://en.wikipedia.org/wiki/Model-view-controller
    http://en.wikipedia.org/wiki/Architectural_pattern_(computer_science)

    Singleton is a design pattern
    http://en.wikipedia.org/wiki/Singleton_pattern
    http://en.wikipedia.org/wiki/Design_pattern_(computer_science)

    2.
    Secondly why would you want to implement your own query-cache and not rely on the database one MySQLs one ?

    ( Reply )
  38. PG

    David September 17th

    Good points Gabriel. About number 2, this home-baked query cache will also consume a fair amount of memory, depending on what you do with this framework.

    ( Reply )
  39. PG

    FINCH September 17th

    rock on bro! great article! :)

    ( Reply )
  40. PG

    Joey M September 17th

    AMAZING!! I am your biggest fan!

    ( Reply )
  41. PG

    Christoph Herrmann September 17th

    Hi,

    after the comments for the first part, yet for the second. :)

    Database Handler:
    – Why you don’t use PDO or more abstraction? MySQL is good, but not the only database
    – I can’t execute a second query and reading the first? very unusefull for nested statements
    – Why the class is not a singleton class? I think a central class for all singletons have too many dependence and it’s error-prone if the user don’t store manually the objects
    – Why you use “trigger_error” for incorrect statements? What reason speak against exceptions?
    – In every case I wan’t use a other connection, I must change the active connection bevor the statement to the other connection and after to the standardconnection for the rest of the application? I think it is very error-prone

    the rest i have not read. the database handler is not very usefull without the possibility for nested statements and the missing exception handling in my eyes.

    ( Reply )
  42. PG

    James September 18th

    Interesting tut!

    I don’t think I’m ever going to get to this kind of level in PHP, simply because there is no necessity for me … But it’s definitely all good to know and your tuts serve as a good reference! :)

    ( Reply )
  43. PG

    rap September 18th

    wow this is great dude! hoping of part 3 to come out soon!

    ( Reply )
  44. PG

    dangerduck September 18th

    This is a great tut. I’m learning a ton of really useful information from it.

    Thanks guys…

    ( Reply )
  45. PG

    Simon Zimmermann September 18th

    Nice tutorial. Also good discussion on different ways of doing this in the comment section. Keep up the good writing :)

    o/ simon.

    ( Reply )
  46. PG

    Required September 18th

    Nobody noticed the huge SQL Injection vulnerability in the code?

    From what I see any code using either insertRecord() or updateRecord() functions will have to escape data (which is not been done in the example).

    ( Reply )
  47. PG

    Michael Peacock September 18th

    @Required, I’d hardly call it a vulnerability that you need to escape data…if you want the function to cleanup data itself, then write it in, but I *always* sanitize data before it gets passed to any database function. The way I’ve used it in the tutorial in my example, is with trusted data i.e. data I have put into the code…but if you are dealing with untrusted/user submitted data it should go without saying that you should sanitize it first! Several tutorials could be written about security alone (I plan on covering some security issues in a tutorial later)

    @everyone, thanks for the comments, and all of those relating to alternative templating methods – its quite interesting to see alternative and better ways to do it. As I’ve said before, with these tutorials, Im showing you *a* method to create a framework, I’d certainly suggest using your own components in the framework, here is just a method to slot things together, my code should just be used as a guide.

    ( Reply )
  48. PG

    arthur September 21st

    Really great tutorial Michael. Very clear and well documented.
    I can’t wait to see next part..
    thank you very much.

    ( Reply )
  49. PG

    Alex September 23rd

    Suggestions about the DB class:

    We can use mysql_real_escape_string / mysql_escape_string to filter user inputs. (security)
    I also recommend memcached, which enhances DB caching system (performance)

    ( Reply )
  50. PG

    insic October 1st

    Part 3 please.

    ( Reply )
  51. PG

    John October 1st

    Great article and comments!! I’ve learned alot from both, but for all those that still don’t get it, Michael summed it up when he said “here is just a method to slot things together, my code should just be used as a guide.” All the other suggestions and improvements are very helpful but lets not bash MP for any short comings of this Tut/framework so far. It’s really just a way to help us structure our apps and make improvements as we each individually see needed!…

    @MP – I don’t know if you’ve thought about scalability in the framework. I don’t know alot about it but my friend tells me that it’s useful to code your app from the beginning with one db connection for reads and one for writes, in case you need to have your db servers set up where one is dedicated to writes in the future. Do you have any recommendations for doing that? Although I suppose that with this framework that change should be easy to make in the future should you ever need it, right?

    Thanks again, I’m eagerly looking forward to the rest of the series!

    ( Reply )
  52. PG

    Ajith Ekanayake October 1st

    Hats off to you guys.

    Great stuff. When is the part 3 ..

    Ajith

    ( Reply )
  53. PG

    hardyboyz October 5th

    this is really good tutorial…when you will publish the third?

    ( Reply )
  54. PG

    John October 6th

    This is a great tutorial but the suspense is killing me, when’s part 3 going to be released?

    ( Reply )
  55. PG

    Abraham L Coetzee October 7th

    Ah this is delicious, thank you kindly!

    ( Reply )
  56. PG

    vnju October 8th

    Great! I can’t wait part 3 anymore, please come soon… Thanks

    ( Reply )
  57. PG

    Joey M October 10th

    Michael,
    I am seriously on the edge of my seat waiting for part 3. I am using a very similar method (much more extended) for my school project which was inspired by your first tut. I can’t wait to implement my designers work into the framework. Any idea when the next tut is coming?

    ( Reply )
  58. PG

    Jeffrey Way October 10th

    @Joey – Michael’s currently working on it. It shouldn’t be too much longer. :)

    ( Reply )
  59. PG

    r3l4x October 16th

    Hey!

    Great Tut!!! Can’t wait for part 3.

    But there is an mistake in the template class / function replaceBits() in line 52:

    $templateContent = file_get_contents( $bit );

    should be

    $templateContent = file_get_contents( $template );

    Best regards

    r3l4x

    ( Reply )
  60. PG

    Salman October 17th

    we are waiting for part 3 so we can start designing our own framework “”PHPonTrack” :)
    thanks for the part 1 and part 2

    regards
    salman sadiq

    ( Reply )
  61. PG

    mattlanter October 24th

    Part 3 coming anytime soon?

    ( Reply )
  62. PG

    Santiago October 25th

    Great tutorial!
    Where is the part 3?

    ( Reply )
  63. PG

    John October 27th

    Part 3? Pretty Please!

    ( Reply )
  64. PG

    Jeffrey Way October 27th

    Hey guys. As soon as Michael sends me Part 3, we’ll post it ASAP!

    ( Reply )
  65. PG

    Michael Peacock October 27th

    Hey,

    Sorry about the delay everyone, my business partner has written part 3 (as it is design related) but it we have had a busy couple of weeks, I’ve only just finished going through it. It’s just been emailed to Jeffery so hopefully it will be on the site soon.

    Just an FYI: I’m going to block write the next few instalments asap, so we don’t have any delays like this again.

    ( Reply )
  66. PG

    Simon Zimmermann October 29th

    Sweet, Michael. Looking forward to reading it. It’s a nice inspiration to learn about design patterns.

    ( Reply )
  67. PG

    Mandy Singh October 31st

    Hi Michael,

    Thanks a lot, I’m really enjoying this tute and am finding it handy. Looking forward to the rest :)

    ( Reply )
  68. PG

    JJ November 10th

    Heya! It’s been a few weeks since last comment on update? Where’s PT3 lurking? :>

    ( Reply )
  69. PG

    Michael Peacock November 10th

    I don’t know, I’m guessing Jeffery is waiting for an empty slot in the tutorials schedule?

    ( Reply )
  70. PG

    kaqfa November 11th

    Please….. i wait part 3 for weeks, and …..

    ( Reply )
  71. PG

    Milner08 November 14th

    Any news on part 3??

    Hope it comes soon!

    ( Reply )
  72. PG

    Jeffrey Way November 14th

    I have Part 3 scheduled for next week.

    ( Reply )
  73. PG

    Magy November 18th

    Thanks, this tutorial is fantastic :)

    ( Reply )
  74. PG

    MJ November 23rd

    Very useful and education tutorial. You explain it very clearly- really helping me wrap my brain around OOP in PHP. I’ll definitely recommend this series as a must-read to my friends!

    Thanks for your hard work and sharing your knowledge with us.
    Part 3 should be great.

    ( Reply )
  75. PG

    T Pham November 25th

    good job man,
    i dont know how you do that but it’s perfect for me, ilike it and waiting, waiting for the great part :) . Thank

    ( Reply )
  76. PG

    restaurants groningen January 17th

    Great tutorial,

    It’s also a good start to understand the capabilities of major frameworks like code igniter and Zend.

    This tutorial gives you a great view of the idea behind the MVC structure. Definitly when you are a php rookie.

    Greetings from the Netherlands (Groningen)

    ( Reply )
  77. PG

    shiby February 7th

    Please how I call function addTemplateBit ??? If I use in index this one : $registry->getObject(’template’)->addTemplateBit(’userbar’,'usertoolsbar.tpl.php’); … and put to template : {userbar} , so nothing displayed. Plsease help I use this framework for my personal CMS system. I so much need this. THANKS !

    ( Reply )
  78. PG

    Marc February 8th

    found a slight typo in the code on line 49 of the template.class.php file you wrote:

    $templateContent = file_get_contents($bit);

    $bit isn’t defined yet $bits is.

    ( Reply )
  79. PG

    KP March 6th

    How do I use insertRecords()?

    ( Reply )
  80. PG

    Felix March 8th

    Thanks a lot for those tutorials, it’s really interesting to build a framework from scratch!
    I’ve just noticed a slight problem with the database class. After destructing the object, the connections will still be opened. This is due to the fact that there isn’t any magic method called __deconstruct. The actual destructor method is __destruct. Your __deconstruct method has to be called explicitly in the code to close the connections, which makes it pretty useless. The reason it doesn’t triggers any errors is because PHP believes you want to call a method __deconstruct and not build a destructor ;)

    Still, this is a great tutorial! Looking forward for part IV and V!

    ( Reply )
  81. PG

    mubarak March 24th

    without storing the database object how you called it?? will it work?? in the database example..

    ( Reply )
  82. PG

    J April 7th

    You sure are the man, i wish you all the best.
    Thank you for taking time to show a oop app.

    ( Reply )
  83. PG

    SammyK May 4th

    Fantastic article, Michael Peacock. You are my hero. ;)

    Just wanted to point out a minor adjustment for those who are integrating their own error reporting into the database class.

    In the method: newConnection(), this:

    $this->connections[$connection_id]->error

    Doesn’t actually return an error – errors that occur when connecting to the database are returned with:

    $this->connections[$connection_id]->connect_error

    Or before 5.2.9:

    mysqli_connect_error()

    ( Reply )
  84. PG

    CgBaran Tuts May 5th

    Great tutorial thanks

    ( Reply )
  85. PG

    raw May 26th

    a very nice database class indeed. i just noticed that sanitizedata was not used inside the class so we don’t have to call it in the model/controller. is it also a good thing if we use utf8_encode along with sanitizedata. thanks..

    ( Reply )
  86. PG

    Chris July 9th

    ummm help i get this error.

    Fatal error: Call to undefined method PCARegistry::storeCoreObjects() in C:\cefx\www\c\index.php on line 37

    ( Reply )
  1. Arrow
    Gravatar

    Your Name
    July 9th