PHP 5 Object + TinyButStrong
How to create a structured website

Welcome to my tutorial !
Hope you will appreciate it. If so please consider a donation: Thanks !
My tutorial explains how to deal with objects in PHP 5 with the powerful TinyButStrong template engine.
It is divided in several parts which are connected to one another. You can directly go to the part of your choice if you feel comfortable with the basics, but the tutorial is made in such way that things are progressively added.
It is highly recommended to follow the links if you are not quite sure about the way a functionalitie works...
I also recommend to learn the basics of TBS first, but it should be ok if you are a versatile developer.

Who am I : My name is TiTi, I'm a 22 years old french guy.
The creation of this tutorial began during the 2008 summer.
If you want to add something, report an error or a missing point, please contact me

Creative Commons License Content on this tutorial is licensed under a Creative Commons Attribution 3.0 License

What are we going to talk about ?

Introduction

Why should you use TinyButStrong: Why you should use PHP Object: Before starting, some recommended links: For any help, you can:

PART 1: A simple PHP 5 class

Let's begin with a very basic example, a simple class with only 3 members and the corresponding TBS template.
The PHP code could be devided in 3 parts: class definition, class instanciation/affectation, TBS merging.

 PHP: demo1.php 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

class student
{
    public 
$StudentId;
    public 
$StudentName;
    public 
$StudentAge;
}

$guy = new student();
$guy->StudentId 5;
$guy->StudentName 'WOOD';
$guy->StudentAge 22;

include_once(
'includes/tbs_class_php5.php');
$TBS = new clsTinyButStrong;
$TBS->LoadTemplate('demo1.html');
$TBS->Show();

?>
 HTML: demo1.html 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Demo 1</title>
</head>
<body>

<b>ID :</b> [var.guy.StudentId]<br />
<b>NAME :</b> [var.guy.StudentName]<br />
<b>AGE :</b> [var.guy.StudentAge]

</body>
</html>

Result:
As you can see, I've defined a class with 3 properties : Id, Name and Age. Then I instanciate this class with the $guy variable. Between lines 10 and 13 I affect values to my object. The final part is the TinyButStrong merging : inclusion of the TBS class, instanciation, template loading and finally the call to the Show() method.
On the template side (html), I'm using the TBS syntax to define what data I want to merged. In this demo I want to see the 3 properties of my guy variable, that i'm accessing with var.guy

Ok great we've got a very simple example with a class (student), an instance of this class ($guy), and that's it ! Nothing complicated here, let's learn more in the second part !

PART 2: PHP 5 Object + MySQL

You can now imagine that every field of my $guy is not directly coming from the php source, but from a database... For instance, you could just call a method that takes care of the SQL stuff and call `$guy->loadStudent(5);` in the php file. We will see an example of that in this second part of the tutorial.

First thing to do is to add a table in your MySQL database and insert a record in it: everything you need is commented in the demo2.php script. Then we have to connect to the database in our script. To do such a thing, let's have a look at connect_bdd.php (I put it into the 'includes' folder on my system):

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$db_host 
'localhost'// host
$db_base 'mydatabase'// base  
$db_user 'myuser'// user
$db_pass 'mypassword'// password

$db_link mysql_connect($db_host$db_user$db_pass);
mysql_select_db($db_base$db_link);
mysql_query("SET NAMES 'utf8'"$db_link);
//---------
//Then we erase everything to be sure we can't access this data from TBS with `[var.db_pass]` for example
$db_host $db_base $db_user $db_pass $db_link NULL;
?>
IMPORTANT: You shouldn't use this file because it doesn't test if the connection is successful or not. We will see how to do that further with the use of Exceptions.


Ok so we know how to connect to the MySQL database, then how to make a request and get a record in the student class ?

 PHP: demo2.php 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php

include_once('includes/connect_bdd.php'); // database connection

/*

CREATE TABLE IF NOT EXISTS `Students` (
  `StudentId` int(11) NOT NULL auto_increment,
  `StudentName` varchar(50) NOT NULL,
  `StudentAge` varchar(50) NOT NULL,
  PRIMARY KEY  (`StudentId`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

INSERT INTO `Students` (`StudentId`, `StudentName`, `StudentAge`) VALUES (1, 'DUPONT', 22);

*/

// a little function to help protecting sql requests
function quote_smart($value)
{
    if(
get_magic_quotes_gpc())
        
$value stripslashes($value);
    
    if(!
is_numeric($value))
        
$value mysql_real_escape_string($value);
    
    return 
$value;
}

class 
student
{
    public 
$StudentId;
    public 
$StudentName;
    public 
$StudentAge;
    
    public function 
loadStudent($IdE)
    {
        
$IdE quote_smart($IdE);
        
$sql "
        SELECT * FROM Students
        WHERE StudentId={$IdE};
        "
;
        
$res mysql_query($sql);
        if(
$row mysql_fetch_assoc($res))
        {
            
$this->StudentId $IdE// this id is correct (we're into the if), we can set the value
            
$this->StudentName $row['StudentName'];
            
$this->StudentAge $row['StudentAge'];
        }
        else
            die(
'Student not found !');
    }
}

//-------------------

$guy = new student();
$guy->loadStudent(1);

// that's it, no more job :-)

include_once('includes/tbs_class_php5.php');
$TBS = new clsTinyButStrong;
$TBS->LoadTemplate('demo2.html');
$TBS->Show();

?>
 HTML: demo2.html 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Demo 2</title>
</head>
<body>

<b>ID :</b> [var.guy.StudentId]<br />
<b>NAME :</b> [var.guy.StudentName]<br />
<b>AGE :</b> [var.guy.StudentAge]

</body>
</html>

Result:
Modifications from demo1:
Woa look at that ! Once the class is done: only 2 lines of code in the php file !
You can get the information of every student in the database simply by changing the parameter :)
$guy = new student();
$guy->loadStudent(1);


Of course you can still do SQL requests directly with TBS using MergeBlock, but I prefer to put the PHP Object `layer` between the database and the presentation layer, in order to keep absolute control of my data in my own classes. Your project will be way better segmented with this approch.
It's not obvious with a simple class example, but with a lot of classes, I recommand to deal with SQL commands only into classes, not anymore in the code that use the classes.


PART 3: Getters and Setters

I'm now considering you aldready now what is the OOP programming technique, if not please read some articles, for example the wikipedia article on object-oriented programming

Now I'm gonna talk about the visibility issue with class members: public or private/protected ?
If you still don't know what that means, check the php doc about OOP members visibility
By now you should agree and understand why it is better to use private properties. But how can we use them with TBS ?

Ok let's change 'public' by 'private' in the class:
class student
{
    private $StudentId;
    private $StudentName;
    private $StudentAge;
}

Now we have 2 problems:
To solve these 2 problems, you have to use accessors:

 PHP: demo3.php 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<?php

include_once('includes/connect_bdd.php'); // database connection

/*

CREATE TABLE IF NOT EXISTS `Students` (
  `StudentId` int(11) NOT NULL auto_increment,
  `StudentName` varchar(50) NOT NULL,
  `StudentAge` varchar(50) NOT NULL,
  PRIMARY KEY  (`StudentId`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

INSERT INTO `Students` (`StudentId`, `StudentName`, `StudentAge`) VALUES (1, 'DUPONT', 22);

*/

// a little function to help protecting sql requests
function quote_smart($value)
{
    if(
get_magic_quotes_gpc())
        
$value stripslashes($value);
    
    if(!
is_numeric($value))
        
$value mysql_real_escape_string($value);
    
    return 
$value;
}

class 
student
{
    private 
$StudentId;
    private 
$StudentName;
    private 
$StudentAge;
    
    public function 
loadStudent($IdE)
    {
        
$IdE quote_smart($IdE);
        
$sql "
        SELECT * FROM Students
        WHERE StudentId={$IdE};
        "
;
        
$res mysql_query($sql);
        if(
$row mysql_fetch_assoc($res))
        {
            
$this->StudentId $IdE// this id is correct (we're into the if), we can set the value
            
$this->StudentName $row['StudentName'];
            
$this->StudentAge $row['StudentAge'];
        }
        else
            die(
'Student not found !');
    }
    
    public function 
getStudentId()
    {
        return 
$this->StudentId;
    }
    public function 
getStudentName()
    {
        return 
$this->StudentName;
    }
    public function 
getStudentAge()
    {
        return 
$this->StudentAge;
    }
    
    public function 
setStudentName($newname)
    {
        
$this->StudentName $newname;
    }
    
    public function 
getAge($year)
    {
        
$actualYear intval(date('Y'));
        return 
$this->StudentAge + ($year $actualYear);
    }
}

//-------------------

$guy = new student();
$guy->loadStudent(1);
$guy->setStudentName('MARTIN');

// that's it, no more job :-)

include_once('includes/tbs_class_php5.php');
$TBS = new clsTinyButStrong;
$TBS->LoadTemplate('demo3.html');
$TBS->Show();

?>
 HTML: demo3.html 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Demo 3</title>
</head>
<body>

<b>ID :</b> [var.guy.getStudentId]<br />
<b>NAME :</b> [var.guy.getStudentName]<br />
<b>AGE :</b> [var.guy.getStudentAge]<br />
<b>Age in 2015 :</b> [var.guy.getAge(2015)]

</body>
</html>

Result:
Modifications from demo2:
Please note that you dont have to use parentheses in the TBS language, unless you use parameters.

Sometimes I ask myself why I should use accessors rather than public members, the main reason is that you cannot modify an object properties from outside the object methods (which force you to be really sure about what you're doing), but there is more: As a rule of thumb, you have to use accessors and put your data into private members.


PART 4: 3 magic words

Part 3 was great nope?
But now I'm going to describe another problem with php: Overloading

The problem with overloading is that you can add another member to your class simply by trying to access it, example:
<?php

class student
{
    private 
$StudentId;
    private 
$StudentName;
    private 
$StudentAge;
}
$guy = new student();
$guy->nickname 'SuperGuy'// oO what is nickname ?
print_r($guy); // let's see what's in $guy

?>
Result of the print_r:
student Object
(
    [StudentId:private] => 1
    [StudentName:private] => DUPONT
    [StudentAge:private] => 22
    [nickname] => SuperGuy
)

The nickname property doesn't exist in the class, but.... ouch ! arf... the property as been added:-(

As you can guess, it is simple to make the mistake in you code and add a new field to your object rather than modify one. grr!

Hopefully, I've got the solution to resolve this problem :-)
What's very cool is this solution also simplify getters and setters: you only have to write 1 getter and 1 setter for your class.
Just three magic words: __get ; __set ; property_exists

 PHP: demo4.php 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<?php

include_once('includes/connect_bdd.php'); // database connection

/*

CREATE TABLE IF NOT EXISTS `Students` (
  `StudentId` int(11) NOT NULL auto_increment,
  `StudentName` varchar(50) NOT NULL,
  `StudentAge` varchar(50) NOT NULL,
  PRIMARY KEY  (`StudentId`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

INSERT INTO `Students` (`StudentId`, `StudentName`, `StudentAge`) VALUES (1, 'DUPONT', 22);

*/

// a little function to help protecting sql requests
function quote_smart($value)
{
    if(
get_magic_quotes_gpc())
        
$value stripslashes($value);
    
    if(!
is_numeric($value))
        
$value mysql_real_escape_string($value);
    
    return 
$value;
}

class 
student
{
    private 
$StudentId;
    private 
$StudentName;
    private 
$StudentAge;
    
    public function 
loadStudent($IdE)
    {
        
$IdE quote_smart($IdE);
        
$sql "
        SELECT * FROM Students
        WHERE StudentId={$IdE};
        "
;
        
$res mysql_query($sql);
        if(
$row mysql_fetch_assoc($res))
        {
            
$this->StudentId $IdE// this id is correct (we're into the if), we can set the value
            
$this->StudentName $row['StudentName'];
            
$this->StudentAge $row['StudentAge'];
        }
        else
            die(
'Student not found !');
    }
    
    public function 
__get($attribute)
    {
        if(!
property_exists(get_class($this), $attribute))
            die(
'Wrong student property : '.$attribute);
        
        return 
$this->$attribute;
    }
    
    public function 
__set($attribute$value)
    {
        if(!
property_exists(get_class($this), $attribute))
            die(
'Wrong student property : '.$attribute);
        
        
// others verifications
        
if($attribute == 'StudentId')
            die(
'Sorry but StudentId can\'t be updated');
        if((
$attribute == 'StudentAge' ||  $attribute == 'StudentId') && !is_numeric($value))
            die(
'Invalid data : need a numeric value for '.$attribute);
        
        
$this->$attribute $value;
    }
    
    public function 
getAge($year)
    {
        
$actualYear intval(date('Y'));
        return 
$this->StudentAge + ($year $actualYear);
    }
}

//-------------------

$guy = new student();
$guy->loadStudent(1);

// Again possible like in demo1 : direct access to members even if they are private
$guy->StudentName 'MARTIN';
$guy->StudentAge 24;
//$guy->StudentId = 666; // you better not do that => die('Sorry but StudentId can\'t be updated');

// that's it, no more job :-)

include_once('includes/tbs_class_php5.php');
$TBS = new clsTinyButStrong;
$TBS->LoadTemplate('demo4.html');
$TBS->Show();

?>
 HTML: demo4.html 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Demo 4</title>
</head>
<body>

<b>ID :</b> [var.guy.__get(StudentId)]<br />
<b>NAME :</b> [var.guy.__get(StudentName)]<br />
<b>AGE :</b> [var.guy.__get(StudentAge)]<br />
<b>Age in 2015 :</b> [var.guy.getAge(2015)]

</body>
</html>

Result:
Please note that you can now directly use `$guy->StudentAge = 'Martin';` even if the property is private ! The __get method will be called :)
Unfortunately this doesn't work the same with TBS template => you have to do [var.guy.__get(StudentName)]
It is still better than [var.guy.getStudentName] because you don't have to name a function :)

Because we use `property_exists`, an error will raise if you're trying to access (get or set) an unknow field, no mistake anymore !
Now it is up to you to do others verifications in this method: for example control the age value your are trying to set (can't be > 0 ...).
I included some others verifications in the setter to show an example.


PART 5: Exceptions + TBS

Ok we're almost done, what can we do better ? It's simple: Error handling.
Do you really check if every function that is called return a correct value, and not an error ?
If so that's great ! But your program must be very huge, with a lot of brackets...

Do you also remember the end of the `loadStudent` function ? Just look at this: `die('Student not found !');` !
Ok imagine that the SELECT procedure return 0 rows, for whatever reason => your website is going to look ugly !
The same will happen everytime you use `die` in your code !

So what can you do to correctly check errors and still don't break your site interface ? You could use Exceptions !
What are exceptions ?
We are going to discover an example using the following exception class I made for you:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?php

/*

CREATE TABLE IF NOT EXISTS `_Exceptions` (
  `IdException` int(11) NOT NULL auto_increment,
  `Instant` datetime NOT NULL,
  `Message` varchar(200) NOT NULL,
  `Line` int(11) NOT NULL,
  `File` varchar(200) NOT NULL,
  `SQL` text,
  `Trace` text NOT NULL,
  `Details` text NOT NULL,
  PRIMARY KEY  (`IdException`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

*/

class myException extends Exception
{
    private 
$sql;
    private 
$instant;
    
    public function 
__construct($msg$requete NULL)
    {
        
parent::__construct($msg); // don't forget to call parent constructor
        
$this->sql $requete;
        
$this->instant time();
    }
    
    public function 
getError()
    {
        
$return  'An exception has been generated :<br />';
        
$return .= '<b>Date</b> : '.date('d/m/Y'$this->instant).' at '.date('H:i:s'$this->instant).'<br />';
        
$return .= '<b>Message</b> : '.$this->message.'<br />';
        
$return .= '<b>Line</b> : '.$this->line.'<br />';
        
$return .= '<b>File</b> : '.$this->file.'<br />';
        if(!
is_null($this->sql))
            
$return .= '<b>SQL</b> : '.$this->sql.'<br />';
        
$return .= '<b>Trace</b> : <br />'.nl2br($this->getTraceAsString()).'<br />';
        
$return .= '<b>Details</b> : <br />'.nl2br(str_replace('  ''.'print_r($this->getTrace(), true))).'<br />';
        
        return 
$return;
    }
    
    public function 
sendError()
    {
        
$shipperName 'EXCEPTION LOG';
        
$shipperMail 'exception@email.com';
        
$headers ='From: "'.$shipperName.'"<'.$shipperMail.'>'."\n";
        
$headers .='Content-Type: text/html; charset="iso-8859-1"'."\n";
        
        
$recipient 'my@email.com';
        
$subject 'My PHP application : an exception has been generated';
        
$message ='<html><body>'.$this->getError().'</body></html>';
        
        
mail($recipient$subject$message$headers);
    }
    
    public function 
saveError()
    {
        
$timestamp quote_smart(date('Y-m-d H:i:s'$this->instant));
        
$message quote_smart($this->message);
        
$line quote_smart($this->line);
        
$file quote_smart($this->file);
        
$trace quote_smart($this->getTraceAsString());
        
$details quote_smart(print_r($this->getTrace(), true));
        
        if(
is_null($this->sql))
            
$sql 'NULL';
        else
            
$sql "'".quote_smart($this->sql)."'";
        
        
$request "INSERT INTO _Exceptions VALUES(
        NULL,
        '{$timestamp}',
        '{$message}',
        {$line},
        '{$file}',
        {$sql},
        '{$trace}',
        '{$details}');
        "
;
        if(!
mysql_query($request))
            throw new 
Exception('Insertion error for an exception : '.$request); // Exception, not myException, else recursivity is possible...
    
}
}

?>


Please note I've added a new table in the following php:


 PHP: demo5.php 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<?php

include_once('includes/connect_bdd.php'); // database connection
include_once('includes/class_myException.php'); // personnal exception class

/*

CREATE TABLE IF NOT EXISTS `Students` (
  `StudentId` int(11) NOT NULL auto_increment,
  `StudentName` varchar(50) NOT NULL,
  `StudentAge` varchar(50) NOT NULL,
  PRIMARY KEY  (`StudentId`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

INSERT INTO `Students` (`StudentId`, `StudentName`, `StudentAge`) VALUES (1, 'DUPONT', 22);

*/

// a little function to help protecting sql requests
function quote_smart($value)
{
    if(
get_magic_quotes_gpc())
        
$value stripslashes($value);
    
    if(!
is_numeric($value))
        
$value mysql_real_escape_string($value);
    
    return 
$value;
}


//-------------------


class student
{
    private 
$StudentId;
    private 
$StudentName;
    private 
$StudentAge;
    
    public function 
loadStudent($IdE)
    {
        
$IdE quote_smart($IdE);
        
$sql "
        SELECT * FROM Students
        WHERE StudentId={$IdE};
        "
;
        
$res mysql_query($sql);
        if(
$row mysql_fetch_assoc($res))
        {
            
$this->StudentId $IdE// this id is correct (we're into the if), we can set the value
            
$this->StudentName $row['StudentName'];
            
$this->StudentAge $row['StudentAge'];
        }
        else
            throw new 
myException('Student not found !'$sql); // here i'm using the second parameter
    
}
    
    public function 
__get($attribute)
    {
        if(!
property_exists(get_class($this), $attribute))
            throw new 
myException('Wrong student property : '.$attribute);
        
        return 
$this->$attribute;
    }
    
    public function 
__set($attribute$value)
    {
        if(!
property_exists(get_class($this), $attribute))
            throw new 
myException('Wrong student property : '.$attribute);
        
        
// others verifications
        
if($attribute == 'StudentId')
            throw new 
myException('Sorry but StudentId can\'t be updated');
        if((
$attribute == 'StudentAge' ||  $attribute == 'StudentId') && !is_numeric($value))
            throw new 
myException('Invalid data : need a numeric value for '.$attribute);
        
        
$this->$attribute $value;
    }
    
    public function 
getAge($year)
    {
        
$actualYear intval(date('Y'));
        return 
$this->StudentAge + ($year $actualYear);
    }
}


//-------------------


$_isException 0// no exception for the moment
try
{
    
$guy = new student();
    
$guy->loadStudent(1);
    
    
$guy->StudentAge 'unauthorized string'// launching an exception !
}
catch(
myException $_anException// writing over $_anException
{
    
$_isException 1;
    
//$theError = $e->getError();
    
$_anException->sendError(); // mail me !
    
    /* 
        Normally we should protect the following line with a try-catch block,
        because ... it could launch an Exception  !
        Here i'm not using try{}catch{} because i don't care if the error can't be save in database
        
        I commented the line to NOT save the exception in my database
        (else each net surfer request for this demo = one line in my database => BIG)
    */
    //$_anException->saveError();
}

// that's it, no more job :-)

include_once('includes/tbs_class_php5.php');
$TBS = new clsTinyButStrong;
$TBS->LoadTemplate('demo5.html');
$TBS->Show();

?>
 HTML: demo5.html 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Demo 5</title>
</head>
<body>

Welcome to Demo 5 :)<br /><br />

<div>[onload;block=div;when [var._isException]=0]
    <b>ID :</b> [var.guy.__get(StudentId)]<br />
    <b>NAME :</b> [var.guy.__get(StudentName)]<br />
    <b>AGE :</b> [var.guy.__get(StudentAge)]<br />
    <b>Age in 2015 :</b> [var.guy.getAge(2015)]
</div>

<div>[onload;block=div;when [var._isException]=1]
    There is an error, dammit !<br />
    <b>Error message :</b> [var._anException.getMessage]
</div>

</body>
</html>

Result:
I included a customized exception `myException` which is subclassing the PHP Exception class.
I recommend to put every single class in a separate file, rather than everything in one php file like I'm doing here.
Others modifications are simple:
The rule: no more `die` or `echo` or `print*`, this is the base of templates systems !

As you can see I'm using the TBS conditionnal display in the template, but you can also use a complete different template or a redirection in the catch block, as you wish ! But I prefer to do it that way to display errors that correspond to this very precised page, we will se that in the next part of the tutorial.

Moreover my customized exception class could be used to save errors in a log table or send an email to myself.
In conclusion: everytime a problem happens: I'm sure what's going on with my website, and TinyButStrong helps me not to break the user interface:-)


PART 6: A form example

If you read the previous part of this tutorial, you should now have enough in you hands to create a good site.
This part is a complete form example: from first page to successful validation throught warnings...

 PHP: demo6.php 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
<?php

include_once('includes/connect_bdd.php'); // database connection
include_once('includes/class_myException.php'); // personnal exception class

/*

CREATE TABLE IF NOT EXISTS `Students` (
  `StudentId` int(11) NOT NULL auto_increment,
  `StudentName` varchar(50) NOT NULL,
  `StudentAge` varchar(50) NOT NULL,
  PRIMARY KEY  (`StudentId`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

INSERT INTO `Students` (`StudentId`, `StudentName`, `StudentAge`) VALUES (1, 'DUPONT', 22);

*/

// a little function to help protecting sql requests
function quote_smart($value)
{
    if(
get_magic_quotes_gpc())
        
$value stripslashes($value);
    
    if(!
is_numeric($value))
        
$value mysql_real_escape_string($value);
    
    return 
$value;
}


//-------------------


class student
{
    private 
$StudentId;
    private 
$StudentName;
    private 
$StudentAge;
    
    
// Constructor
    
public function __construct()
    {
        foreach(
$this as $key => $value)
            
$this->$key NULL;
    }
    
    
// Destructor
    
public function __destruct()
    {    
        foreach(
$this as $key => $value)
            unset(
$this->$key);
    }
    
    public function 
loadStudent($IdE)
    {
        
$IdE quote_smart($IdE);
        
$sql "
        SELECT * FROM Students
        WHERE StudentId={$IdE};
        "
;
        
$res mysql_query($sql);
        if(
$row mysql_fetch_assoc($res))
        {
            
$this->StudentId $IdE// this id is correct (we're into the if), we can set the value
            
$this->StudentName $row['StudentName'];
            
$this->StudentAge $row['StudentAge'];
        }
        else
            throw new 
myException('Student not found !'$sql); // here i'm using the second parameter
    
}
    
    public function 
__get($attribute)
    {
        if(!
property_exists(get_class($this), $attribute))
            throw new 
myException('Wrong student property : '.$attribute);
        
        return 
$this->$attribute;
    }
    
    public function 
__set($attribute$value)
    {
        if(!
property_exists(get_class($this), $attribute))
            throw new 
myException('Wrong student property : '.$attribute);
        
        
// others verifications
        
if($attribute == 'StudentId')
            throw new 
myException('Sorry but StudentId can\'t be updated');
        if((
$attribute == 'StudentAge' ||  $attribute == 'StudentId') && !is_numeric($value))
            throw new 
myException('Invalid data : need a numeric value for '.$attribute);
        
        
$this->$attribute $value;
    }
    
    public function 
getAge($year)
    {
        
$actualYear intval(date('Y'));
        return 
$this->StudentAge + ($year $actualYear);
    }
    
    public function 
addStudent()
    {
        if(
//is_null($this->StudentId) ||
        
is_null($this->StudentName) ||
        
is_null($this->StudentAge))
            throw new 
myException('An essential student member is NULL');
        
        
$this->StudentName strtoupper($this->StudentName);
        
        if(
strlen($this->StudentName) < 3)
            throw new 
myException('Name too short');
        if(
$this->StudentAge 0)
            throw new 
myException('Age must be >0');
        
        
$StudentId quote_smart($this->StudentId);
        
$StudentName quote_smart($this->StudentName);
        
$StudentAge quote_smart($this->StudentAge);
        
        
$sql "INSERT INTO Students VALUES(
        NULL,
        '{$StudentName}',
        {$StudentAge});
        "
;
        if(!
mysql_query($sql))
            throw new 
myException('The student was not added to the database'$sql);
        
$this->StudentId mysql_insert_id();
    }
}


//-------------------


$_isException 0// no exception for the moment
$_success 0;
try
{
    if(isset(
$_POST['fname']) && isset($_POST['fage']))
    {
        
$guy = new student();
        
$guy->StudentName $_POST['fname'];
        
$guy->StudentAge $_POST['fage'];
        
        
$guy->addStudent();
        
$_success 1;
    }
}
catch(
myException $_anException// writing over $_anException
{
    
$_isException 1;
    
//$theError = $e->getError();
    //$_anException->sendError(); // mail me !
    
    /* 
        Normally we should protect the following line with a try-catch block,
        because ... it could launch an Exception  !
        Here i'm not using try{}catch{} because i don't care if the error can't be save in database
        
        I commented the line to NOT save the exception in my database
        (else each net surfer request for this demo = one line in my database => BIG)
    */
    //$_anException->saveError();
}

// that's it, no more job :-)

include_once('includes/tbs_class_php5.php');
$TBS = new clsTinyButStrong;
$TBS->LoadTemplate('demo6.html');
$TBS->Show();

?>
 HTML: demo6.html 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Demo 6</title>
</head>
<body>

Welcome to Demo 6 :)<br /><br />


<div style="background-color:red;border:2px solid black;">[onload;block=div;when [var._isException]=1]
    There is an error, dammit !<br />
    <b>Error message :</b> [var._anException.getMessage]
</div>

<div>[onload;block=div;when [var._success]=0]
    Add a new student in the database :<br />
    <form method="post" action="[var..script_name]">
        <table border="0">
            <tr>
                <td align="right">Name :</td>
                <td><input type="text" name="fname" maxlength="50" value="[var._POST.fname;noerr]" /></td>
            </tr>
            <tr>
                <td align="right">Age :</td>
                <td><input type="text" name="fage" value="[var._POST.fage;noerr]" /></td>
            </tr>
            <tr>
                <td colspan="2" align="center"><input type="submit" value="Add to database" /></td>
            </tr>
        </table>
    </form>
</div>

<div>[onload;block=div;when [var._success]=1]
    The student has been successfully added !<br />
    <a href="[var..script_name]">Add another student</a>
</div>

</body>
</html>

Result:
I've added 2 public functions to the class: __construct and __destruct
I recommand the implementation of these 2 functions to correctly initialize your objects.

I've also added the function `addStudent()` to add a student into the database...
Use of `noerr` in the template (at the script first call, var._POST.fage does not exist...)

Please note the two levels of verifications: =>We got a complete form with error handling, everything fit in a single php file and a single html file, woa !

Of course you can add some javascript code to make some verifications before the php processing of the form.
Good way to make a javascript verifications with a formular
But your php must re-verify EVERYTHING for a simple reason: never believe user input !
Example of problems: In this two cases, if you don't make verifications in your php application, then your database is going to get crapy !


PART 7: More with TBS and PHP Objects

PHP 5 could be a real pain if you're using inheritance and static members, I tell you that.
But this example is very easy to understand: it shows how to get a table of objects and use it with a TBS template.

 PHP: demo7.php 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
<?php

include_once('includes/connect_bdd.php'); // database connection
include_once('includes/class_myException.php'); // personnal exception class

/*

CREATE TABLE IF NOT EXISTS `Students` (
  `StudentId` int(11) NOT NULL auto_increment,
  `StudentName` varchar(50) NOT NULL,
  `StudentAge` varchar(50) NOT NULL,
  PRIMARY KEY  (`StudentId`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

INSERT INTO `Students` (`StudentId`, `StudentName`, `StudentAge`) VALUES (1, 'DUPONT', 22);

*/

// a little function to help protecting sql requests
function quote_smart($value)
{
    if(
get_magic_quotes_gpc())
        
$value stripslashes($value);
    
    if(!
is_numeric($value))
        
$value mysql_real_escape_string($value);
    
    return 
$value;
}


//-------------------


class student
{
    private 
$StudentId;
    private 
$StudentName;
    private 
$StudentAge;
    
    
// Constructor
    
public function __construct()
    {
        foreach(
$this as $key => $value)
            
$this->$key NULL;
    }
    
    
// Destructor
    
public function __destruct()
    {    
        foreach(
$this as $key => $value)
            unset(
$this->$key);
    }
    
    public function 
loadStudent($IdE)
    {
        
$IdE quote_smart($IdE);
        
$sql "
        SELECT * FROM Students
        WHERE StudentId={$IdE};
        "
;
        
$res mysql_query($sql);
        if(
$row mysql_fetch_assoc($res))
        {
            
$this->StudentId $IdE// this id is correct (we're into the if), we can set the value
            
$this->StudentName $row['StudentName'];
            
$this->StudentAge $row['StudentAge'];
        }
        else
            throw new 
myException('Student not found !'$sql); // here i'm using the second parameter
    
}
    
    public function 
__get($attribute)
    {
        if(!
property_exists(get_class($this), $attribute))
            throw new 
myException('Wrong student property : '.$attribute);
        
        return 
$this->$attribute;
    }
    
    public function 
__set($attribute$value)
    {
        if(!
property_exists(get_class($this), $attribute))
            throw new 
myException('Wrong student property : '.$attribute);
        
        
// others verifications
        
if($attribute == 'StudentId')
            throw new 
myException('Sorry but StudentId can\'t be updated');
        if((
$attribute == 'StudentAge' ||  $attribute == 'StudentId') && !is_numeric($value))
            throw new 
myException('Invalid data : need a numeric value for '.$attribute);
        
        
$this->$attribute $value;
    }
    
    public function 
getAge($year)
    {
        
$actualYear intval(date('Y'));
        return 
$this->StudentAge + ($year $actualYear);
    }
    
    public function 
addStudent()
    {
        if(
//is_null($this->StudentId) ||
        
is_null($this->StudentName) ||
        
is_null($this->StudentAge))
            throw new 
myException('An essential student member is NULL');
        
        
$this->StudentName strtoupper($this->StudentName);
        
        if(
strlen($this->StudentName) < 3)
            throw new 
myException('Name too short');
        if(
$this->StudentAge 0)
            throw new 
myException('Age must be >0');
        
        
$StudentId quote_smart($this->StudentId);
        
$StudentName quote_smart($this->StudentName);
        
$StudentAge quote_smart($this->StudentAge);
        
        
$sql "INSERT INTO Students VALUES(
        NULL,
        '{$StudentName}',
        {$StudentAge});
        "
;
        if(!
mysql_query($sql))
            throw new 
myException('The student was not added to the database'$sql);
        
$this->StudentId mysql_insert_id();
    }
    
    
//-----
    
    
public static function getAllStudents()
    {
        
$etu = array();
        
        
$res mysql_query('SELECT * FROM Students');
        while(
$row mysql_fetch_assoc($res))
        {
            
$e = new student();
            foreach(
$row as $attribute => $value)
            {
                if(
property_exists(get_class($e), $attribute))
                    
$e->$attribute $value;
            }
            
$etu[] = $e;
        }
        
        return 
$etu;
    }
    
    public static function 
getCount()
    {
        
$result mysql_query('SELECT COUNT(*) AS nb FROM Students');
        
$row mysql_fetch_assoc($result);
        return 
$row['nb'];
    }
    
    public static function 
deleteStudent($IdS)
    {
        
$IdS quote_smart($IdS);
        
        
$sql "DELETE FROM Students
        WHERE StudentId={$IdS};
        "
;
        if(!
mysql_query($sql))
            throw new 
myException('The student was not deleted from database'$sql);
        if(
mysql_affected_rows() == 0)
            throw new 
myException('There was no corresponding student');
    }
}


//-------------------


$_isException 0// no exception for the moment
try
{
    if(isset(
$_GET['delete']))
        
student::deleteStudent($_GET['delete']);
    
    if(isset(
$_POST['fname']) && isset($_POST['fage']))
    {
        
$guy = new student();
        
$guy->StudentName $_POST['fname'];
        
$guy->StudentAge $_POST['fage'];
        
        
$guy->addStudent();
    }
    
    
$nbStudents student::getCount();
    
$list student::getAllStudents();
}
catch(
myException $_anException// writing over $_anException
{
    
$_isException 1;
    
//$theError = $e->getError();
    //$_anException->sendError(); // mail me !
    
    /* 
        Normally we should protect the following line with a try-catch block,
        because ... it could launch an Exception  !
        Here i'm not using try{}catch{} because i don't care if the error can't be save in database
        
        I commented the line to NOT save the exception in my database
        (else each net surfer request for this demo = one line in my database => BIG)
    */
    //$_anException->saveError();
}

include_once(
'includes/tbs_class_php5.php');
$TBS = new clsTinyButStrong;
$TBS->LoadTemplate('demo7.html');
if(!
is_null($list)) // an exception may have been launched before the affectation of $list
    
$TBS->MergeBlock('Slist'$list); // merge the array
$TBS->Show();

?>
 HTML: demo7.html 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Demo 7</title>
<style type="text/css">
.ligne1{
background-color:#CCFFFF;
}
.ligne2{
background-color:#FFCC99;
}
</style>
</head>
<body>

Welcome to Demo 7 :)<br /><br />

<div>[onload;block=div;when [var._isException]=0]
    There are [var.nbStudents] student[var.nbStudents;if [val]+-1; then 's'; else ''] in database<br />
    There are [Slist.#] student[Slist.#;if [val]+-1; then 's'; else ''] in database (without getCount() !)<br />
    <table border="1">
    <tr>
        <th>ID</th>
        <th>NAME</th>
        <th>AGE</th>
        <th>AGE IN 2015</th>
        <th>Delete?</th>
    </tr>
    <tr class="[Slist.$;ope=mod:2;if [val]='0';then 'ligne1'; else 'ligne2';]">
        <td>[Slist.__get(StudentId);block=tr;bmagnet=table]</td>
        <td>[Slist.__get(StudentName)]</td>
        <td>[Slist.__get(StudentAge)]</td>
        <td>[Slist.getAge(2015)]</td>
        <td><a href="[var..script_name]?delete=[Slist.__get(StudentId)]">delete?</a></td>
    </tr>
    </table>
</div>

<div style="background-color:red;border:2px solid black;">[onload;block=div;when [var._isException]=1]
    There is an error, dammit !<br />
    <b>Error message :</b> [var._anException.getMessage]
</div>

<div>
    Add a new student in the database :<br />
    <form method="post" action="[var..script_name]">
        <table border="0">
            <tr>
                <td align="right">Name :</td>
                <td><input type="text" name="fname" maxlength="50" value="[var._POST.fname;noerr]" /></td>
            </tr>
            <tr>
                <td align="right">Age :</td>
                <td><input type="text" name="fage" value="[var._POST.fage;noerr]" /></td>
            </tr>
            <tr>
                <td colspan="2" align="center"><input type="submit" value="Add to database" /></td>
            </tr>
        </table>
    </form>
</div>

</body>
</html>

Result:
Here I'm using two static functions: getAllStudents, getCount and deleteStudent
Try the code with an empty table: bmagnet & if are very useful on the template side ! With TinyButStrong, I can easylly deal with the 's' at the end of the plural when necessary ;)

This demo is also the first one with a table of objects : I'm using MergeBlock.
In order to alternatively change the background color of the row, I'm using the 'ope' operator to check if the current item number modulo 2 is equal to 0 (-> can be divided by 2 or not).


PART 8: PHP Objects with arrays properties + TBS sub-blocks

This demonstration is a bit different because I'm not re-using the code previously seen in the PART 7 : I'm just using a simple student class with an array member.
The problem is : how can I render a table of students with their personnal array ? We have to use TinyButStrong sub-blocks :

 PHP: demo8.php 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

class student
{
    public 
$StudentId;
    public 
$StudentName;
    public 
$StudentNotes;
}

$guy1 = new student();
$guy1->StudentId 1;
$guy1->StudentName 'WOOD';
$guy1->StudentNotes = array('Maths'=>11,'Sports'=>14,'Physics'=>8,'French'=>17);

$guy2 = new student();
$guy2->StudentId 2;
$guy2->StudentName 'DELUISE';
$guy2->StudentNotes = array('Maths'=>9,16,'Sports'=>15,'Physics'=>10,6,19,8,13);

$students = array($guy1$guy2); // here the data is directly written into the php file, but i could be coming from the database...

include_once('includes/tbs_class_php5.php');
$TBS = new clsTinyButStrong;
$TBS->LoadTemplate('demo8.html');
    
$TBS->MergeBlock('Slist'$students); // merge the array
        
$TBS->MergeBlock('SNoteslist''array''students[%p1%]->StudentNotes'); // merge the sub-array using %p1%
$TBS->Show();

?>
 HTML: demo8.html 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Demo 8</title>
</head>
<body>

<table border="1">
    <tr>
        <th>ID</th>
        <th>NAME</th>
        <th>NOTES</th>
    </tr>
    <tr bgcolor="#CCFFFF">
        <td>[Slist.StudentId;block=tr;bmagnet=table]</td>
        <td align="center">[Slist.StudentName]</td>
        <td>        
            <table border="1" width="100%">
                <tr>
                    <th>Matter</th>
                    <th>Evaluation</th>
                </tr>
                <tr>
                    <td>[SNoteslist.key]</td>
                    <td>[SNoteslist.val;block=tr;bmagnet=table;p1=[Slist.$]]</td>
                </tr>
            </table>        
        </td>
    </tr>
</table>

</body>
</html>

Result:
The complicated stuff is at line 26 in both php and html files, explanations :

-Basically we have a table of objects, we already know how to deal with that since the Part 7 : a simple MergeBlock (line 25).
-To loop throught this array in the template, we have to use to key word "block=".
So it's logic to say that we will have to use another "block=" word to loop for each evaluation of the student... (line 26)
-The complicated thing here is that the html sub-loop is corresponding to more than one php array ! In facts :

'Slist' correspond to $students
'SNoteslist' correspond to every ->StudentNotes, which means $students[0]->StudentNotes and $students[1]->StudentNotes and so on...

-So we have to tell TinyButStrong which object is currently being processed ($students[0] or $students[1] ?) to map 'SNoteslist' with the good array. We can do that with the %p1% parameter.
-In the html I'm saying %p1% is the current index of the $students array ; value will be 0 or 1 in this demo.
-In the php I'm saying where %p1% should be used : between the [ ] characters to access the good object.

I know this sounds complicated when beginning with sub-blocks, don't worry I've been there too.
The best thing to do is to test by yourself the content of various variables such as [Slist.$] or [Slist.#], made some tests...

I recently needed to merge a sub-block of a sub-block in a personnal application. I also deescribe it as a "sub-sub-block".
To give you an idea : it's like having another array for each evaluation (the details ot the notation for example) in this demo...
TinyButStrong can deal with that with %p2%, as instance in the template : [SNoteslist2.val;block=tr;p1=[Slist.$];p2=[SNoteslist.$]]
And if you want more headaches you can continue with %p3%, %p4%, ... !!


PART 9: PHP 5 Objects + TBS Cache

Nowadays it is frequent to see that a website is 'down' due to the huge amount of simultaneous requests a sudden popularity generate...
It's cool to discover new interesting websites on plateforms like stumbleupon or digg... But sometimes thoses websites can't handle the amount of visitors because of their lack of optimisations... too bad.
The #1 tip to BOOST your website responsness is to cache it, or at least the maximum possible, to reduce slow calculations or databases requests. And when I say BOOST, that's because it's a no-brainer : cached page = FAST !

So this tutorial will describe A way of caching the previous example.
The html is almost the same, I just added a datetime viewer.

Be sure the folder where cache files are put is writable ! Permissions for 'others' to write ;)
When using the cache plugin, you might be checking into your cache folder to see which files are created/destroyed and retrieve to content of thoses files.
Be careful :
Take care with you ftp client when you navigate through the folders : by default most of current ftp software like filezilla have a cache option (their cache is not for web pages but for files list of ftp folders) turned ON to : this way the ftp software is faster.
Hit refresh or desactive the option to force your client to retrieve the file list everytime your're entering in a folder...

 PHP: demo9.php 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php

include_once('includes/tbs_class_php5.php');
include_once(
'includes/tbs_plugin_cache.php'); // the cache plugin

class student
{
    public 
$StudentId;
    public 
$StudentName;
    public 
$StudentNotes;
}

$TBS = new clsTinyButStrong;
$TBS->PlugIn(TBS_INSTALLTBS_CACHE'./cache''cache_*.html'); // cached files will be put in the folder 'cache' with a name beginning by 'cache_' and ending by '.html'

if(!$TBS->PlugIn(TBS_CACHE'demo9'600)) // 600 s = 10min
{
    
// if we pass here that means we (re)generate the cache file => we do all the processing (requests to database, mergeblock, ...)
    
    
$guy1 = new student();
    
$guy1->StudentId 1;
    
$guy1->StudentName 'WOOD';
    
$guy1->StudentNotes = array('Maths'=>11,'Sports'=>14,'Physics'=>8,'French'=>17);
    
    
$guy2 = new student();
    
$guy2->StudentId 2;
    
$guy2->StudentName 'DELUISE';
    
$guy2->StudentNotes = array('Maths'=>9,16,'Sports'=>15,'Physics'=>10,6,19,8,13);
    
    
$students = array($guy1$guy2); // here the data is directly written into the php file, but i could be coming from the database...
    
    
$TBS->LoadTemplate('demo9.html');
        
$TBS->MergeBlock('Slist'$students); // merge the array
            
$TBS->MergeBlock('SNoteslist''array''students[%p1%]->StudentNotes'); // merge the sub-array using %p1%
}
$TBS->Show();

?>
 HTML: demo9.html 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Demo 9</title>
</head>
<body>

Now : [var..now;frm='yyyy-mm-dd hh:nn:ss']

<table border="1">
    <tr>
        <th>ID</th>
        <th>NAME</th>
        <th>NOTES</th>
    </tr>
    <tr bgcolor="#CCFFFF">
        <td>[Slist.StudentId;block=tr;bmagnet=table]</td>
        <td align="center">[Slist.StudentName]</td>
        <td>        
            <table border="1" width="100%">
                <tr>
                    <th>Matter</th>
                    <th>Evaluation</th>
                </tr>
                <tr>
                    <td>[SNoteslist.key]</td>
                    <td>[SNoteslist.val;block=tr;bmagnet=table;p1=[Slist.$]]</td>
                </tr>
            </table>        
        </td>
    </tr>
</table>

</body>
</html>

Result:
Note that we have to use the object $TBS soon in the code => need to include the TBS class at the beginning.
It's up to you to determine the 'MaxAge' of a cache file... it mostly depends of the load your webserver is suffering.
In case a page is frequently updated, it might be bad to set a too large cache time, because it will force your script to frequently destroy the previous cache file before creating the new one.

We just install the plugin and then use it :
-if we pass into the if then we generate the cache file
-then every call to this page (for 600s) will simply output the cache file to your browser : no requests, no mergeblock, no calculations => FAST

Of course if a modification occurs with the data : new student, new note, updated evaluation, deleted record, ....
=> The cache file must be destroy. That way the next visiteur asking for this page will launch the regeneration of its cached version !

I added a link to destroy manually the cache file. Be careful with cached pages : it could gave you headaches too ;)
Don't forget that the simple existence of a cached page could make your script modifications useless until the timelimit as been reached or the cache file deleted.

With TinybutStrong you have the possibilty to cache just a part of your page, or merge some data after the cache as been loaded.
That's pretty useful if you want to cache the content of a page but not the menu, depending if your connected or not for example...
Have fun with the cache plugin !


PART 10: Some tips

Website architecture with TBS and PHP 5, some tips :

This is the end of my tutorial.
Has my tutorial helped you? If so, please consider a donation: Thanks !

Creative Commons License Content on this tutorial is licensed under a Creative Commons Attribution 3.0 License

Valid XHTML 1.0 Transitional