OOP Object Oriented Programming
In our experience people struggle to learn OOP because:
- It is a bit of a weird concept
- Examples given in tutorials are complicated, confusing and not real-world
- The jargon is mind blowing
- Not knowing any history of the pre-OOP world, could make you ask 'why OOP?'
The first hurdle is the greatest, and we hope to get you over it!
btw we assume an understanding of basic programming concepts such as functions and variables.
This article will also be of use to anyone converting from PHP4 to PHP5 or later.
A Little History
Decades ago (late 1970's ?) programmers were writing ever more complicated code; code which was written by more than one person; code which included conflicting snippets from other companies; code which included short term bodges which became too entwined to remove safely; code which was largely uncommented.
OOP sought to address these issues, and more.
Structure
OOP breaks a program into smaller more manageable chunks (objects). Each object has a single purpose (eg communicating with a database), and groups data and functions corresponding to this purpose in one place.
Lets say you have an object called $myDatabase which handles database communication.
- All other parts of your program must use $myDatabase to access the database
- If the database connection is lost, $myDatabase will reconnect
- $myDatabase keeps the connection details private so no other part of the program can corrupt them
- $myDatabase knows nothing about HTML, dates, what the data it provides is about, or where it goes to
- All it does is handle database communication
In use, using PHP syntax, it may look a little like this:
<?php
$myDatabase->query('SELECT * FROM products WHERE 1');
$rows = $myDatabase->row_count();
$myDatabase->username=''; // PHP error : 'username' is data private to this object
$myDatabase->connect(); // PHP error : 'connect' is a function private to this object
?>
Classes and Objects
The words class and object are often used interchangeably in OOP. They are not the same thing.
Continuing our example, lets say you have 2 databases you want to connect to. It could look like this:
<?php
$db1 = new dbComm('my_db1', 'localhost', 'username', 'pass');
$db2 = new dbComm('my_db1', 'localhost', 'un2', 'pass123');
$db1->query('SELECT * FROM products WHERE 1');
$db2->query('SELECT * FROM products WHERE 1');
if ($db1->row_count() > $db2->row_count())
{
echo "There are more products in my_db1 !!";
}
?>
So $db1 and $db2 are objects. The class of both these objects is dbComm. We'll see how to define classes below.
Note: $db1 and $db2 are said to be 'instances' of dbComm
Classes, Instantiation, Constructors and Destructors
Here is a simple class
<?php
class page
{
public function __construct($title)
{
echo "<html>\n";
echo "<head><title>$title</title></head>\n";
echo "<body>\n";
}
public function __destruct()
{
echo "</body>\n";
echo "</html>\n";
}
public function p($str)
{
echo "<p>$str</p>\n";
}
}
?>
and this php:
<?php
$html = new page('My Page');
$html->p('This is my webpage content !!');
?>
will product this output:
<html>
<head><title>My Page</title></head>
<body>
<p>This is my webpage content !!</p>
</body>
</html>
What happened ?
- The 'new' keyword found class 'page' and created an object from it (or $html became an instance of 'page')
- The constructor function '__construct' gets called automatically
- Function 'p' can be called because it is defined as 'public' (were it 'private', it could only be used internally in the object)
- The program ends and the object is destroyed automatically (calling function '__destruct' in the process)
Notes:
- In PHP constructor and destructor functions are optional
- Destructors are called when then script ends, or the object goes out of scope, or gets deleted using the unset() function
- The PHP exit() function will call object destructors before exiting the script
Class Constants, Properties, 'this', 'self'
Use your powers of reason; what does this class do?
<?php
class m3Price
{
const VAT_RATE = 17.5;
private $incVat = 0;
public function __get($name)
{
switch($name)
{
case 'inc_vat': return $this->incVat;
case 'ex_vat': return $this->incVat * 100 / (100 + self::VAT_RATE);
}
trigger_error("m3Price has no gettable property '$name'", E_USER_ERROR);
}
public function __set($name, $value)
{
switch($name)
{
case 'inc_vat':
$this->incVat=$value;
break;
case 'ex_vat':
$this->incVat = $value + $value * self::VAT_RATE / 100;
break;
default:
trigger_error("m3Price has no settable property '$name'", E_USER_ERROR);
}
}
}
?>
It keeps a price, and on demand, tells you the value including or excluding a specified VAT rate. It is used like this:
<?php
$price = new m3Price; // $price becomes an instance of the class 'm3Price'
$price->inc_vat=100; // Same as $price->__set('inc_vat', 100);
echo $price->ex_vat; // Same as echo $price->__get('ex_vat'); (echos 85.1063829787)
$price->ex_vat=100; // Same as $price->__set('ex_vat', 100);
echo $price->inc_vat; // Same as echo $price->__get('ex_vat'); (echos 117.5)
echo m3Price::VAT_RATE; // (echos 17.5)
echo $price->incVat; // PHP error. $incVat was declared as private (only for use within the class)
?>
Notes:
- $incVat was declared inside the class, but outside any functions, so it is available to any function within the class as $this->incVat
- VAT_RATE is constant; its value cannot be changed.
Constants are the same across all instances of the class, so are accessed by self:: rather than $this-> - Class constants can be accessed at any time outside the class by class_name::constant_name
(in our example m3Price::VAT_RATE) - 'inc_vat' and 'ex_vat' are said to be properties of $price
Tips:
- We like to prefix classes we write with 'm3', as in the example above. It avoids name clashes with other classes we acquire, and months down the line if we find a bug, we can search for all deployments of a class given this specific name.
[ this article is under development ]