Learning
OOP in PHP ASAP!
PHP is so much more than a scripting
language. It’s a full-fledged language capable of building very complex
applications. By harnessing the full power of Object Oriented Programming, you
can reduce the amount of time you spend coding and use it to build better
websites. This tutorial will show you how.
What is OOP?
OOP stands for Object Oriented Programming. OOP is a programming paradigm wherein you create
“objects” to work with. These objects can then be tailored to your specific
needs, to serve different types of applications while maintaining the same code
base. Quite useful indeed.
What is an object?
An object is simply a copy or
instance of a “class”. A class can be defined as a “black box” from where we
create our objects and access its attributes (variables) and methods
(functions). The most common analogy given when explaining classes and OOP is
how you drive a car: basically, you have 2 or 3 pedals, a gear stick and a
steering wheel. You don’t need (and most probably don’t want) to know how the
car works when you press the pedals. You just want your car to go back and
forth, left and right. And OOP is precisely that. You don’t need to know how
the methods of a class work (if you didn’t implement it yourself), just what
they do. OOP is also useful if you have a large number of objects of the same
type in a system: you just have to create the objects and then manipulate them
all in the same way, without rewriting any code. Moreover, an object is able to
maintain it’s state (variable values and such) throughout the execution of the
program.
The implementation of “Car” is hidden from us, however we can use its full capacities.
OOP in PHP
PHP 5 (although most of the ideas in
this article also apply to PHP 4) has great support for object oriented
programming by providing easy class creation. PHP provides every paradigm other
“true” OOP languages implement (Python and JAVA, for example), like
inheritance, polymorphism and encapsulation.
Inheritance
The basic idea behind inheritance is
that similar objects share common properties. So by creating a “generic” class,
we can have a blueprint to build our subsequent classes on. Imagine, if you
will, a car’s properties: color, number of wheels, horsepower, number of seats,
etc. Having this template we can further specialize our cars by extending this
class: creating a racing car that has a “nitro” property, or a truck that has a
“trailer” property. The bottom line is: create a more generic class that
contains most of the common attributes and you will have much less work
defining other objects only slightly different. Instead of rewriting the whole
code, you just extend it’s properties, saving a lot of time in the process.
Inheritance diagram for our car classes.
Encapsulation
As previously explained, one of the
main advantages of using objects is that we don’t need to reveal all of its
members (attributes or functions); just the necessary interfaces to work with
it. Details not useful to the use of these objects should be hidden from the
rest of the objects. This is what is referred to as encapsulation.
Levels of visibility
·
public: means that a class
member is visible and usable / modifiable by everyone
·
private: means that a class
member is only usable / modifiable by the class itself
·
protected: means that a class
member is only usable / modifiable by the class itself and eventual sub-classes
NOTE: By default, in PHP, a class
member is public unless declared private or protected. Check out an example here.
Polymorphism
Polymorphism is an OOP characteristic
that allows the programmer to assign a different meaning or usage to something
in different contexts – specifically, to allow a class member to perform
different tasks depending on the context it was used. Imagine you have a Person
class and two sub-classes of Person: Japanese and American. Both implement a
function named talk(), but with different languages and social context. And
while both of them are basically People (as they derive from the class Person),
their implementation of the function talk() is very different. So you basically
have two objects of the class Person in which the talk() function works
differently.
Getting our hands dirty
Ok, now for the real action. We will
be creating a simple class to handle image manipulation and information
extraction. For the duration of this tutorial I will assume you have a basic
understanding of PHP (variables, creating functions, working with control flow
statements and loops). Reading the GD
PHP manual will remove any doubts you may have
about image handling functions.
Step 1: Creating our
class
We start by defining our class:
1.
<?php
2.
class Image {
3.
}
4.
?>
This just tells PHP we will start
defining a new class, named “Image”. Now we will define the class constructor. A constructor is simply the function
that is called when creating a new object. In PHP 5 this can
be achieved by two different methods: creating a public function with the exact
same name of the class (PHP 4 and on) or by creating a function called
“__construct()” (PHP 5 only):
Step 2: Creating the
class constructor
The following two pieces of code do
exactly the same thing:
1.
<?php
2.
class Image {
3.
public function Image() {
4.
echo "We just created and object!";
5.
}
6.
}
7.
$image = new Image(); // prints "We just created and object!"
8.
?>
1.
<?php
2.
class Image {
3.
function __construct() {
4.
echo "We just created and object!";
5.
}
6.
}
7.
$image = new Image(); // prints "We just created and object!"
8.
?>
NOTE: A class constructor is always
public.
Class constructors should be used to
ensure that the created object has a minimal amount of data to work with; in
our case, the desired image.
As such, the first thing we have to
do is to read the image, whatever it’s type may be. Currently, the GD library
supports a number of image types, such as jpg, png, gif, bmp, and others; we
just have to read the image and determine it’s type.
1.
<?php
2.
class Image {
3.
function __construct($filename) {
4.
5.
// read the image file to a binary buffer
6.
$fp = fopen($filename, 'rb') or die("Image '$filename' not found!");
7.
$buf = '';
8.
while(!feof($fp))
9.
$buf .= fgets($fp, 4096);
10.
11.
// create image
12.
imagecreatefromstring($buf);
13.
}
14.
}
15.
$image = new Image("image.png"); // If everything went well we have now read the image
16. ?>
So what have we done? To open the
image as effortlessly as possible, we will be using GD’s functionimagecreatefromstring (which takes a binary string of data as input), instead of imagecreatefromjpeg,imagecreatefrompng or imagecreatefromgif,
for example.
So we try to
open an image file and assign it’s file pointer to $fp,
and, in case of failure, terminate the program’s execution.
1.
$fp = fopen($filename, 'rb') or die("Image '$filename' not found!");
Next, we
create an empty string to hold our data…
1.
$buf = '';
…and read the
whole file, concatenating the data read with our newly created string contents.
1.
while(!feof($fp))
2.
$buf .= fgets($fp, 4096);
Now we just
have to create our image using the data we just read…
1.
imagecreatefromstring($buf);
… and create
an object to use all these functions.
1.
$image = new Image("image.png");
NOTE: jpg,
png, gif and most image files need to be read in binary mode, hence the “rb”
passed as the second argument of the fopen function. “r” means read-only and
“b” means binary.
Step
3: Defining class attributes and methods
In it’s
current form our class isn’t very useful. So we will be adding some attributes
and methods to make it more useful. So we will start by 1: defining some
internal variables (note the “private” visibility declaration before each
variable)
1.
<?php
2.
class Image {
3.
4.
// class atributes (variables)
5.
private $image;
6.
private $width;
7.
private $height;
8.
private $mimetype;
9.
10.
function __construct($filename) {
11.
12.
// read the image file to a binary buffer
13.
$fp = fopen($filename, 'rb') or die("Image '$filename' not found!");
14.
$buf = '';
15.
while(!feof($fp))
16.
$buf .= fgets($fp, 4096);
17.
18.
// create image and assign it to our variable
19.
$this->image = imagecreatefromstring($buf);
20.
21.
// extract image information
22.
$info = getimagesize($filename);
23.
$this->width = $info[0];
24.
$this->height = $info[1];
25.
$this->mimetype = $info['mime'];
26.
}
27.
}
28.
$image = new Image("image.png"); // If everything went well we have now read the image
29. ?>
And 2: a
method to display the image.
1.
<?php
2.
class Image {
3.
4.
.
5.
.
6.
.
7.
8.
public function display() {
9.
header("Content-type: {$this->mimetype}");
10.
switch($this->mimetype) {
11.
case 'image/jpeg': imagejpeg($this->image); break;
12.
case 'image/png': imagepng($this->image); break;
13.
case 'image/gif': imagegif($this->image); break;
14.
}
15.
//exit;
16.
}
17.
}
18.
$image = new Image($_GET['image']); // If everything went well we have now read the image
19. ?>
In this step
we just created some class attributes (image, width, height and mimetype) to
hold the object’s data. Then we made some modifications to assign the created
image to our class attribute $image…
1.
$this->image = imagecreatefromstring($buf)
… and
extracted the image’s informations to our remaining class variables (read the
documentation ongetimagesize to
fully understand how the image information is read):
1.
// extract image information
2.
$info = getimagesize($filename);
3.
$this->width = $info[0];
4.
$this->height = $info[1];
5.
$this->mimetype = $info['mime'];
Next we
created a function that outputs the image to the browser by defining the
appropriate headers (read more on http headers here) and using the appropriate function (with
the switch statement) to output the image based on the original image mimetype
(for this tutorial we will just support jpg, png and gif but, as I said before,
GD support a multitude of other formats. Read the php documentation for more).
So what’s
this “$this” stuff in there? “$this”, in PHP, refers to the class itself, and
it’s used to point to class attributes or functions. As such, $this->image
points to the class attribute named “$image” and $this->image = … changes
the value of the class attribute. If you were to write $image = … you would
just be creating a new local variable named “$image”, available exclusively for
the duration of the function. This is one of the main things to pay attention
to when creating classes in PHP.
Our not very useful (yet!) display method.
Step
4: Defining our “Thumbnail” sub-class
Right now our
class isn’t very useful. Sure, we can read our image and display it, but that’s
it. We will now create a sub-class to create our thumbnails. (We really don’t
need to create a sub-class, but we will for the sake of the tutorial, to
demonstrate inheritance and polymorphism). So, for the inheritance to work
properly we need to slightly change the definition of our super-class (Image).
We just need to change the visibility of our class variables from “private” to
“protected”. And now we will create the constructor of our subclass.
1.
<?php
2.
class Image {
3.
4.
// class atributes (variables)
5.
protected $image;
6.
protected $width;
7.
protected $height;
8.
protected $mimetype;
9.
10.
.
11.
.
12.
.
13.
}
14.
15.
class Thumbnail extends Image {
16.
17.
function __construct($image, $width, $height) {
18.
19.
// call the super-class contructor
20.
parent::__construct($image);
21.
22.
// modify the image to create a thumbnail
23.
$thumb = imagecreatetruecolor($width, $height);
24.
imagecopyresampled($thumb, $this->image, 0, 0, 0, 0, $width, $height, $this->width, $this->height);
25.
$this->image = $thumb;
26.
}
27.
}
28. ?>
So what
exactly are we doing here? We created a new class, derived from our original,
meaning that we can access all of its public and protected attributes and
methods. We call the super-class constructor, responsible for reading the image
and extracting its information. The sub-class constructor does not call its
superclass constructor so we need to call it explicitly.
Now we create
a new image for our thumbnail, with the width and height passed:
1.
$thumb = imagecreatetruecolor($width, $height);
Resample
(resize) the original image into the new one, to create the thumbnail:
1.
imagecopyresampled($thumb, $this->image, 0, 0, 0, 0, $width, $height, $this->width, $this->height);
And finally
modifiy the original image to contain the thumbnail instead of the full-size
image:
1.
$this->image = $thumb;
And guess
what? We don’t really need to write a new function to display the thumbnail
because the same principle applies, whether you are displaying a full size
image or a thumbnail. After all, it’s still an image! So we just need to call
our display() function, defined in the super-class, and we’re done!
Our completed class and respective sub-class.
And that
concludes our tutorial. As an exercise I, suggest you implement a function to
save the generated thumbnails to disk instead of outputing it on the fly (Where
should you implement that function? In the super or sub-class?). Good luck and
check the zip provided for an example usage and the full classes developed here
(you need to have a PHP powered server to test it).