My first php framework

My first php framework

What is MVC?

Model–View–Controller (MVC) is an architectural pattern used in software engineering. Successful use of the pattern isolates business logic from user interface considerations, resulting in an application where it is easier to modify either the visual appearance of the application or the underlying business rules without affecting the other. In MVC, the model represents the information (the data) of the application; the view corresponds to elements of the user interface such as text, checkbox items, and so forth; and the controller manages the communication of data and the business rules used to manipulate the data to and from the model.

1. Model handles all our database logic. Using the model we connect to our database and provide an abstraction layer.
2. Controller represents all our business logic i.e. all our ifs and else.
3. View represents our presentation logic i.e our HTML/XML/JSON code.

Why should I write my own framework?

So why should you write your own framework? Firstly, it is a great learning experience. You will get to learn PHP inside-out. You will get to learn object-oriented programming, design patterns and considerations.

More importantly, you have complete control over your framework. Your ideas can be built right into your framework. Although always not beneficial, you can write coding conventions/functions/modules the way you like.

Lets dive right in

Directory Structure

Although we will not be a couple of directories mentioned above for this tutorial, we should have them in place for future expansion. Let me explain the purpose of each directory:

application – application specific code
config – database/server configuration
db – database backups
library – framework code
public – application specific js/css/images
scripts – command-line utilities
tmp – temporary data

Once we have our directory structure ready, let us understand a few coding conventions.

1. mySQL tables will always be lowercase and plural e.g. items, cars
2. Models will always be singular and first letter capital e.g. Item, Car
3. Controllers will always have “Controller” appended to them. e.g. ItemsController, CarsController
4. Views will have plural name followed by action name as the file. e.g. items/view.php, cars/buy.php

We first add .htaccess file in the root directory which will redirect all calls to the public folder

 RewriteEngine on RewriteRule ^$ public/ [L] RewriteRule (.*) public/$1 [L] 

We then add .htaccess file to our public folder which redirect all calls to index.php. Line 3 and 4 make sure that the path requested is not a filename or directory. Line 7 redirects all such paths to index.php?url=PATHNAME

 RewriteEngine On RewriteCond % !-f RewriteCond % !-d RewriteRule ^(.*)$ index.php?url=$1 [PT,L] 

This redirection has many advantages-
a) we can use it for bootstrapping i.e. all calls go via our index.php except for images/js/cs.
b) we can use pretty/seo-friendly URLS
c) we have a single entry point

Now we add index.php to our public folder

. This is to avoid injection of any extra whitespaces in our output. For more, I suggest you view Zend's coding style. Our index.php basically set the $url variable and calls bootstrap.php which resides in our library directory. Now lets view our bootstrap.php else < error_reporting(E_ALL); ini_set('display_errors','Off'); ini_set('log_errors', 'On'); ini_set('error_log', ROOT.DS.'tmp'.DS.'logs'.DS.'error.log'); >> /** Check for Magic Quotes and remove them **/ function stripSlashesDeep($value) < $value = is_array($value) ? array_map('stripSlashesDeep', $value) : stripslashes($value); return $value; >function removeMagicQuotes() < if ( get_magic_quotes_gpc() ) < $_GET = stripSlashesDeep($_GET ); $_POST = stripSlashesDeep($_POST ); $_COOKIE = stripSlashesDeep($_COOKIE); >> /** Check register globals and remove them **/ function unregisterGlobals() < if (ini_get('register_globals')) < $array = array('_SESSION', '_POST', '_GET', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES'); foreach ($array as $value) < foreach ($GLOBALS[$value] as $key =>$var) < if ($var === $GLOBALS[$key]) < unset($GLOBALS[$key]); >> > > > /** Main Call Function **/ function callHook() < global $url; $urlArray = array(); $urlArray = explode("/",$url); $controller = $urlArray[0]; array_shift($urlArray); $action = $urlArray[0]; array_shift($urlArray); $queryString = $urlArray; $controllerName = $controller; $controller = ucwords($controller); $model = rtrim($controller, 's'); $controller .= 'Controller'; $dispatch = new $controller($model,$controllerName,$action); if ((int)method_exists($controller, $action)) < call_user_func_array(array($dispatch,$action),$queryString); >else < /* Error Generation Code Here */ >> /** Autoload any classes that are required **/ function __autoload($className) < if (file_exists(ROOT . DS . 'library' . DS . strtolower($className) . '.class.php')) < require_once(ROOT . DS . 'library' . DS . strtolower($className) . '.class.php'); >else if (file_exists(ROOT . DS . 'application' . DS . 'controllers' . DS . strtolower($className) . '.php')) < require_once(ROOT . DS . 'application' . DS . 'controllers' . DS . strtolower($className) . '.php'); >else if (file_exists(ROOT . DS . 'application' . DS . 'models' . DS . strtolower($className) . '.php')) < require_once(ROOT . DS . 'application' . DS . 'models' . DS . strtolower($className) . '.php'); >else < /* Error Generation Code Here */ >> setReporting(); removeMagicQuotes(); unregisterGlobals(); callHook();

Let me explain the above code briefly. The setReporting() function helps us display errors only when the DEVELOPMENT_ENVIRONMENT is true. The next move is to remove global variables and magic quotes. Another function that we make use of is __autoload which helps us load our classes automagically. Finally, we execute the callHook() function which does the main processing.

Читайте также:  Php time свое время

First let me explain how each of our URLs will look – yoursite.com/controllerName/actionName/queryString

So callHook() basically takes the URL which we have received from index.php and separates it out as $controller, $action and the remaining as $queryString. $model is the singular version of $controller.

e.g. if our URL is todo.com/items/view/1/first-item, then
Controller is items
Model is item (corresponding mysql table)
View is delete
Action is delete
Query String is an array (1,first-item)

After the separation is done, it creates a new object of the class $controller.”Controller” and calls the method $action of the class.

Now let us create a few classes first namely our base Controller class which will be used as the base class for all our controllers, our Model class which will be used as base class for all our models.

First the controller.class.php

_controller = $controller; $this->_action = $action; $this->_model = $model; $this->$model =& new $model; $this->_template =& new Template($controller,$action); > function set($name,$value) < $this->_template->set($name,$value); > function __destruct() < $this->_template->render(); > >

The above class is used for all communication between the controller, the model and the view (template class). It creates an object for the model class and an object for template class. The object for model class has the same name as the model itself, so that we can call it something like $this->Item->selectAll(); from our controller.

While destroying the class we call the render() function which displays the view (template) file.

Now let us look at our model.class.php

connect(DB_HOST,DB_USER,DB_PASSWORD,DB_NAME); $this->_model = get_class($this); $this->_table = strtolower($this->_model)."s"; > function __destruct() < >>

The Model class extends the SQLQuery class which basically is an abstraction layer for the mySQL connectivity. Depending on your requirements you can specify any other DB connection class that you may require.

Now let us have a look at the SQLQuery.class.php

_dbHandle = @mysql_connect($address, $account, $pwd); if ($this->_dbHandle != 0) < if (mysql_select_db($name, $this->_dbHandle)) < return 1; >else < return 0; >> else < return 0; >> /** Disconnects from database **/ function disconnect() < if (@mysql_close($this->_dbHandle) != 0) < return 1; >else < return 0; >> function selectAll() < $query = 'select * from `'.$this->_table.'`'; return $this->query($query); > function select($id) < $query = 'select * from `'.$this->_table.'` where `id` = \''.mysql_real_escape_string($id).'\''; return $this->query($query, 1); > /** Custom SQL Query **/ function query($query, $singleResult = 0) < $this->_result = mysql_query($query, $this->_dbHandle); if (preg_match("/select/i",$query)) < $result = array(); $table = array(); $field = array(); $tempResults = array(); $numOfFields = mysql_num_fields($this->_result); for ($i = 0; $i < $numOfFields; ++$i) < array_push($table,mysql_field_table($this->_result, $i)); array_push($field,mysql_field_name($this->_result, $i)); > while ($row = mysql_fetch_row($this->_result)) < for ($i = 0;$i < $numOfFields; ++$i) < $table[$i] = trim(ucfirst($table[$i]),"s"); $tempResults[$table[$i]][$field[$i]] = $row[$i]; >if ($singleResult == 1) < mysql_free_result($this->_result); return $tempResults; > array_push($result,$tempResults); > mysql_free_result($this->_result); return($result); > > /** Get number of rows **/ function getNumRows() < return mysql_num_rows($this->_result); > /** Free resources allocated by a query **/ function freeResult() < mysql_free_result($this->_result); > /** Get error string **/ function getError() < return mysql_error($this->_dbHandle); > >

The SQLQuery.class.php is the heart of our framework. Why? Simply because it can really help us reduce our work while programming by creating an SQL abstraction layer. We will dive into an advanced version of SQLQuery.class.php in the next tutorial. For now lets just keep it simple.

Читайте также:  Mr. Camel

The connect() and disconnect() functions are fairly standard so I will not get into too much detail. Let me specifically talk about the query class. Line 48 first executes the query. Let me consider an example. Suppose our SQL Query is something like:

SELECT table1.field1 , table1.field2, table2.field3, table2.field4 FROM table1,table2 WHERE ….

Now what our script does is first find out all the output fields and their corresponding tables and place them in arrays – $field and $table at the same index value. For our above example, $table and $field will look like

$field = array(field1,field2,field3,field4);
$table = array(table1,table1,table2,table2);

The script then fetches all the rows, and converts the table to a Model name (i.e. removes the plural and capitalizes the first letter) and places it in our multi-dimensional array and returns the result. The result is of the form $var[‘modelName’][‘fieldName’]. This style of output makes it easy for us to include db elements in our views.

Now let us have a look at template.class.php

_controller = $controller; $this->_action = $action; > /** Set Variables **/ function set($name,$value) < $this->variables[$name] = $value; > /** Display Template **/ function render() < extract($this->variables); if (file_exists(ROOT . DS . 'application' . DS . 'views' . DS . $this->_controller . DS . 'header.php')) < include (ROOT . DS . 'application' . DS . 'views' . DS . $this->_controller . DS . 'header.php'); > else < include (ROOT . DS . 'application' . DS . 'views' . DS . 'header.php'); >include (ROOT . DS . 'application' . DS . 'views' . DS . $this->_controller . DS . $this->_action . '.php'); if (file_exists(ROOT . DS . 'application' . DS . 'views' . DS . $this->_controller . DS . 'footer.php')) < include (ROOT . DS . 'application' . DS . 'views' . DS . $this->_controller . DS . 'footer.php'); > else < include (ROOT . DS . 'application' . DS . 'views' . DS . 'footer.php'); >> >

The above code is pretty straight forward. Just one point- if it does not find header and footer in the view/controllerName folder then it goes for the global header and footer in the view folder.

Now all we have to add is a config.php in the config folder and we can begin creating our first model, view and controller!

 [/sourcecode] Write it is empty, but will have more information when we expand our framework in Part 2. Now create a file called itemscontroller.php in the controller folder [sourcecode language="php"] set('title',$name.' - My Todo List App'); $this->set('todo',$this->Item->select($id)); > function viewall() < $this->set('title','All Items - My Todo List App'); $this->set('todo',$this->Item->selectAll()); > function add() < $todo = $_POST['todo']; $this->set('title','Success - My Todo List App'); $this->set('todo',$this->Item->query('insert into items (item_name) values (\''.mysql_real_escape_string($todo).'\')')); > function delete($id = null) < $this->set('title','Success - My Todo List App'); $this->set('todo',$this->Item->query('delete from items where >Finally create a folder called items in the views folder and create the following files in it-

Источник

Читайте также:  Array pointers in php

Простой php фреймворк для начинающих

Представляю вашему вниманию супер простой php фреймворк. В нем только самое основное, что нужно для простых блогов.

Index.php /assets/ /framework/php/ /framework/html/ /framework/system/

Итак, поехали:
Начнем с парадной двери — index.php
Вообщем отказался напрочь от html кода в индексе, так как это неудобно.
Для head или footer решил сделать отдельные функции, так как для них писать одну универсальную функцию слишком запутано получиться. Зато теперь для каждого из них можно подключать дополнительно разные примочки типа галерею.

Название классов и функции говорят сами за себя, все просто, думаю даже начинающий поймет для чего они предназначены.

 class content < public function control() < $second = $GLOBALS['second']; $base = new base; $sth = mysql_query("SELECT * FROM page WHERE url='$second' "); while ($row = mysql_fetch_array($sth)) < $title = $row['title']; $html = $row['html']; >switch ($second) < case '': require_once('/framework/html/home.html'); break; case "news": require_once('/framework/html/news.html'); break; case "contacs": require_once('/framework/html/contacs.html'); break; default : require_once('/framework/html/single-page.html'); break; >> > // шаблонизатор class html < public function get_html($file_name) < require_once('/framework/html/'.$file_name.'.html'); >> // конец class database < public function connect() < $db = mysql_connect ("localhost","root",""); mysql_select_db ("test",$db); mysql_query("SET NAMES 'utf8'"); >> class control_url < public function only_second($class,$function) < if(!empty($GLOBALS['second']) and empty($GLOBALS['third'])) < $class = new $class; $class :: $function(); // вызов функций первого уровня >> public function only_third($class,$function) < if(!empty($GLOBALS['third'])) < //print 'second url only'; $class = new $class; $class :: $function(); // вызов функций второго уровня >> > class news < public function news_list() < $lang = stripcslashes($GLOBALS['lang']); $news = mysql_query("SELECT * FROM news "); while ($row = mysql_fetch_array($news)) < $title = $row['title']; $img = $row['img']; $html = $row['html']; $url = $row['id']; include('/framework/html/news_list.html'); >> public function single_news() < $id = $GLOBALS['third']; $news = mysql_query("SELECT * FROM news WHERE "); while ($row = mysql_fetch_array($news)) < $title = $row['title']; $img = $row['img']; $html = $row['html']; include('/framework/html/single-news.html'); >> > class base < public function breadcums() < >public function head() < $second = $GLOBALS['second']; $third = $GLOBALS['third']; $sth = mysql_query("SELECT * FROM page WHERE url='$second' "); while ($row = mysql_fetch_array($sth)) < $title = $row['title']; $description = $row['description']; $keywords = $row['keywords']; >require_once('/framework/html/head.html'); > public function footer() < $html = new html; $html :: get_html('footer',''); >public function header() < $html = new html; $html :: get_html('header',''); >public function nav() < $html = new html; $lang = stripcslashes($GLOBALS['lang']); require_once('/framework/html/nav_'.$lang.'.html'); >public function not_found($title) < if(empty($title)) < require_once('/framework/html/404.html'); >> > ?> 

А теперь сами HTML файлы: news_list.html

Как вы видите в html нет функций, только переменные. Вообще идея была по максимуму не использовать php код в html. И наоборот вообще не использовать html в php.

Данный код выводит новостные блоки в разделе новости, по нажатию перенаправляет на страницу для полного чтения.

А вот вывод статичных страниц:

Выводит такие страницы как: О нас, контакты. Но при желании тут можно подключить модули
просто вставив к примеру module :: galery(‘about’);

 

Итог: данный фреймворк выполняет по умолчанию самые основные функции для создания сайта визиток или блога. HTML и PHP практически полностью отделены друг от друга. Основным критерием была простота. Для того чтобы на нем работали несколько сложных приложений надо сделать базовые функции более универсальными.

Источник

Оцените статью