Web application programming in PHP for beginners developpers
The first thing to get into before to get into programming php is to have a good grasp about the HTTP protocol basics, because php scripts are executed over http protocol, and most of the data coming in and out of the php script will be through the HTTP protocol.
I explain the basics of HTTP protocol and execution of php script, and how data from HTTP request and URL are used in PHP in this post :
There are 3 kinds of tasks a php or cgi script can do typically to answer an http request
- Storing some input data on the server, potentially associated with a session information.
- Generating html content to display some data or file stored on the server.
- Outputing formated data to answer request from other applications.
Most web application when it come to website have to do all these three things, storing informations coming from a form in page in the browser and generating pages to display this content.
Eventually it can have functions to output stored data for dynamic request inside of the web page or mobile applications.
The first thing one has to think about when designing a web application, is which part of the content is going to be static and which part is dynamic.
- Static content is content that is not changing over the time, not depending on execution context, neither on data stored on the server neither on user input.
- Dynamic content is content that is not known when the application is built, either coming from user input, or generated based on data stored on the server.
Dynamic content take more computing resource to generate on the server, as it will need some program to fetch the data from the server storage, and formatting it for display or output, whereas static content can be sent directly as it is stored on the server, and will require less resource.
MVC Model
Most web applications will follow the so called MVC model, which stand for Model-View-Controller.
- Model is the part of the code that is concerned with storing and fetching dynamic data stored on the server.
- View is the part that is concerned with displaying content and pages based on this data.
- Controller is the part that process the server and request variables and select the model and view to generate the page from the request.
Controller
The controller is mostly the entry point of the application.
It will be concerned by reading and parsing information from the HTTP request filled by the HTTP server, and making basic processing of this request, such as fetching variables from the query string and http headers, and then calling the internal functions to display the page with the requested data.
The request generally contain session informations to identify the user, and also the information to know which page is being requested, and an identifier for the content such as the particular article or video that has to be displayed on the page.
Views
Views are also often called templates, and often come under the form of markup language such as HTML, with some additional markup to tell the parser where to insert the dynamic content stored on the server.
HTML is used to display this content as web page, it stand for Hyper Text Markup Language, and is close to the synthax of XML standard, both being based on 'markups' or tags, which is a way to structure a document with a list of containers embeded in each others, who can either contain some data, or other so called 'children tags' inside of it, as well as attributes.
A tag is a container associated with a name and attributes to give informations about how to interpret or display the data or children that it contain.
A tag is opened with a markup such < tag > and closed by a markup such as < /tag > .
Attributes are added in the opening tag such < tag attribute="value" > Tag Content < /tag > .
Most HTML tags will contain a style or class attribute, defining the custom style such as size, color, font, or alignment, that the browser will use to display the content or child elements.
Eventually when tags doesn't contain any children or content, the synthax of 'self enclosing tags' can be used, such as < tag/ > .
The additional '/' before the ' > ' in the tag opener mean the tag is self enclosed, and doesn't need a closing tag.
Everything between the tag opener and closer is the content or children elements contained into the tag.
With HTML, all tags are contained into a parent tag and will inherit its style and position, and all HTML documents will contain a single root element, that is used to identify the type of the document.
In case of an HTML page, the global structure will be as follow
< html > tag must appear at the begining of the document, and is the root element identifying the document as an html page. It contain all the other tags, and is closed at the end of the document.
< head > tag contains children elements defining meta information about the page. These elements are not displayed on the page, but contain tags that will be used either by search engine indexing, or by the client browser, to get information about the page such as the title, language, character set, assets such as stylesheets to be included in the page, or other display information.
< body > tag will contain the element that are being displayed into the page, as the actual content of the document.
The basic structure of an html page comes as follow
< html >
< head > < title > My page title< /title > < /head >
< body > Content of the page< /body >
< /html >
A web application will most likely have to generate several different pages, that correspond to different functions or parts of the site, but all pages will be following this same structure.
HTML can also contain formulars, which are used to group some input field together, and send the data from those field to the server.
A < form > tag will be used to identify a formular, and will generally contain a number of children < input > tags for different fields, and a submit button to send the data to the specified script.
Following attributes will generally be used in the form tag.
- Method attribute specify the type of http request used to submit the data (mostly GET or POST).
- Action attribute specify the URL of the script that will received the information.
a typical HTML form looks like this
< form action="myscript.php" method="get" >
< input type="text" name="textInput" value="value" > < /input >
< input type="submit" name="submit" value="Send" > < /input >
< /form >
The second input is a submit button, and will submit the data to the script when pressed.
In this example, it will make the browser go to the URL myscript.php?textInput=value.
myscript.php is specified in the action attribute of the form, and as the method used is GET, the variables are sent via the URL query string.
When values are sent using the GET method, the values will be 'url encoded' as urls can only contain certain characters, and the other characters will be replaced by something like %CHARCODE. For example a space will be encoded as %20 or '+'.
All the < input > tags contained in a form must contain the attribute 'name' which will specify the name of the variable associated with the value entered in the field.
Model
Models are used to represent a particular type of dynamic data that the application will have to store or display.
The model will generally use a database engine in the backend, and will represent all the different types of content the server can store and manipulate, such as user accounts, articles or categories, and will have different functions to fetch or store those objects to fullfill the different requests sent to the application.
The controller will generally match a particular model with a certain set of views or layout, in order to display a particular kind of data in the page as queried by the HTTP request.
PHP program structure
As seen before, the application will generally be broken down into different parts, which will be reflected by being contained in different php files on the server.
Each inlcuded files will contain a number of functions, that will be called by the controller with the variables parsed from the HTTP request.
Functions
Functions are declared with a name followed by parenthesis containing a coma separated list of variable that will be set by the caller, and the code to be executed when the functions is called is enclosed between {} brackets.
Additionally, they can return a value, that can be assigned to a variable by the caller code.
function my_function($parameter1,$parameter2)
{
echo "function my_function called with parameter1 = $parameter1 and parameter2 = $parameter2";
return TRUE;
}
$ret = my_function('value1','value2');
Will print
function my_function called with parameter1 = value1 and parameter2 = value2
Echo is used to output a text to the browser.
The variable $ret will be set to the return value, TRUE.
When using double quotes "", variables can be included into the text, they are marked by starting with a $, as well as escaped character, marked with a '\x'. Escape character can be used to quote non printable character, or a double quote. "\n" mean line break.
When using single quotes '', the quoted text will be interpreted litterally, and php will not parse any variable or characters inside of them.
A '.' between two variables or text values will concatenate the operand to the source.
Arrays
Data is often represented under the form of arrays, which is a single variable who can contain different values indexed with a number or a key.
The php built in function explode allow to transform a serie of values seperated by a character called the 'separator' into such array.
For example, given a text like
$text = 'value1,value2,value3';
The php function explode can turn this text into a php array.
$array = explode(',', $text);
$array[0] equal value1
$array[1] equal value2
$array[2] equal value3
The php foreach loop then allow to execute a block of code for each elements of the array.
foreach ($array as $key)
{
echo $key.'< br > ';
}
Will execute echo $key.'< br > ' for each element of the array, $key being set to the value of the current element in the array.
The main entry point of the program will generally be the file index.php, as it's the file that is loaded by default by most HTTP server when no file is specified in the URL.
This index.php file will then include the different controllers, models and views composing the application.
For simple application, the index.php can eventually be used as the controller, and only include the different views and models.
If the application is relatively small, all the controllers views and models can be included by 'hardcoding' the include into the code.
If the application become bigger, and contain lot of different views or models, or plugins that can be added dynamically, the different files can be included 'on demand' depending on the request.
Simple user account example
In this example i will make sample program with pages to register new accounts and login the user by opening a session, and then a page to display the account when signed in.
The main index.php file can be used as the controller for this example, and will include the model to manipulate users and sessions, as well as the view to display them.
The controller is responsible for parsing the data from the HTTP request, and calling those functions with the data from the URL or contained in the request.
Model for user accounts
The first file will be the model to manipulate user account.
It contain a number of different functions to manipulate the account data .
The list of registered users will be stored as a csv file, which stand for comma separated value, each users will be inserted as a new line in the file, each line representing the users as its name and hashed password separated by coma such as
username1,password hash
username2,password hash
username3,password hash
The function 'find_account' will be used to check if a given user is registered.
The function 'register_account' will be used to insert a new user into the file.
The function 'check_user_login' will be used to check if the given username and password match a registered user.
The function 'get_user_list' will be used to fetch an array containing username of all registered users.
model_users.php
<?phpfunction find_account ($username)
{
// check if the file containing the list of users exists
if(!is_file('data/users.csv'))return FALSE;
// read the whole csv file
$user_data = file_get_contents('data/users.csv');
// break the csv file's lines into an array containing each user
$users = explode("\n",$user_data);
foreach($users as $user)
{
// break each lines into an array containing the username and password
$user_infos= explode( ',' , $user);
// return TRUE if the username match the request
if($user_infos[0] == $username) return TRUE;
}
return FALSE; // return FALSE if no user match the request.
}
function register_account($username,$password)
{
// check if input username is longer than 3 character
if(strlen($username)<3)return FALSE;
// check if the input username already exists, and return FALSE if it's already registered.
if(find_account($username) == TRUE)return FALSE;
// create the new line to insert into the file, containing the username and password hash separated by a coma.
$newline = $username. ',' . md5($password);
// add the new user line into the file
file_put_contents ('data/users.csv', $newline ."\n", FILE_APPEND);
// return TRUE
return TRUE;
}
function check_user_login($username,$password)
{
// Compute the hash of the input password
$pw_hash = md5($password);
// Check if the file containing the list of users exists
if(!is_file('data/users.csv'))return FALSE;
// Read the whole csv file
$user_data = file_get_contents('data/users.csv');
// break the csv file's lines into an array containing each user
$users = explode("\n",$user_data);
foreach($users as $user)
{
// break each lines into an array containing the username and password
$user_infos= explode( ',' , $user);
// return TRUE if the username and password match the request
if(($user_infos[0] == $username) && ($user_infos[1] == $pw_hash)) return TRUE;
}
// return FALSE if no user match the request.
return FALSE;
}
function get_user_list()
{
$user_list = array();
if(!is_file('data/users.csv'))return $user_list;
$user_data = file_get_contents('data/users.csv');
$users = explode("\n",$user_data);
foreach($users as $user)
{
// break each lines into an array containing the username and password
$user_infos= explode( ',' , $user);
array_push($user_list, $user_infos[0]);
}
return $user_list;
}
?>
Model for user sessions
The second file will be the model to manipulate user sessions.
Each active sessions will be stored as a csv file with a unique generated name based on a session id, and this session id will be sent to the client as a cookie.,
Each file represent the session as the username and the IP address used to login such as
username1,IP address
The function 'clear_session' will be used to clear a given session.
The function 'delete_old_sessions' will be used to remove sessions older than the number of second specified.
The function 'check_session' will be used to retreive the username if the given session id and ip address match an active session.
The function new_session_id is used to generate a new unique session file.
The function new_session is used to create a new session from a given username and IP address.
model_sessions.php
<?phpfunction clear_session($sess_id)
{
// remove the file associated with the input session id
unlink('data/sess_'.$sess_id);
}
function delete_old_sessions($secs)
{
// get current time
$now = time();
/* List all active session files */
foreach(glob("data/sess_*") as $f) {
/* Remove files older than the given number of seconds */
if (is_file($f) && ($now - filemtime($f) > $secs)) unlink($f);
}
}
function check_session($sess_id,$address)
{
// Check if the given session id exists
if(!is_file('data/sess_'.$sess_id))return FALSE;
// Read all the sessions informations from the file
$session_data = file_get_contents('data/sess_'.$sess_id);
// Break the session file's informations into an array
$session_infos = explode(',', $session_data);
// Return the username from the session information if the IP address match the input.
if($session_infos[1]==$address)
return $session_infos[0];
// Return false if the IP doesn't match
return FALSE;
}
function new_session_id()
{
// Create a random unique ID of 10 hexadecimal characters.
while(is_file($sess_file = 'data/sess_'.bin2hex(random_bytes(5)))){}
return $sess_file;
}
function new_session($username,$address)
{
$sess_file=new_session_id();
// Insert the username and IP address in the new session file
file_put_contents( $sess_file, $username.','.$address);
// return the new session id. ( Everything after the 10th character of the session file name );
return substr($sess_file,10);
}
?>
User account view
In order to display the pages for users, the controller will use a view to generate the HTML for a formular in order to enter the information for new users or to login new users, and display informations of signed in users .
In professional website, an CMS or toolkit will be used which contain a template system such as smarty, but in this example it will generate the html manually in order to keep the code simple and barebone.
generate_register_view will generate the HTML form to register a new user.
generate_login_view will generate the HTML form to login a registered user.
generate_account_view will generate the HTML page to view a registered user information.
generate_user_list_view will generate the HTML page to view the list of registered users.
view_users.php
<?phpfunction generate_register_view()
{
$html = '< strong > Register new user ( or < a href="index.php?page=login" > login< /a > )< /strong > ';
$html .= '< form action="index.php" method="GET" > ';
$html .= '< label > username< /label > < input type="text" name="username" / > < br/ > (3 char min and cannot contain a coma)';
$html .= '< label > password< /label > < input type="password" name="password" / > < br/ > ';
$html .= '< input type="hidden" name="action" value="register" / > ';
$html .= '< input type="submit" value="register" / > < br/ > ';
$html .= '< /form > ';
return $html;
}
function generate_login_view()
{
$html = '< strong > Signin registered user ( or < a href="index.php?page=register" > register< /a > )< /strong > ';
$html .= '< form action="index.php" method="GET" > ';
$html .= '< label > username< /label > :< input type="text" name="username" / > < br/ > ';
$html .= '< label > password< /label > :< input type="password" name="password" / > < br/ > ';
$html .= '< input type="hidden" name="action" value="login" / > ';
$html .= '< input type="submit" value="login" / > < br/ > ';
$html .= '< /form > ';
return $html;
}
function generate_account_view($user)
{
$html = 'logged in as '.$user.' < a href="index.php?action=logout" > logout< /a > ';
return $html;
}
function generate_user_list_view($user_list)
{
$html = '< strong > user list < /strong > < hr/ > ';
foreach($user_list as $user)
{
$html .= "$user < br/ > ";
}
return $html;
}
?>
Main index file
The main index.php is used as the controller, it's used to parse the variables from the HTTP request, execute the function specified in the request, and generate the page title and body depending on the request.
index.php
<?php
// Include the two models and the view
include "model_users.php";
include "model_sessions.php";
include "view_users.php";
// Check if the data directory exists, and create it if it doesn't exists.
if(!is_dir('data'))
{
if(mkdir('data') !== TRUE )
{
echo 'unable to create data directory';
exit(0);
}
}
// Fetch the variables from HTTP request. The @ mean silent errors.
@$action = $_GET["action"];
@$message = $_GET["message"];
@$sess_id = $_COOKIE['session_cookie'];
// Remove session older than 3600 secs
delete_old_sessions(3600);
//Check if a session already exists from the cookie value and client IP address
$user_session = check_session($sess_id,$_SERVER['REMOTE_ADDR']);
if($user_session == FALSE) unset($sess_id);
// Get username and password from url query string, and register the new account. Then redirect with a message depending on the result
function register()
{
$username = urldecode($_GET["username"]);
$password = urldecode($_GET["password"]);
// remove invalid line breaks from username
$username = str_replace(array("\r\n", "\n", "\r"),'',$username);
//check username doesn't contain a coma, and try registering the new user
if( (strpos($username, ',') === FALSE)&&
(register_account($username,$password) == TRUE))
{
$msg=urlencode('new user registered');
header('location:index.php?page=login&message='.$msg);
}
else
{
$msg=urlencode('error registering new user');
header('location:index.php?page=register&message='.$msg);
}
}
// Login a registered user from the username and password input from the query string. Then redirect with a message depending on the result.
function login()
{
$username = urldecode($_GET["username"]);
$password = urldecode($_GET["password"]);
$address = $_SERVER['REMOTE_ADDR'];
if(check_user_login($username,$password) == TRUE)
{
$sess_id = new_session($username,$address);
setcookie('session_cookie',$sess_id);
header('location:index.php?page=account');
}
else
{
$msg=urlencode('error login user');
header('location:index.php?page=login&message='.$msg);
}
}
// Remove the active session and the cookie, then redirect to the login page.
function logout()
{
clear_session($sess_id);
setcookie('session_cookie','',-1);
header('location:index.php?page=login');
}
// Execute the action specified in the url, by calling the selected function and exit.
switch($action)
{
case 'register': register(); exit(0); break;
case 'login': login(); exit(0); break;
case 'logout': logout(); exit(0); break;
}
// If no action in the url, generate the page title and content depending on the page specified in the URL query string.
$page = $_GET["page"];
switch($page)
{
default:
$page = 'register';
case 'register' :
$page_title = "Register new user";
$page_body = generate_register_view();
break;
case 'login' :
$page_title = "Sign new user";
$page_body = generate_login_view();
break;
case 'usrlst' :
$page_title = "List of registered user";
$user_list = get_user_list();
$page_body = generate_user_list_view($user_list);
break;
case 'account' :
if(empty($sess_id))
{
header('location:index.php?page=login');
exit(0);
}
$page_title = "my account";
$page_body = generate_account_view($user_session);
break;
}
?>
// Output the HTML.
< html >
< head >
< title > < ?php echo $page_title ? > < /title >
< /head >
< body >
< div style="text-align:center;width:100%" > Welcome on the php example #1< /div >
< hr/ >
< div >
< a href="index.php?page=usrlst" > user list< /a >
< ?php if(empty($sess_id)) { ?>
< a > href="index.php?page=login" > login< /a >
< ?php } else { ?>
< a href="index.php?page=account" > account < /a >
< a href="index.php?action=logout" > logout < /a >
< ?php } ?>
< /div >
< div > < ?php echo $message; ? > < /div >
< ?php echo $page_body; ? >
< /body >
< /html >
The demo application is testable on http://nodix.eu/phpTutos/1 and the file downloadable as zip at http://nodix.eu/phpTutos/1.zip.
The code is kept simple on purpose for the sake of explaining the basic barebone, and would need more checking and better structure for production site.
It uses simple file storing instead of database and more advanced functions. I will get into database and class in another tutorial.
In next tutorial, i will get into client side javascript programming, to insert dynamic content in the web page, and basics of CSS stylesheet.