Writing a Php5 Mvc Framework Part 6: Flexible db connection, Load Css and Js files,
Creating Front Controller, doing Method Overridding and Obullo style writing
Forget all other old parts because of ssc pattern is depreciated, very silly think ! Because of it has got a lot of disadvantages, but take it easy we learnt how to develop a framework writing a deprecated pattern :).Please don’t use this framework before releasing the beta version.
The most important thing in this part we will learn the Obullo style writing.
Alpha 8 version changes:
- SSC pattern depreciated and removed from the framework , we use loader class as static from now on
- renamed first /controllers dir as /directories
- changed common.php get_config() function, chaged config.php file.
- added multiple db connection
- integrated css, script and js loader functions changed
- renamed SSC class as Ob class, added new Obullo class, Library Class depraciated.
- added OB_Front Controller Class and added User/Front_Contoller class for method overridding
- added obullo_style_writing option to config.php file.
Directory Structure:
Updates about directory structure
We have three diferent directories,
application/directories/ this folder contains application views, models, library, script files relating to called controller.
Directory functions:
| Function | Directory | Description |
|---|---|---|
| loader::helper() | .application/directories/{$controller}/helpers/ | load helper file from called controller dir |
| loader::lib() | .application/directories/{$controller}/libraries/ | load library file from called controller dir |
| loader::lang() | .application/directories/{$controller}/lang/ | load language file from called controller dir |
| loader::model() | .application/directories/{$controller}/models/ | load model file from called controller dir |
| loader::script() | .application/directories/{$controller}/scripts/ | load script file from called controller dir |
| loader::view() | .application/directories/{$controller}/views/ | load view file from called controller dir |
| loader::css() | .application/directories/{$controller}/views/css/ | load .css file from called controller dir |
- aplication/ this folder contains application views, language, models, library and script files.
Loading the files from application folders means ‘common application files’ who want these files use for different controllers.
Aplication functions:
| Function | Directory | Description |
|---|---|---|
| loader::app_helper() | .application/helpers/ | load common helper file from /application dir |
| loader::app_lib() | .application/libraries/ | load common library file from /application dir |
| loader::app_lang() | .application/language/ | load common lang file from /application dir |
| loader::app_model() | .application/models/ | load common model file from /application dir |
| loader::app_script() | .application/scripts/ | load common script file from /application dir |
| loader::app_view() | .application/views/ | load common view file from /application dir |
- base/ this folder just contains core files of the framework.
You can’t do any changes in this folder.This is for framework flexibility, when the framework go up the next version you just overwrite new files to /base folder.
Base functions:
| Function | Directory | Description |
|---|---|---|
| loader::base_helper() | .base/helpers/ | load base (core) helper file from /base dir |
| loader::base_lib() | .base/libraries/ | load base (core) library file from /base dir |
| loader::base_lang() | .base/lang/ | load base (core) language file from /base dir |
| loader::base_script() | .base/scripts/ | load base (core) script file from /base dir |
- source/ this folder just contains resource files like /images, /css and /js files.
Source functions:
| Function | Directory | Description |
|---|---|---|
| loader::source_js() | .source/js/ | load js file from /source dir |
| loader::source_css() | .source/css/ | load css file from /source dir |
Flexible get_cofig() function:
get_config() function now is very flexible, we can get all configuration files via get_static() method we use it like this
get_config( $config_filename, $variable);
forexample we want to get $robots config variable from application/config/user_agent.php file we just write config file name and variable
get_config(’user_agent’, ‘robots’);
if config file name and config variable name is same you don’t need to write variable name.
get_config(’config’);
<?php
/**
* Loads the (static) configuration or language files.
*
* @access private
* @author Ersin Güvenç
* @param string $filename file name
* @param string $var variable of the file
* @param string $folder folder of the file
* @version 0.1
* @version 0.2 added $config_name param
* @version 0.3 added $var variable
* @version 0.4 renamed function as get_static ,renamed $config_name as $filename, added $folder param
* @return array
*/
function get_static($filename = 'config', $var = '', $folder = '')
{
static $static = array();
if ( ! isset($static[$filename]))
{
if ( ! file_exists($folder.DS.$filename.EXT))
throw new CommonException('The static file '.DS.$folder.$filename.EXT.' does not exist.');
require($folder.DS.$filename.EXT);
if($var == '') $var = &$filename;
if ( ! isset($$var) OR ! is_array($$var))
throw new CommonException('The static file '.DS.$folder.$filename.EXT.' file does not appear to be formatted correctly.');
$static[$filename] =& $$var;
}
return $static[$filename];
}
/**
* Get config file.
*
* @param string $filename
* @param string $var
* @return array
*/
function get_config($filename = 'config', $var = '')
{
return get_static($filename, $var, 'application'.DS.'config');
}
$config = get_config('user_agents', 'robots'); // get $robots variable from application/config/user_agent.php
print_r($config);
// output
// Array ( [googlebot] => Googlebot [msnbot] => MSNBot [slurp] => Inktomi Slurp [yahoo] => Yahoo [askjeeves] => AskJeeves [fastcrawler] => FastCrawler [infoseek] => InfoSeek Robot 1.0 [lycos] => Lycos )
$database = get_config('database'); // get $database variable from application/config/database.php
print_r($database['db']);
// output
// Array ( [hostname] => localhost [username] => root [password] => [database] => example_db [dbdriver] => mysql [dbh_port] => [char_set] => utf8 )
?>
Multiple Db Connection:
Multiple connection very flexible because of you can change the db variable and anything what you want.Open your /config/database.php file and add your second database config.
<?php
if( !defined('BASE') ) exit('Access Denied!');
/**
* Obullo Framework (c) 2009.
*
* PHP5 MVC-Min Software for PHP 5.2.4 or newer
* Derived from Code Igniter
*
* @package obullo
* @author obullo.com
* @copyright Ersin Güvenç (c) 2009.
* @since Version 1.0
* @filesource
* @license
*/
$database['system']['active_record'] = TRUE;
$database['db']['hostname'] = "localhost";
$database['db']['username'] = "root";
$database['db']['password'] = "";
$database['db']['database'] = "example_db";
$database['db']['dbdriver'] = "mysql";
$database['db']['dbh_port'] = "";
$database['db']['char_set'] = "utf8";
// second database
$database['db2']['hostname'] = "localhost";
$database['db2']['username'] = "root";
$database['db2']['password'] = "";
$database['db2']['database'] = "texminator";
$database['db2']['dbdriver'] = "mysql";
$database['db2']['dbh_port'] = "";
$database['db2']['char_set'] = "utf8";
//----------- config/database.php ---------------//
?>
Now you can use your db2 connection like this
<?php
// Default Connection from config file
//------------------- db ----------------------//
loader::database(); // default static connection
$this->db->query('SELECT * FROM articles');
$result = $this->db->all(assoc);
print_r($result);
echo '<br /><br />';
// Custom Connection from config file
//------------------- db2 ----------------------//
loader::database('db2'); // second static connection
$this->db2->query('SELECT * FROM tr_users');
$result2 = $this->db2->all(assoc);
print_r($result2);
echo '<br /><br />';
?>
This is really good but if you want to a dynamic db connection without touch the db configuration file this solution will save your life.
Look at
<?php
// Dynamic custom connection
//------------------- db3 ----------------------//
$params = array(
'variable' => 'db3', // this->db3
'hostname' => 'localhost',
'username' => 'root',
'password' => '',
'database' => 'nested_model',
'dbdriver' => 'mysql',
'dbh_port' => '',
'char_set' => 'utf8',
);
loader::database($params); // manual dynamic connection with params
$this->db3->query('SELECT * FROM category');
$result3 = $this->db3->all(assoc);
print_r($result3);
echo '<br /><br />';
?>
Good, but also you want to get db configration items.
I added a new function called db_item() and its defined in Common.php file.
Call it like this db_item(’hostname’, ‘db2′);
<?php
echo db_item('username', 'db2');
// output
// root
// or you can get all database config variables
// call the get_config function
$database = get_config('database');
print_r($database['db2']);
// output
// Array ( [hostname] => localhost [username] => root [password] => [database] => texminator [dbdriver] => mysql [dbh_port] => [char_set] => utf8 )
?>
How to load Css and Script files
For loading source file look at run() method
<?php
Class Test extends Controller
{
public $sample_var = 'this is sample var !';
function __construct()
{
parent::__construct();
parent::__user();
loader::base_helper('form'); // load helper from /base directory
loader::base_helper('url');
}
function index()
{
loader::base_lang('calendar');
echo ob::lang('cal_su');
$this->title_tag = 'This is the default function test/test/index ! (defined in config/routes.php)'; // look at /User folder
$this->head_tag = loader::script('test');
$data['example_var'] = 'example var works !';
$this->body_tag = loader::view('view_index',$data);
loader::app_view('view_base');
}
function run()
{
$this->head_tag = loader::source_css(array('obullo','example'));
$this->head_tag .= loader::source_js('jquery');
$this->head_tag .= loader::script('test');
$this->title_tag = 'Im the Test2 Controller !';
$data['sample_array'] = array('1','2','3','4','5');
$data['example_var'] = 'Hello World!';
$this->body_tag = loader::view('view_test',$data);
loader::app_view('view_base');
}
} //end of the class.
?>
As you can see all source file stored to $this->head_tag variable.We load the our base view file with
loader::app_view(’view_base’); function. This function load the view_base.php file from
/application/views/ folder. And we print the $this->head_tag variable in this view.
Creating Front Controller and Method Overridding
If you want to change all core functions without touching the core of the framework we need a
user and base front controllers, i guess front controller required for framework flexibility because of
someone may want to change the calling controller operations, file constants, include functions etc..
I added an OB_Front_Controller class to base folder.
<?php
/**
* Base Front Controller Class
*
* Pre-controller for calling controllers
*
* @package Obullo
* @subpackage Base
* @category Front Controllers
* @author Ersin Güvenç
* @link
*/
class OB_Front_Controller
{
/**
* Constructor
*
* Set Front Controller Defaults
* Suitable for Method Overriding
*
* @see http://www.java2s.com/Code/Php/Class/
* MethodoverrideforRectangleclass.htm
*
* @access public
*/
public function __construct()
{
require (BASE.'config'.DIRECTORY_SEPARATOR.'ob_constants'.EXT);
require (BASE.'config'.DS.'fl_constants'.EXT);
require (BASE.'libraries'.DS.'Errors'.EXT);
require (BASE.'libraries'.DS.'Registry'.EXT);
require (BASE.'libraries'.DS.'Common'.EXT);
}
/**
* Run the Application
*
* @access public
*/
public function run()
{
ini_set('display_errors', config_item('display_errors'));
date_default_timezone_set(config_item('timezone_set'));
$Uri = base_register('URI');
$Router = base_register('Router');
header('Content-type: text/html;charset='.config_item('charset')); // UTF-8
$GLOBALS['d'] = $Router->fetch_directory(); // Get requested directory
$GLOBALS['c'] = $Router->fetch_class(); // Get requested controller
$GLOBALS['m'] = $Router->fetch_method(); // Get requested method
// Check the controller exists or not
if ( ! file_exists(DIR.$GLOBALS['d'].DS.'controllers'.DS.$GLOBALS['c'].EXT))
{
if($Router->query_string) show_404("{$GLOBALS['c']}/{$GLOBALS['m']}");
throw new CommonException('Unable to load your default controller.
Please make sure the controller specified in your Routes.php file is valid.');
}
require (BASE.'libraries'.DS.'Ob'.EXT);
require (BASE.'libraries'.DS.'Loader'.EXT);
require (BASE.'libraries'.DS.'Obullo'.EXT);
require (BASE.'libraries'.DS.'Controller'.EXT);
require (BASE.'libraries'.DS.'Model'.EXT);
// call the controller.
require (DIR.$GLOBALS['d'].DS.'controllers'.DS.$GLOBALS['c'].EXT);
if ( ! class_exists($GLOBALS['c'])
OR $GLOBALS['m'] == 'controller'
OR strncmp($GLOBALS['m'], '_', 1) == 0
OR in_array(strtolower($GLOBALS['m']), array_map('strtolower', get_class_methods('Controller')))
)
{
show_404("{$GLOBALS['c']}/{$GLOBALS['m']}");
}
// If Everyting ok Declare Called Controller !
$OB = new $GLOBALS['c']();
// Check method exist or not
if ( ! in_array(strtolower($GLOBALS['m']), array_map('strtolower', get_class_methods($OB))))
{
show_404("{$GLOBALS['c']}/{$GLOBALS['m']}");
}
// Call the requested method. 1 2 3
// Any URI segments present (besides the directory/class/function)
// will be passed to the method for convenience
call_user_func_array(array($OB, $GLOBALS['m']), array_slice($Uri->rsegments, 3));
// Close the pdo connection ..
if (class_exists('DB') AND isset($OB->db))
$OB->db = NULL;
// Close your custom db connections if its exists !
// ..$OB->db2 = NULL;
// .
}
} // end class.
/*
|--------------------------------------------------------------------------
| Custom User Front Controller
|--------------------------------------------------------------------------
|
| User can create own Front Controller who want extend
| and do method overridding for base OB_Front_Controller library
|
*/
require('application'.DIRECTORY_SEPARATOR.'user'.DIRECTORY_SEPARATOR.'Front_Controller'.EXT);
// END Front Controller class
/* End of file Front_Controller.php */
/* Location: ./base/libraries/Front_Controller.php */
?>
I also added a Front_Controller class to aplication/User folder.By the way don’t forget you can edit all files in the
/User folder.Open the Front Controller class and overwrite to existing methods.
This known as method overridding in the PHP language.
<?php
if( !defined('BASE') ) exit('Access Denied!');
/**
* This is your front controller class you can change it.
* It extends to Base Front Controller Class.
* You will just do method overriding.
*
* @see Method Overridding
* @link http://www.java2s.com/Code/Php/Class/
* MethodoverrideforRectangleclass.htm
*/
Class Front_Controller extends OB_Front_Controller
{
public function __construct()
{
// remove parent code
// copy base/base/OB_Front_Controller __construct()
// method contents and paste here !
// and customize this contents !
parent::__construct();
}
public function run()
{
// remove parent code
// copy base/base/OB_Front_Controller run()
// method contents and paste here !
// and customize this contents !
parent::run();
}
}
// end of the class.
?>
And this is index.php file its changed.
<?php
/**
|--------------------------------------------------------------------------
| Obullo Framework (c) 2009 - 2010.
|--------------------------------------------------------------------------
|
| @VERSION 1.0 @alpha8 (Current)
|
| PHP5 MVC-min Software for PHP 5.2.4 or newer
| Derived from Code Igniter.
|
| @license public
| @see license.txt
*/
// --------------------------------------------------------------------
define('EXT', '.php');
define('BASE', 'base'. DIRECTORY_SEPARATOR);
require(BASE . 'libraries' . DIRECTORY_SEPARATOR . 'Front_Controller.php');
$application = new Front_Controller();
$application->run();
// --------------------------------------------------------------------
?>
Maybe you had trouble why we use the front controller ? Because of someone may want to call the controllers directly or want to setup a rest server, i thought front controller required in a framework if you want to do a rest server outside of the framework structure, just create a Front_controller2 class under the /user folder, copy index.php and save it as server.php in the root. Server.php will be your custom front controller now you can do all rest request to server.php.
Obullo style writing
i added a config['obullo_style_writing'] option into /config/config.php file.While this option is true you can call all base libraries as static.
Look at directories/controllers/test/test_input.php
Class Test_input extends Controller
{
function __construct()
{
parent::__construct();
parent::__user();
loader::base_helper('form');
loader::base_lib('session');
loader::database();
}
function index()
{
$this->title_tag = 'Input and Session Class Test !! ';
$this->head_tag = loader::source_css('example');
session::set('test','this is a test session variable !');
$this->body_tag = session::get('test').'<br /><br />';
if(input::post('username'))
$this->body_tag .= '<b>username: </b>' . input::post('username');
$this->body_tag .= form_open('test/test_input');
$this->body_tag .= form_input('username', input::post('username'));
$this->body_tag .= form_submit('send','Send');
$this->body_tag .= form_close();
loader::app_view('view_base');
}
} //end of the class.
As you see above the code, we can call all base libraries via library name.In this example we called session and input classes as static.You can call all other base libraries which is exists in the .base/shortcuts folder owing to loader::shortcut() function that called before from .base/obullo/Conrtoller.php file.
This is the .base/obullo/Contoller.php file , all base library shortcut classes automaticaly initialize in there.
/**
* Obullo Controller
*
* @package Obullo
* @subpackage Base.libraries
* @category Libraries
* @version 1.0
* @version 1.1 renamed Register as base_register
* @version 1.2 added 'extends to ob'
* @version 1.3 added __autoloader()
* @version 1.4 removed __autoloader()
* @version 1.5 added loader::shortcut()
* @deprecated self::__autoloader()
*/
Class Controller extends ob
{
function __construct()
{
// warning ! parent::__construct()
// must be at the top otherwise
// __autoloader functionality does not work !
parent::__construct();
$this->ob_init();
log_message('debug', "Controller Class Initialized");
}
function ob_init()
{
$Classes = array(
'config' => 'Config',
'input' => 'Input',
'lang' => 'Lang',
'router' => 'Router',
'uri' => 'URI',
'content' => 'Content',
);
// Auto load default base libraries.
foreach ($Classes as $public_var => $Class)
{
$this->$public_var = base_register($Class);
}
// Auto load default base shortcuts
if(config_item('obullo_style_writing'))
{
loader::shortcut('config');
loader::shortcut('input');
loader::shortcut('lang');
loader::shortcut('uri');
}
} //end function.
} //end class.
also when you use the loader::base_lib() function in a file, this function also load automaticaly its shortcut file which before defined in the base/shortcut folder.
Quote comes from .base/obullo/loader.php file.
<?php
/**
* loader::base_lib();
*
* load base libraries from /base folder.
*
* @param mixed $class
* @param mixed $static_or_params
* @return self::_library()
*/
public static function base_lib($class, $static_or_params = NULL)
{
self::_library($class, $static_or_params, TRUE);
self::shortcut($class);
}
?>
if you still think like ‘this is not enough for me’ , i added a helpers folder to application/user/ directory.You can also call your custom libraries as static from this folder.
Like this
loader::user_helper(’your_class_helper’);
Quote comes from directories/test/test.php file.
<?php
function user_helper()
{
loader::user_helper('user'); // load class helper from /user directory
loader::lib('test_lib');
echo user::test_lib();
echo '<br /><br />';
}
?>
i added a user.php static class to application/user folder that means i can call my test_lib.php library as static that defined before in the directories/test/libraries folder.
<?php
/**
* Class helpers must be static,
* and class name must be contain lowercase letters.
*/
Class user
{
public static function test_lib()
{
return ob::instance()->test_lib->test();
}
}
?>
Download Obullo Framework 1.0 @alpha8
Many thanks to who read this article , please tell me your ideas or questions .. anything about it..
i will keep your ideas /* copyright @author */ blocks in this framework…

Just in the last two weeks I decided to write my own framework from scratch, not only to help me learn more about OOP in PHP, but to understand exactly what was happening and how. Your tutorials really helped me in writing my own and understanding the inner workings. Thanks for the time/effort you’ve invested.