Intermediate PHP

Builds upon the foundation principles of PHP, and now follows on into the concept of Object Oriented Programming within PHP.

 

It is assumed that you will already have a good understanding of the general OOP ideas and techniques, and the following material will therefore not go into the theory behind OOP but will focus upon how it is implemented within PHP.

 

Some of the more advanced techniques in PHP will be presented after the OO PHP articles.

class

The keyword class is used to define a user defined (abstract) data type. It is then followed by the user defined class name (/identifier) and a pair of curly braces { }

e.g class className { }

 

As you can see the above is not too useful on its own and this is because classes usually contain class members, consisting of attributes (/properties) and behaviours (/methods).

new

An object is created (/ instantiated) using the keyword new to assign the class to the user defined object name.

e.g. $myObject = new className ;

 

This example uses the var_export() function to view the contents of the object:

<?php

class Bike {}

echo "<h1>Instantiating a Bike object</h1><br>" ;

$myObject = new Bike ;

echo "<pre>" . var_export($myObject, TRUE) . "</pre>" ;

?>

Save & refresh browser:

Instantiating a Bike Object

NULL

 

The var_export() function is showing a NULL output since the class is empty (i.e. it has no members; properties/methods).

Properties

Aka attributes 

  • Data type definitions for storing the actual values to be assigned to an individual object
  • Must be defined with a visibility keyword: public, protected, private, final
  • Can also be initialised by assigning a value within the class definition

 

This example shows a property being added to the class:

<?php

class Bike {

	public $make ;
}

echo "<h1>Instantiating a Bike Object</h1>" ;

$myBike = new Bike ;

echo "<p>A new Bike object has been instantiated, called \$myBike</p>";

//the -> (small arrow) access operator is used to get or set an object's attributes
$myBike->make = "Ducati" ; //$ sign is not required for variable when using ->

echo "<p>The \$myBike object's single property, \$make, has been assigned a value of: ";

echo $myBike->make . "</p>";

echo "<pre>" . var_export($myBike, TRUE) . "</pre>" ;

?>

Save & refresh browser:

Instantiating a Bike Object

A new Bike object has been instantiated, called $myBike

 

The $myBike object's single property, $make, has been assigned a value of: Ducati

 

Bike::__set_state(array(

'make' => 'Ducati',
))

 

 

*Note: the -> (aka small arrow) access operator is used to get or set an object's attributes

-> Access Operator

The -> (aka small arrow) access operator is used to get or set an object’s attributes.

 

This example show the -> access operator being used to set and get a properties values:

 Access Operator" ><?php

	class Bike {

		public $make ;
	}

	echo "<h1>Using the -> access operator to set and get an object's properties</h1>";

	$myBike = new Bike ;

	//SETTING the property value using the -> (small arrow) access operator
	$myBike->make = "Ducati" ; //$ sign is not required for variable when using ->

	echo "<p>The \$myBike object's single property, \$make, has been assigned a value of: ";

	//GETTING the property value using the -> (small arrow) access operator
	echo $myBike->make . "</p>";

?>

Save & refresh browser:

Using the -> access operator to set and get an object's properties

The $myBike object's single property, $make, has been assigned a value of: Ducati

Methods

Aka behaviours

  • Internal functions of an object defining what it can do
  • Public be default, but a good idea to specify, just in case, and for good readability

 

This example now shows a method being added to the class:

<?php

class Bike {

	public $make ;

	public function display() {
		echo "does something</p>";
	}
}

echo "<h1>Instantiating a Bike Object</h1>" ;

$myBike = new Bike ;

echo "<p>A new Bike object has been instantiated, called \$myBike</p>";

//the -> (small arrow) access operator is used to get or set an object's attributes
$myBike->make = "Ducati" ; //$ sign is not required for variable when using ->

echo "<p>The object's single property, \$make, has been assigned a value of: ";

echo $myBike->make . "</p>";

echo "<p>A method has also been defined that: ";

echo $myBike->display() ; //parentheses () must follow method name

echo "The method can also be called without having the need for an instantiated object, and still: ";

Bike::display() . "<br>";

echo "<pre>" . var_export($myBike, TRUE) . "</pre>" ;

?>

Save & refresh browser:

Instantiating a Bike Object

A new Bike object has been instantiated, called $myBike

 

The object's single property, $make, has been assigned a value of: Ducati

 

A method has also been defined that: does something

 

The method can also be called without having the need for an instantiated object, and still: does something
Bike::__set_state(array(
'make' => 'Ducati',
))

 

 

*Note: parentheses must follow the method name

Constructor

The constructor is a magic method that is automatically called upon at the instantiation of a new object.

 

Syntax:

public function __construct() {

... code goes here...

}

 

(*note: magic methods start with a double underscore __ and will be discussed below)

 

A class showing a constructor being automatically called upon an object's instantiation:

<?php
	class Dog {

		public function __construct() {
			echo "An instance of the the '" . __CLASS__ . "' class has been instantiated!";
		}
	}
	$fido = new Dog;
?>

Save & refresh browser:

An instance of the the 'Dog' class has been instantiated!

 

 

*note: the above code example also uses the __CLASS__ magic constant, which returns the name of the class

 

  • When using inherited classes, PHP initially looks for and if found will use that constructor in the child class
  • If the child class does not have a constructor, PHP will look to the parent, and then the grand parent, until it reaches the top or finds a constructor
  • Only one constructor will be called

Destructor

PHP (like Java, but unlike C++) has automatic garbage collection.

 

Unless otherwise explicitly carried out, objects are automatically destroyed at the end of the script they were created in.

 

In some cases, it might sometimes be useful to specifically carry out a task when an object  is destroyed, say to close a database connection.

 

Accordingly, the destructor is a magic method that is automatically called when an object is destroyed, e.g. at the end of the script.

 

Syntax:

public function __destruct() {

… code goes here…

 }

 

(*note: magic methods start with a double underscore __ and will be discussed below)

 

A class showing a destructor being automatically called at the end of the script:

<?php
	class Dog {

		public function __construct() {
			echo "An instance of the the '" . __CLASS__ . "' class has been instantiated!<br>";
		}
		public function __destruct() {
			echo "Yalp!! The '" . __CLASS__ . "' object has been destroyed!
";
		}
	}
	$fido = new Dog;

	$echo "The script ends here.
";
?>

Save & refresh browser:

An instance of the the 'Dog' class has been instantiated!
The script ends here.
Yalp!! The 'Dog' object has been destroyed!

 

It can be seen that the destructor was called after the last command in the script, i.e. when the script ended.

Deleting Objects with unset()

To delete an object before the end of a script the keyword unset() is used with the object's variable name within its parentheses

 

Syntax:

unset($myObject);

 

To call the destructor before the end of the script, i.e. when you require, use the unset() function:

<?php
	class Dog {

		public function __construct() {
			echo "An instance of the the '" . __CLASS__ . "' class has been instantiated!<br>";
		}
		public function __destruct() {
			echo "Yalp!! The '" . __CLASS__ . "' object has been destroyed!<br>";
		}
	}
	$fido = new Dog;
	unset($fido);
	echo "The script ends here.<br>";
?>

Save & refresh browser:

An instance of the the 'Dog' class has been instantiated!
Yalp!! The 'Dog' object has been destroyed!
The script ends here.

 

It can now be seen that the destructor has been called before the end of the script by using the unset() function.

:: Scope Resolution

The scope resolution operator :: (aka in Hebrew as Paamayim Nekudotayim or the double colon) is explicitly used to access static, constant and overridden properties and methods of a class. The class required is defined to the left of the :: operator and the member to the right.

 

Syntax:

className::memberName ;

 

 

<?php
	class Bike {
		public $name;
		public function setName($myName) {
			$this->name = $myName;
		}
		public function greet() {
			echo "Welcome to the Ducati appreciation club!<br>";
		}
		public function speak() {
			echo "The latest Ducati road going sports bike is the $this->name.<br><br>";
		}
	}
	class MotoGP extends Bike {
		private $prototype;

		public function setPrototype($myPrototype) {
			$this->prototype = $myPrototype;
		}
		public function greet() {
			echo "Welcome Ducati MotoGP supporters!<br>";
		}
		public function speak() {
			echo Bike::greet();
			echo $this->greet();
			echo "The $this->prototype is the MotoGP prototype racing equivalent of their road bike.<br><br>";
		}
	}
	Bike::greet();
	$panigale = new Bike;
	echo "The above shows a class method being called without an object instance.<br><br>";
	$panigale->setName("Panigale");
	$panigale->speak();

	$desmodici = new MotoGP;
	$desmodici->setPrototype("Desmodici");
	$desmodici->speak();
	MotoGP::greet();
?>

Save & refresh browser:

Welcome to the Ducati appreciation club!
The above shows a class method being called without an object instance.

 

The latest Ducati road going sports bike is the Panigale.

 

Welcome to the Ducati appreciation club!
Welcome Ducati MotoGP supporters!
The Desmodici is the MotoGP prototype racing equivalent of their road bike.

 

Welcome Ducati MotoGP supporters!

Parent Constructor

A Parent Constructor is used to add new functionality in a child's constructor whilst also using the Parent's constructor. Thus both the original Parent constructor and the Child constructor are called. This is achieved by using the keyword parent before the scope resolution operator :: and the __construct() magic method, within the child constructor.

 

Syntax:

parent::__construct();

 

This example shows a ParentClass and two child classes, one of which utilises the parent constructor as well as its own constructor:

<?php
	class ParentClass {
		function __construct() {
			echo "ParentClass constructor ";
		}
	}
	class RedChild extends ParentClass {
		function __construct() {
			parent::__construct();
			echo "and the RedChild constructor<br>";
		}
	}
	class BlueChild extends ParentClass {
		// inherits Parent's constructor
	}

	echo "ParentClass object instantiated using the ";
	//using the myParent constructor
	$obj = new ParentClass();

	echo "<br>RedChild object instantiated using the ";
	//Using the ParentClass constructor
	//Using the RedChild constructor
	$obj = new RedChild();

	echo "BlueChild object instantiated using the ";
	//using the ParentClass constructor
	$obj = new BlueChild();

?>

Save & refresh browser:

ParentClass object instantiated using the ParentClass constructor
RedChild object instantiated using the ParentClass constructor and the RedChild constructor
BlueChild object instantiated using the ParentClass constructor

Objects within objects

Just like any other variable, objects can be used within other objects and are similarly accessed by using the small arrow -> access operator again.

 

This example instantiates a new (Display) object on line 23 that is assigned to the team property within the Rider object, it then assigns a value to the Display object's output property on line 27, and finally echo it back on line 30 to show the assignment has taken place:

<?php
	class Display {
		public $output;

		public function __construct() {
			echo "An object instance of the " . __CLASS__ . " class has been instantiated.<br>";
		}
	}

	class Rider {
		public $name;
		public $team;

		public function __construct() {
			echo "An object instance of the " . __CLASS__ . " class has been instantiated.<br>";
		}
	}

	$marquez = new Rider; //create a Rider object called marquez
	$marquez->name = "Marc Marquez"; //assign a value to its name property

	//now we assign a Display object to the team property
	$marquez->team = new Display;

	//next we assign a value to the output propertyy of the Display object,
	//whcih was assigned above to the team propetry of the Rider object
	$marquez->team->output = "My name is " . $marquez->name . " and I currently ride for Honda.";

	//just to be sure, here's the assigned output property
	echo $marquez->team->output ;
?>

Save & refresh browser:

An object instance of the Rider class has been instantiated.
An object instance of the Display class has been instantiated.
My name is Marc Marquez and I currently ride for Honda.

 

*note: the use of constructors, on lines 5 & 14,  is explained under the constructor magic method here

$this

The pseudo variable $this is used within a class to access the properties of the calling object (i.e. itself).

 

To access properties of an object use $this with the small arrow -> access operator, and then the name of the property without its $ dollar sign:

 

e.g. $this->name

 

Using $this to access the object's properties:

<?php

class Bike {

	public $make ;
	public $model ;
	public $capacity ;
	public $topSpeed ;
	public $cost ;

	public function display() {

		$output = $this->make . ', ';
		$output .= $this->model . ', ';
		$output .= $this->capacity . ', ';
		$output .= $this->topSpeed . ', ';
		$output .= $this->cost . '<br>';

		return $output;
	}
}

echo "<h1>Exploring an object's pseudo varibale \$this</h1>" ;

$myBike = new Bike ;

$myBike->make = "Ducati" ; //$ sign is not required for variable when using ->
$myBike->model = "Panigale" ;
$myBike->capacity = "1200" ;
$myBike->topSpeed = "175" ;
$myBike->cost = "17,999" ;

echo 'Using the display method to show the properties:<br>' . $myBike->display() . '<br>'; //parentheses () must follow method name

echo "<pre>" . var_export($myBike, TRUE) . "</pre>" ;

?>

Save & refresh browser:

Exploring an object's pseudo varibale $this

Using the display method to show the properties:
Ducati, Panigale, 1200, 175, 17,999

 

Bike::__set_state(array(
'make' => 'Ducati',
'model' => 'Panigale',
'capacity' => '1200',
'topSpeed' => '175',
'cost' => '17,999',
))

Inheritance (extends)

Inheritance is an Object Oriented concept that establishes a relationship between a Parent (/Super) class and a Child (/Sub) class.

 

The child is said to inherit the members of the parent.

 

The keyword extends is used in the class signature, as can be seen on line 8 as follows:

<?php
class Person {
	public $name ;
	public function speak() {
		echo "Hi, my name is $this->name, and I'm a person";
	}
}
class Scientist extends Person {
	public $hair ;
	public function speak() {
		echo "Hi, my name is $this->name, and I'm a Scientist!";
		echo "I also have long $this->hair hair!!";
	}
}
$julia = new Scientist ;
$julia->name = "Julia" ;
$julia->hair = "blonde" ;
$julia->speak() ;
?>

Save and refresh browser:

Hi, my name is Julia, and I'm a Scientist!
I also have long blonde hair!!

 

The new Scientist class has extended the Person class, and in this case has also overridden the speak() method of the parent.

Overriding Methods

Methods within a child class can override the parent class, simply by redefining your own version.

 

 

The parent's display method on line 5 is overridden in the child's redefinition on line 13:

<?php
	class ParentClass {
		public $name;

		public function display() {
			$output = $this->name;
			$output .= "Just some random text that's going to be overridden!";
			return $output;
		}
	}
	class ChildClass extends ParentClass {

		public function display() {
			$output = $this->name;
			$output .= " \"Legendary\" NYC Goth DJ!";
			return $output;
		}
	}
	$myChild = new ChildClass;
	$myChild->name = "Vanessa Miasma";
	echo $myChild->display();
	?>

Save & refresh browser:

Vanessa Miasma "Legendary" NYC Goth DJ!

Access Specifiers

The access specifiers (aka access modifiers) define the level of visibility of a class’s members.

 

The following scope keywords are used to define the level of access:

  • public
    • members can be accessed any place the object is visible, in the script
  • protected
    • members can only be accessed from within the class itself and inherited/inheriting classes
  • private
    • members are accessible only from other members of the same class
    • explicitly; they are not accessible by inherited/inheriting classes

The $this-> pseudo variable must be used with the property name to access specific properties! The use of the member name on it's own will be ignored.

 

Example showing public access:

<?php
class Person {
	public $name ;
	public function speak() {
		echo "Hi, my name is $this->name";
	}
}
$julia = new Person ;
$julia->name = "Julia" ;
$julia->speak() ;
?>

Save & refresh browser:

Hi, my name is Julia

 

To benefit from the essential OO concept of encapsulation (/ data hiding), it is desirable to keep the properties of the class private / protected, and to make the class responsible for setting and getting its own properties via its own methods only, hence disabling unwanted changes in an object's properties by anything other than the object itself.

 

The following example shows a protected property that can be inherited by a child class, if this property was set to private it would not be accessible in the child class and could therefore not be used. The example also shows a private property in the child class that can only be accessed within the class:

<?php
	class Person {
		protected $name ;
		public function setName($myName) {
			$this->name = $myName ; //must use $this-> and the name of the property
		}
		public function getName() {
			return $this->name . "<br>";
		}
		public function speak() {
			echo "Hi, my name is $this->name, and I'm a techie<br>";
		}
	}
	class Scientist extends Person {
		private $toys;
		public function setToys($myToys) {
			$this->toys = $myToys ;
		}
		public function speak() {
			$output = "$this->name is a Scientist who works with $this->toys!!";
			return $output;
		}
	}
	$techie = new Person;
//	$techie->name = "Derrick"; //this causes a fatal error, since the property is protected
	$techie->setName("Derrick");
	echo $techie->getName();
	echo $techie->speak();

	$geek = new Scientist ;
//	$geek->toys = "2DIR spectra" ; //this causes a fatal error, since the property is private
	$geek->setName("Julia") ;
	echo $geek->getName() ;
	$geek->setToys("lazers") ;
	echo $geek->speak() ;

?>

Save & refresh browser:

Derrick
Hi, my name is Derrick, and I'm a techie
Julia
Julia is a Scientist who works with lazers!!

 


final

  • used to to declare that a method or class cannot be overridden by a subclass
  • cannot be applied to properties
  • If the class itself is being defined final then it cannot be extended
  • Means of stopping other programmers using the code in unplanned ways

This example will purposefully fail due to the final keyword being applied to the Person class's speak() function.

<?php
	class Person {
		public $name;
		final public function speak() {
			echo $this->name;
		}
	}
	class Scientist extends Person {
		public function speak() {
			echo "$this->name is a Scientist who works with lazers!!";
		}
	}
	$geek = new Scientist;
	$geek->name = "Julia";
	echo $geek->speak() ;
?>

Save & refresh browser:

Fatal error: Cannot override final method Person::speak() in /root/user/public_html/php/index.php on line 12

 

If the child class had not overridden the parent's final declared method, it would have simply used the parent's method with no errors

 


abstract

  • cannot be used directly
  • has to be inherited by a child class

This examples show an abstract class, Person, being inherited by the child, Scientist, class. When the script tries to instantiate the Parent abstract class a fatal error is returned:

<?php
	abstract class Person {
		public $name;
		public function speak() {
			echo $this->name;
		}
	}
	class Scientist extends Person {
		public function speak() {
			echo "$this->name is a Scientist who works with lazers!!";
		}
	}
	$geek = new Scientist;
	$geek->name = "Julia";
	echo $geek->speak() ;

	$techie = new Person;
	$techie->name = "Derrick";
	echo $techie->speak() ;
?>

Save & refresh browser:

Julia is a Scientist who works with lazers!!
Fatal error: Cannot instantiate abstract class Person in /root/user/public_html/php/index.php on line 17

static

The keyword static is used to make class members available at all times throughout a script, without the need for instantiating an object.

 

  • Because static methods are callable without an instance of an object being created, the pseudo-variable $this is not available inside a static method
    • instead the keyword self should be used e.g. return self::$myvar;
  • Static properties cannot be accessed through the object using the arrow operator ->
  • Static methods cannot access normal methods (those not declared static) since they belonging to an object, and not the class

 

Properties declared as static within a class mean that just one version of that property is used by all objects of that class. This could be useful for incrementing say a counter to keep a tally of the number of objects instantiated.

 

When a method is declared static it enables the method to be called like any other function.

 

In order to utilise static members from outside the class, the class name and the :: scope resolution operator must be used so that the correct member can be identified and used.

 

Syntax:

ClassName::member;

 

In this example a value has been assigned to the class's static $name property. This is called using self::$name within the class's static greet() function. To access the members from outside the class scope the :: scope resolution operator is used, as can be seen on lines 11 & 12:

<?php
	class Musician {

		static public $name = "Norman Fisher-Jones";

		static public function greet() {
			echo  "Hi " . self::$name;
		}
	}

	echo Musician::$name . '<br>';
	Musician::greet();
?>

Save & refresh browser:

Norman Fisher-Jones
Hi Norman Fisher-Jones

Object Properties Iteration

Since objects are quite similar to arrays, it is possible iterate through it's properties with a foreach loop.

 

  • properties must be accessible
  • private and protected are not accessible in the general scope

 

This example instantiates an object from a simple class with a number of properties that the foreach loop then iterates through. Note the private property on line 6 is not accessible outside the class:

<?php
	class Bike {
		public $make = "Yamaha";
		public $model = "YZFR1";
		public $cost = 12999;
		private $topSpeed = 165;
		public $mpg = 67;
		public $colour = "Blue";
		public $country = "Japan";
		public $bhp = 146;
		}
	$racer = new Bike;
	foreach($racer as $key => $value) {
		echo "$key = $value<br>";
	}
?>

Save & refresh browser:

make = Yamaha
model = YZFR1
cost = 12999
mpg = 67
colour = Blue
country = Japan

bhp = 146

 

 

To access private/protected properties, the foreach loop is moved into the class as a method:

<?php
	class Bike {
		public $make = "Yamaha";
		public $model = "YZFR1";
		public $cost = 12999;
		private $topSpeed = 165;
		public $mpg = 67;
		public $colour = "Blue";
		public $country = "Japan";
		protected $bhp = 146;

		public function display(){
			foreach($this as $key => $value){
				echo "$key = $value<br>";
			}
		}
	}
	$racer = new Bike;
	$racer->display();
?>

Save & refresh browser:

make = Yamaha
model = YZFR1
cost = 12999
topSpeed = 165
mpg = 67
colour = Blue
country = Japan
bhp = 146

 

Since the object itself is iterating through it's own properties, private and protected properties can now be accessed.

Object type information

The keywords instanceof and is_subclass_of are used to return a boolean TRUE or FALSE value when an object is evaluated against a class name.

 

instanceof will return TRUE if the object belongs to the specified class, or a descendant of that class.

 

Syntax:

$myObject instanceof MyClass;

 

This example creates two objects and tests whether they are instances of the Parent or Child class:

<?php
	class Bike {}
	class MotoGP extends Bike {}

	$r1 = new Bike;
	$m1 = new MotoGP;

	if ($r1 instanceof Bike){
		echo "The r1 object belongs to the Bike class<br>";
	} else {
		echo "The r1 is not a Bike class object<br>";
	}

	if ($m1 instanceof Bike){
		echo "The m1 object belongs to the Bike class<br>";
	} else {
		echo "The m1 is not a Bike class object<br>";
	}

	if ($r1 instanceof MotoGP){
		echo "The r1 object belongs to the BikeMotoGP class<br>";
	} else {
		echo "The r1 is not a MotoGP class object<br>";
	}

	if ($m1 instanceof MotoGP){
		echo "The m1 object belongs to the MotoGP class<br>";
	} else {
		echo "The m1 is not a MotoGP class object<br>";
	}
?>

Save & refresh browser:

The r1 object belongs to the Bike class
The m1 object belongs to the Bike class
The r1 is not a MotoGP class object
The m1 object belongs to the MotoGP class

 

It can be seen that the m1 is a Bike object, since it is inherited from the Bike class. Similarly, it can be seen that the r1 object is not a MotoGP object.

 

To determine is an object is just from a child class is_subclass_of will return TRUE if the object is a descendant of the specified class.

 

Syntax:

is_sub_class($myObject, MyClass);

 

This example creates two objects and tests whether they are in sub classes of the the Parent or Child class:

<?php
	class Bike {}
	class MotoGP extends Bike {}

	$r1 = new Bike;
	$m1 = new MotoGP;

	if (is_subclass_of ($r1, Bike)){
		echo "The r1 object IS in a sub class of the Bike class<br>";
	} else {
		echo "The r1 object is NOT in a sub class of the Bike class<br>";
	}

	if (is_subclass_of ($m1, Bike)){
		echo "The m1 object IS in a sub class of the Bike class<br>";
	} else {
		echo "The m1 object is NOT in a sub class of the Bike class<br>";
	}

	if (is_subclass_of ($r1, MotoGP)){
		echo "The r1 object IS in a sub class of the MotoGP class<br>";
	} else {
		echo "The r1 object is NOT in a sub class of the MotoGP class<br>";
	}

	if (is_subclass_of ($m1, MotoGP)){
		echo "The m1 object IS in a sub class of the MotoGP class<br>";
	} else {
		echo "The m1 object is NOT in a sub class of the MotoGP class<br>";
	}
?>

Save & refresh browser:

The r1 object is NOT in a sub class of the Bike class
The m1 object IS in a sub class of the Bike class
The r1 object is NOT in a sub class of the MotoGP class
The m1 object is NOT in a sub class of the MotoGP class

 

As expected only the m1 object is in a sub class

Type hinting

To ensure only the correct data-type objects (i.e. of the correct class) are used when passed into a method, a technique referred to as type hinting can be used. If the wrong type of object is then passed into that method a fatal error will result, as demonstrated in the following example:

<?php
	class Bike {
		public function display(){
			echo "Bikes are fun!<br>";
		}
	}

	class Car {}

	function output(Bike $aprilia){
		$aprillia->display;
	}

	$mille = new Car;
	output($mille);
?>

Save & refresh browser:

Catchable fatal error: Argument 1 passed to output() must be an instance of Bike, instance of Car given, called in /root/user/public_html/php/index.php on line 15 and defined in /root/user/public_html/php/index.php on line 10

 

Since the $mille object is of the Car class type, it causes an error when the output() function is used, which expects a Bike class type, as can be seen on line 10 above.

 

The use of class type hints overcomes the need for separate instanceof functions, and are simple way to simplify and resolve bugs.

Object Cloning

Sometimes it might be necessary to make a copy of an object, say for backup purposes or when you don't want to effect the state of the original object.

 

To make a copy of an object the keyword clone is used.

 

Syntax:

$copyObject = clone $originalObject;

 

clone can also be used directly within a function's parentheses to pass a copy of the passed in object to the function. In this case $originalObject is left intact and myFunction() acts upon the clone:

 

myFunction(clone $originalObject);

 

 

Once the cloning is complete, if a __clone() method is defined, then the newly created object's __clone() method will be called from the original class, to allow any necessary properties that need to be changed. Also note, an object's __clone() method cannot be called directly.

 

  • clone copies all variables from first object to new object
  • then calls __clone() magic method, from the class it is copying from
    • akin to a constructor for the cloned object

 

In this example an object is instantiated and then assigned to a new object using the clone keyword. Finally, it uses the clone keyword within a function which makes a cloned copy of the passed in object for use within the function:

<?php
	class MyObject {

		public $name = "Original";
		static $instances = 0;
		public $instance;

		public function __construct(){
			echo "$this->name object Instantiated!<br>";
		}

		public function __clone(){
			$this->instance = ++self::$instances;
			echo "Clone " . $this->instance . " copied.<br>";
		}
	}

	function myFunction(){
		echo "This function will firstly clone the object being passed in, ";
		echo "it will then use the __clone() magic method ";
		echo "in the original object's class, ";
		echo "and finally it will print this message.<br>";
	}

	$originalObject = new MyObject;

	$copyObject = clone $originalObject;

	myFunction(clone $copyObject);
?>

Save & refresh browser:

Original object Instantiated!
Clone 1 copied.
Clone 2 copied.
This function will firstly clone the object being passed in, it will then use the __clone() magic method in the class, and finally it will print this message.

Comparing Objects

Objects can be compared using:

 

  • Comparison operator ==
    • Checks if the properties are the same 
  • Identity operator ===
    • Also checks if instances of the same class

This example creates a new object, clones it and then copies the original object using a simple assignment:

<?php
	class Person {

		public $name;
		public $now ;

		public function __construct() {
			echo "\$geek object instantiated.<br><br>";
			$this->now = time();
		}
		public function __clone(){
			//adding 1 to time to make cloned objects have a different $now value
			$this->now = time()+1;
		}
	}

	$geek = new Person;
	$geek->name = "Will";
	$techie = clone $geek;
	$imposter = $geek;

	if ($geek == $techie) {
		echo "\$geek and \$techie objects compare.<br>";
		} else {
			echo "\$geek and \$techie objects don't compare.<br>";
		}
	if ($geek == $imposter) {
		echo "\$geek and \$imposter objects compare.<br>";
		} else {
			echo "\$geek and \$imposter objects don't compare.<br>";
		}
	if ($geek === $techie) {
		echo "\$geek and \$techie objects are identical.<br>";
		} else {
			echo "\$geek and \$techie objects are not identical.<br>";
		}
	if ($geek === $imposter) {
		echo "\$geek and \$imposter objects are identical.<br>";
		} else {
			echo "\$geek and \$imposter objects are not identical.<br>";
		}

	echo "<pre>" . var_export($geek, TRUE) . "</pre>" ;
	echo "<pre>" . var_export($techie, TRUE) . "</pre>" ;
	echo "<pre>" . var_export($imposter, TRUE) . "</pre>" ;

?>

Save & refresh browser:

$geek object instantiated. 

 

$geek and $techie objects don't compare.
$geek and $imposter objects compare.
$geek and $techie objects are not identical.
$geek and $imposter objects are identical.

 

Person::__set_state(array(
'name' => 'Will',
'now' => 1372934803,
))

 

Person::__set_state(array(
'name' => 'Will',
'now' => 1372934804,
))

 

Person::__set_state(array(
'name' => 'Will',
'now' => 1372934803,
))

Saving Objects - Serialization

To save objects the serialize() function is used to convert them into a format that can be saved, like so:

 

$target = serialize($source);

 

This can now be saved to a file using the file_put_contents() function, like so:

 

file_put_contents('path/file.txt', $target);

 

To read the object the complementary functions unserialize() and file_get_contents are utilised, like so:

 

$target = file_get_contents('path/file.txt'); //assigns the file contents

$source = unserialize($target); //converts back to the correct format

 

 

The following example is broken into 3 separate files. First we have the index.php file:

<?php
	include('person.inc');

	$sharon = new Person;
	$sharon->name = "Sharon";
	$serialSharon = serialize($sharon);
	file_put_contents('./file.txt', $serialSharon);

	echo "Sharon object has been stored in the 'file.txt' file.<br>";

	echo "Click <a href=\"read.php\">here</a> to go to the retrieving page that reads the Sharon object from 'file.txt'.<br>";

	echo "<pre>" . var_export($sharon, TRUE) . "</pre>" ;
?>

*note: the var_export() function is being used to show the contents of the object

 

Next is the person.inc file:

<?php
	class Person {

		public $name;
		public $created;

		public function __construct(){
			$this->created = time();
			echo "Object instantiated at " . $this->created . "<br>";
		}
		public function display(){
			echo "Hi, my name is $this->name and I was created at $this->created.<br>";
		}
	}
?>

Save & refresh browser:

Object instantiated at 1372945579
Sharon object has been stored in the 'file.txt' file.
Click here to go to the retrieving page that reads the Sharon object from 'file.txt'. 

 

Person::__set_state(array(
'name' => 'Sharon',
'created' => 1372945579,
))

 

 

Finally, we have the read.php file:

<?php
	include('person.inc');

	echo "The Sharon object will now be read from 'file.txt'.<br>";

	$unserialSharon = file_get_contents('./file.txt');
	$readIn = unserialize($unserialSharon);

	$readIn->display();

	echo "<pre>" . var_export($readIn, TRUE) . "</pre>" ;
?>

*note: the var_export() function is being used to show the contents of the object

 

Save & refresh browser:

The Sharon object will now be read from 'file.txt'.
Hi, my name is Sharon and I was created at 1372945579. 

 

Person::__set_state(array(
'name' => 'Sharon',
'created' => 1372945579,
))

 

 

Note: static members of an object are not serialized

 

 

 

When saving and reading objects using serialize() and unserialize, they automatically look for __sleep() and __wakeup() magic methods for their respective function. You will need to provide these magic methods yourself and are used to perform cleanups of the object before being saved or read.

Magic Methods

These are builtin methods provided by PHP that allow additional functionality in OOP.

 

  • start with two underscores e.g. __construct()
  • automatically called under specific conditions

 

So far the above examples have used the following:

  • __construct()
  • __destruct()
  • __clone()
  • __sleep()
  • __wakeup()

 

The following examples now explore the remaining major magic methods:

  • __autload()
  • __get()
  • __set()
  • __call()
  • __tostring()

 

__autoload()

Sometimes there may be a large number of include files required for multiple classes, which could become unmanageable as the number of separate class include files grows, and you might not want to include every file 'just in case'.

 

PHP has a facility to automatically load files by using the __autoload() function, that attempts to load a (perhaps forgotten include) file based on the name of the object trying to be created.

 

A typical __autoload() function syntax:

 

function __autoload($class_name) {

include 'class.' . $class_name . 'inc';

}

 

If a new object were trying to be created, say using $fido = new Dog; and that class had not been defined/included, then the __autoload function would attempt to load class.Dog.inc

 

*note: the autoloading filename is case sensitive and may require the strtolower() function, if your file names are in lower case.

 

Since the autoloading filename is case sensitive, this example uses the previous person.inc and therefore requires the strtolower() function to convert the Person class name to lower case for the person.inc filename:

<?php
//	include('person.inc');

	function __autoload($class_name){
		$target = strtolower($class_name);
		echo "Attempting to load $target.inc<br>";
		include('./' . $target . '.inc');
	}

	$jim = new Person;
	$jim->name = "Jim";
	$jim->display();

	echo "<pre>" . var_export($jim, TRUE) . "</pre>" ;
?>

Save & refresh browser:

Attempting to load person.inc
Object instantiated at 1372952352
Hi, my name is Jim and I was created at 1372952352.
 

Person::__set_state(array(
'name' => 'Jim',
'created' => 1372952352,
))

 


__get()

Used within a class when an attempt is made to access a property that isn't accessible e.g. if it doesn't have public visibility or doesn't exist.

<?php
	class Person {
		private $name = "James Tiberius Kirk";

		public function __get($var){
			//note using variable variable!!! i.e. the $var after $this->!!!
			echo " __get() magic method called on $var: ";
			return $this->$var . "<br>";
		}
	}
	$jim = new Person;

	echo "Access to private property using the ";
	echo $jim->name;

	echo $jim->age;

	echo "<pre>" . var_export($jim, TRUE) . "</pre>" ;
?>

Save & refresh browser:

Access to private property using the __get() magic method called on name: James Tiberius Kirk
__get() magic method called on age:
 

Person::__set_state(array(
'name' => 'James Tiberius Kirk',
))

 


__set()

Used within a class when a call is made to set a property that isn't accessible e.g. if it doesn't have public visibility or doesn't exist.

<?php
	class Person {
		private $name = "James Tiberius Kirk";

		public function __set($key, $value){
		echo " __set() magic method called!<br>";
			$this->data[$key] = $value;
		}
		public function __get($var){
			//note using variable variable!!! i.e. the $var after $this->!!!
			echo " __get() magic method called on $var: ";
			return $this->$var . "<br>";
		}
	}
	$spock = new Person;
	echo $spock->name;

	echo "Access to private property";
	echo $spock->name = "Mr Spock";

	echo "<pre>" . var_export($spock, TRUE) . "</pre>" ;
?>

Save & refresh browser:

__get() magic method called on name: James Tiberius Kirk
Access to private property __set() magic method called!
__get() magic method called on data: Mr Spock
 

Person::__set_state(array(
'name' => 'James Tiberius Kirk',
))

 


__call()

Used within a class when a call is made to a method that isn't accessible e.g. if it doesn't have public visibility or doesn't exist.

 

This example has commented out the fly() method, on lines 5-8, to invoke the __call() magic method:

<?php
	class Person {
		public $Name;

		public function hello() {
			echo "Hello everybody!<br>";
		}

/*		public function fly() {
			print "People can't fly!<br>";
		}*/

		public function __call($function, $args) {
			$args = implode(', ', $args);
			echo "Sorry, $this->name, Call to $function() with args '$args' failed!\n";
		}
	}

	$clark = new Person;
	$clark->name = "Peter Parker";
	$clark->fly("Only", "Superman", "can", "fly!");
?>

Save & refresh browser:

Sorry, Peter Parker, Call to fly() with args 'Only, Superman, can, fly!' failed!

 


__tostring()

Defined within a class to decide what an object should do when treated like a string (e.g. echo $myObject;) and must return a string:

<?php
	class Person {
		public $Name;

		public function __construct($myName){
			$this->name = $myName;
		}

		public function __toString() {
			return "Hi $this->name, here's the string for the new " . __CLASS__ . " object!";
		}
	}

	$spidey = new Person("Peter Parker");

	echo $spidey;
?>

Save & refresh browser:

Hi Peter Parker, here's the string for the new Person object!

Class/Object Functions

PHP has many inbuilt functions to help with your programming needs. Here's an example script with a few to be getting on with:

<?php
	class Person {
		public $name = "Superman";
		public $age = "99";

		public function __construct($myName, $myAge){
			$this->name = $myName;
			$this->age = $myAge;
		}
		public function greet(){
			echo "$this->name says hi!";
		}
	}
	echo "Create an object instance, calling its constructor to set the name.<br><br>";
	$spidey = new Person("Peter Parker", 42);

	echo "echo the name property: <br>";
	echo "$spidey->name <br><br>";

	echo "Return a boolean if the class exists or not: " . class_exists("Person") . "<br><br>"; //returns boolean

	echo "echo the methods within the class:<br>";
	$myClassMethods = get_class_methods('Person'); //returns array of class methods
	foreach($myClassMethods as $methodName){
		echo $methodName . "<br>";
	}
	echo "<br>";

	echo "echo the properties within the class:<br>";
	$myClassProperties = get_class_vars('Person');//returns an associative array of class properties
	foreach($myClassProperties as $propertyName => $value){
		echo "$propertyName = $value<br>";
	}
	echo "<br>";

	echo "\$spidey was created using the " . get_class($spidey) . " class<br><br>";

	echo "Display an array of the names of the declared classes in the script<br>";
	echo "Depending on what extensions you have compiled or loaded into PHP, additional classes could be present!<br>";
	var_dump(get_declared_classes());
	echo "<br><br>";

	echo "<pre>" . var_export($spidey, TRUE) . "</pre>" ;
?>

Interfaces

Interfaces provide a way of implementing multiple inheritance (not directly available in PHP) through the use of the keywords interface and implements.

 

Potentially, one could inherit from a parent class, and then inherit from that class and so in a chain like fashion. However, this would likely mean a number of the parents, parents, parents, etc, members might be included that might not be required, and avoids needing a very long trail like this:

class Test implements InterfaceA, InterfaceB, InterfaceC, InterfaceD, InterfaceE......InterfaceZ {}

 

Interfaces:

  • 100% abstract classes
  • Cannot be instantiated
  • Contain just the method names, not their definitions (i.e. the inner guts of their code)
  • Act like a contract to the class implementing the specified interface
  • All method names within an interface have public visibility
  • The implementing class must define the methods

The interface simply uses the keyword interface, which is then used by the class using the interface by way of the keyword implements:

<?php
	interface person {
		function setName($myName);
		function getName();
		function setAge($myAge);
		function getAge();
	}
	interface scientist{
		function measure();
		function writePaper();
	}
	class Geek implements person, scientist {
		public $name;
		public $age;
		public function setName($myName){
			$this->name = $myName;
		}
		public function getName(){
			return $this->name ;
		}
		public function setAge($myAge){
			$this->age = $myAge;
		}
		public function getAge(){
			return $this->age ;
		}
		public function measure(){
			echo "$this->name has just made a measurement!<br>";
		}
		public function writePaper(){
			echo "$this->name has written a paper!<br>";
		}
	}
	$steve = new Geek();
	$steve->setName("Stephen Hawking");
	$steve->setAge(71);
	echo "The guy who invented big bangs: ". $steve->getName() . " is now " . $steve->getAge() . " years old!<br>";
	$steve->measure();
	$steve->writePaper();
	echo "<pre>" . var_export($steve, TRUE) . "</pre>" ;
?>

Save & refresh browser:

The guy who invented big bangs: Stephen Hawking is now 71 years old!
Stephen Hawking has just made a measurement!
Stephen Hawking has written a paper! 

 

Geek::__set_state(array(
'name' => 'Stephen Hawking',
'age' => 71,
))

constants

Constant values can be defined within a class using the keyword const.

 

Syntax:

const myConstant = "Constant Value";

 

  • Remain the same and unchangeable
  • Do not use the $ symbol
  • Must be a constant expression
    • not a class's property, result of a mathematical operation or a function call

Example showing a const being defined within the class, then being called from within the class and again externally:

<?php
	class MyClass
	{
		const myConstant = 'constant value';

		public function showConstant() {
			echo  self::myConstant . "<br>";
		}

		public function __construct(){
			echo "Constructor: " . self::myConstant . "<br>";
		}
	}

	$test = new MyClass ;

	$test->showConstant();
?>

Save & refresh browser:

Constructor: constant value
constant value

URL Functions

A few functions that work with URL strings to encode, decode, parse and generally make sure they are in the correct format for transmission over the internet.

 

urlencode()

Since URLs can only be transmitted over the internet using ASCII characters, it is often necessary to convert non-alphanumeric characters to their ASCII coded equivalent

  • Encodes a string to be used in query part of URL
  • Convenient way to pass variables to other pages
  • Returns string in which all non-alphanumeric characters except -_. have been replaced by the percent sign followed by two hex digits and spaces encoded as +
<?php
	echo $myString = "What's this doing here?";
	echo "<br>" . urlencode($myString) . "<br>";
	echo '<a href="someService?myVar=', urlencode($myString), '">test</a>';
?>

Save & refresh browser:

What's this doing here?
What%27s+this+doing+here%3F
test

 

Mousing over the test url will show the non-alphanumeric characters in the link have been converted to their ASCII equivalent codes:

https://tech-academy.co.uk/someService?myVar=What%27s+this+doing+here%3F

 

 

urldecode()

  • Decodes any ASCII two digit hex %## encoding in the given string
  • Plus symbols ('+') are decoded to a space character
<?php
	echo $yourString = "https://tech-academy.co.uk/someService?myVar=What%27s+this+doing+here%3F";
	echo "<br>" . urldecode($yourString) . "<br>";
?>

Save & refresh browser:

https://tech-academy.co.uk/someService?myVar=What%27s+this+doing+here%3F
https://tech-academy.co.uk/someService?myVar=What's this doing here?

 

 

rawurlencode()

  • Similar to above but converts spaces to %20

rawurldecode()

  •  Decodes above rawurlencoded string
<?php
	echo $myString = "Isn't this a string with % non-alphanumeric $ * £ characters?";
	$raw = rawurlencode($myString);
	echo "<br>" . $raw . "<br>";
	echo '<a href="someService?myVar=', $raw , '">test</a><br>';

	echo rawurldecode($raw);
?>

Save & refresh browser:

Isn't this a string with % non-alphanumeric $ * £ characters?
Isn%27t%20this%20a%20string%20with%20%25%20non-alphanumeric%20%24%20%2A%20%C2%A3%20characters%3F
test
Isn't this a string with % non-alphanumeric $ * £ characters?

 

Mousing over the test url will show the non-alphanumeric characters in the link have been converted to their ASCII equivalent codes:

https://derrickrobinson.com/php/someService?myVar=Isn%27t%20this%20a%20string%20with%20%25%20non-alphanumeric%20%24%20%2A%20%C2%A3%20characters%3F

 

base64_encode() / base64_decode()

  • Encodes the given data with base64
  • This encoding is designed to make binary data survive transport through transport layers that are not 8-bit clean, such as mail bodies
  • Base64-encoded data takes about 33% more space than the original data
<?php
	echo $myString = 'This string is being base64 encoded / decoded <br>' ;

	$b64enc = base64_encode($myString) ;
	echo $b64enc . "<br>" ;

	echo "<br>Now decoding:<br>";
	$b64dec = base64_decode($b64enc);
	echo $b64dec . "<br>";
?>

Save & refresh browser:

This string is being base64 encoded / decoded
VGhpcyBzdHJpbmcgaXMgYmVpbmcgYmFzZTY0IGVuY29kZWQgLyBkZWNvZGVkOiA8YnI+

 

Now decoding:
This string is being base64 encoded / decoded

 

More URL functions here.

Exception Handling

Exception handling is carried out by the use of the keywords throw, trycatch.

 

An exception can be thrown and caught (catched).

 

  • Code may be surrounded in a try block, to facilitate the catching of potential exceptions
  • Each try must have at least one corresponding catch block
  • Multiple catch blocks can be used to catch different classes of exceptions
  • Normal execution (when no exception is thrown within the try block, or when a catch matching the thrown exception's class is not present) will continue after that last catch block defined in sequence
  • Exceptions can be thrown (or re-thrown) within a catch block.

 

  • When an exception is thrown, code following the statement will not be executed, and PHP will attempt to find the first matching catch block
  • If an exception is not caught, a PHP Fatal Error will be issued with an "Uncaught Exception ..." message, unless a handler has been defined with set_exception_handler().
  • In PHP 5.5 and later, a finally block may also be specified after the catch blocks
  • Code within the finally block will always be executed after the try and catch blocks, regardless of whether an exception has been thrown, and before normal execution resumes.
  • The thrown object must be an instance of the Exception class or a subclass of Exception. Trying to throw an object that is not will result in a PHP Fatal Error.
<?php
	//create function with an exception
	function checkNum($number) {
		if($number>1) {
			throw new Exception("Value must be 1 or below");
		}
		return true;
	}

	//trigger exception in a "try" block
	try {
		checkNum(2);
		//If the exception is thrown, this text will not be shown
		echo 'If you see this, the number is 1 or below';
	}

	//catch exception
	catch(Exception $e){
		echo 'Message: ' .$e->getMessage();
	}
?>

Save & refresh browser:

If you see this, the number is 1 or below

PDO

PHP Data Objects, provide database access standardisation (some call this an access abstraction layer) that allows portable code to be used across multiple databases and platforms. Meaning, the same functions to perform queries and fetch data are used regardless of the type of database being used.

 

3 classes:

  • PDO class maintains database connection
  • PDOStatement class, handles SQL queries
  • PDOException class, provides error handling

 

PDO class

requires 3 pieces of information:

  • database location
  • username
  • password

 

The database location is in the form of a DSN (Data Source Name) which conforms to a specific format and is slightly different for each type of database.

 

To determine the available database drivers on your system:

<?php
foreach(PDO::getAvailableDrivers() as $driver)
    {
    echo $driver.'<br />';
    }
?>

Save & refresh browser:

sqlite 

sqlite2

mysql

 

 

DSN syntax:

database_type:host_location;database_name;

 

A simple connection could then be made using:

$dsn = "mysql:host=localhost;dbname=test_db";
$pdo = new PDO($dsn, $username, $password, $opts);

 

 

For the purpose of these PDO articles, I will be using my motogp database and the riders table, as follows:

 

CREATE TABLE 'riders' (
'id' mediumint(8) NOT NULL AUTO_INCREMENT,
'name' varchar(25) COLLATE utf8_unicode_ci NOT NULL,
'team' varchar(25) COLLATE utf8_unicode_ci NOT NULL,
'points' mediumint(8) NOT NULL,
'status' varchar(28) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY ('id')
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ; 

 

INSERT INTO 'riders' ('id', 'name', 'team', 'points', 'status') VALUES
(1, 'Dani Pedrosa', 'Honda', 136, 'Factory'),
(2, 'Jorge Lorenzo', 'Yamaha', 127, 'Factory'),
(3, 'Marc Marquez', 'Honda', 113, 'Factory'),
(4, 'Cal Crutchlow', 'Yamaha', 87, 'Factory'),
(5, 'Valentino Rossi', 'Yamaha', 85, 'Factory'),
(6, 'Andrea Dovizioso', 'Ducati', 65, 'Factory'),
(7, 'Stefan Bradl', 'Honda', 51, 'Factory'),
(8, 'Nicky Hayden', 'Ducati', 50, 'Factory'),
(9, 'Alvaro Bautista', 'Honda', 47, 'Factory'),
(10, 'Aleix Espargaro', 'ART', 44, 'CRT');

 

We'll now connect to the above database table and perform a simple query:

<?php
	$host = 'localhost';
	$dbname = 'motogp';
	$dsn = "mysql:host=$host;dbname=$dbname";
	$username = "testing";
	$password = "secret";

	$dbh = new PDO($dsn, $username, $password);//database handle

	echo "Connected to the $dbname database<br>";

	$sqlQuery = 'SELECT * from riders';//select everything from the riders table

	foreach($dbh->query($sqlQuery) as $row) {
		echo $row['rider_name'] . " rides for " . $row['rider_team'] . "<br>";
	}
	$dbh = null; //close connection
?>

Save & refresh browser:

Connected to the motogp database
Dani Pedrosa rides for Honda
Jorge Lorenzo rides for Yamaha
Marc Marquez rides for Honda
Cal Crutchlow rides for Yamaha
Valentino Rossi rides for Yamaha
Andrea Dovizioso rides for Ducati
Stefan Bradl rides for Honda
Nicky Hayden rides for Ducati
Alvaro Bautista rides for Honda
Aleix Espargaro rides for ART

PDO Exceptions

A PDOException will be thrown if there are any connection errors. These can be caught in a try/catch block.

 

The PDO::setAttribute() method is used to set the error reporting level as follows:

  • PDO::ATTR_ERRMODE: Error reporting.
    • PDO::ERRMODE_SILENT: Just set error codes.
    • PDO::ERRMODE_WARNING: Raise E_WARNING.
    • PDO::ERRMODE_EXCEPTION: Throw exceptions.

Syntax:

$object->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

 

Exception methods can now be used:

getCode()

getMessage()

getTrace()

getFile()

getLine()

returns error code

returns the exception message

returns the exception stack trace as an array

returns the name of the file the exception was created

returns the line number where the exception was created

 

For this example, to show the connection error being caught, I have changed the username to potato:

<?php
	$host = 'localhost';
	$dbname = 'motogp';
	$dsn = "mysql:host=$host;dbname=$dbname";
	$username = "potato";
	$password = "secret";
	try {
		$dbh = new PDO($dsn, $username, $password); //database handle
		$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //set error reporting level

		echo "Connected to the $dbname database<br>";

		$sqlQuery = 'SELECT * from riders';//select everything from the riders table

		foreach($dbh->query($sqlQuery) as $row) {
			echo $row['rider_name'] . " rides for " . $row['rider_team'] . "<br>";
		}
		$dbh = null; //close connection
	}
	catch (PDOException $error) { //catch PDOException
		echo "Error!: " . $error->getMessage() . "<br>";
		file_put_contents( './dbErrors.txt', $error->getMessage(), FILE_APPEND );
		die("Can't Connect!");
	}
?>

Save & refresh browser:

Error!: SQLSTATE[28000] [1045] Access denied for user 'potato'@'localhost' (using password: YES)

Organising Files

Whilst not strictly a PDO concept, I am showing the use of placing some of the code into external files that are then included.

 

This will keep the examples slightly shorter and show modularity.

 

First the connection to the database:

<?php
	$db_host = 'localhost';
	$db_name = 'motogp';
	$dsn = "mysql:host=$db_host;dbname=$db_name";
	$db_username = "testing";
	$db_password = "secret";
	$dbh = new PDO($dsn, $db_username, $db_password);
	$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	if(!$dbh){
		throw PDOException;
	} else {
		echo "Connected to the $db_name database<br>";
	}
?>

Save & refresh browser:

Connected to the motogp database

 

Changing any of the connection variables throws an exception :

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000] [2005] Unknown MySQL server host 'xxxxxxx' (25)' in /path/user/public_html/pdo/connect.php:7 Stack trace: #0 /path/user/public_html/pdo/connect.php(7): PDO->__construct('mysql:host=xxxx...', 'testing', 'secret') #1 {main} thrown in /path/user/public_html/pdo/connect.php on line 7

 

 

Now the error handling:

<?php
	function errorHandling(Exception $error) {
		echo "Error!<br/>";

		$diag = $error->getTrace();
		if($diag[0]['class'] != ""){
			$diagClass = $diag[0]['class'];
		}
		$func = $diag[0]['function'];
		$file = $diag[0]['file'];
		$line = $diag[0]['line'];
		$errorMessage = $error->getMessage() . "<br>" .
		"Class & Method: " . $diagClass . " & " . $func . "<br>" .
		"File: " . $file . "<br>" .
		"Line: " . $line . "<br>";

		echo $errorMessage;

		file_put_contents( './dbErrors.txt', $errorMessage, FILE_APPEND );
	}
?>

 

The above files can now be included where required and provide neater more readable code:

<?php
//turn errors off (since we're doing it ourselves in error_handling.php
	ini_set('display_errors', '0');

	//connect to the database
	include_once('connect.php');
	//make sure to use the customised error handling
	include_once('error_handling.php');

	//now do a query
	try{
		$sqlQuery = 'SELECT * from riders';//select everything from the riders table

		foreach($dbh->query($sqlQuery) as $row) {
			echo $row['name'] . " rides for " . $row['team'] . "<br>";
		}
	}
	catch (PDOException $error) { //catch PDOException
		echo "Error!: " . $error->getMessage() . "<br/>";
		file_put_contents( './dbErrors.txt', $error->getMessage() . "\n", FILE_APPEND );
	}
?>

Save & refresh browser:

Connected to the motogp database
Dani Pedrosa rides for Honda
Jorge Lorenzo rides for Yamaha
Marc Marquez rides for Honda
Cal Crutchlow rides for Yamaha
Valentino Rossi rides for Yamaha
Andrea Dovizioso rides for Ducati
Stefan Bradl rides for Honda
Nicky Hayden rides for Ducati
Alvaro Bautista rides for Honda
Aleix Espargaro rides for ART

PDO Prepared Statements

Precompiled SQL statement that uses the prepare() PDO method that is then executed by the execute() method.

 

Line 10 shows an SQL query being assigned to a variable which is then assigned to the $statement variable using the prepare() method on line 13. The prepared statement is then executed on line 16:

<?php
	ini_set('display_errors', '0');
	include_once('connect.php');
	include_once('error_handling.php');

	$team = 'Honda';
	$status = 'Factory';

	//assign the query to a variable
	$sqlQuery = "SELECT * from riders where team = '$team' AND status = '$status'";

	//now use the prepare() method & assign to $statement to create a prepared statement
	$statement = $dbh->prepare($sqlQuery);

	try{
		$statement->execute();
	}
	catch(PDOException $error){
		echo errorHandling($error);
	}
	while($row = $statement->fetch(PDO::FETCH_ASSOC)) {
		echo $row['name'] . " rides a " . $row['team'] . " " . $row['status'] . " MotoGP bike!<br>";
	}
?>

Save & refresh browser:

Connected to the motogp database
Dani Pedrosa rides a Honda Factory MotoGP bike!
Marc Marquez rides a Honda Factory MotoGP bike!
Stefan Bradl rides a Honda Factory MotoGP bike!
Alvaro Bautista rides a Honda Factory MotoGP bike!

 

*note: the fetch() method is now being used in the while loop on line 21, in place of our previous query() method in the foreach loop

PDO Placeholders

To obtain the full benefit of prepared statements placeholders are used within the prepared statement.

 

Placeholders protect against SQL injection, since the data never gets inserted into the SQL query.

 

There are two types of placeholders: named and unnamed

 

The following list shows a comparison of 3 examples:

 

  • No placeholders - ripe for SQL Injection!
    • $statement = $dbh->("INSERT INTO riders (name, team, status) values ($name, $team, $status)");
  • Named placeholders
    • $statement = $dbh->("INSERT INTO riders (name, team, status) value (:name, :team, :status)");
    • the named placeholders are in the rightmost parentheses, and take the form of a colon : followed by the placeholder name without quotes
  • Unnamed placeholders
    • $statement = $dbh->("INSERT INTO riders (name, team, status) values (?, ?, ?);
    • the unnamed placeholders are in the rightmost parentheses, and take the form of a question mark ?

 

 

When using placeholders, the variables need to be bound (/associated) to the placeholders using the bindParam() method (binds a parameter to a variable) for named placeholders or the bindValue() method (binds a value to a parameter) for unnamed placeholders.

 

This example shows the named placeholders within the query on line 10, which is then used in the prepared statement on line 13 and bound to the variables on lines 16 & 17:

<?php
	ini_set('display_errors', '0');
	include_once('connect.php');
	include_once('error_handling.php');

	$team = 'Yamaha';
	$status = 'Factory';

	//Named placeholder, note colon before variable & no quotation marks
	$sqlQuery = "SELECT * from riders where team = :team AND status = :status";

	//now use the prepare method & assign to $statement to create a prepared statement
	$statement = $dbh->prepare($sqlQuery);

	//the variables now need to be bound (/associated) to the named placeholders
	$statement->bindParam(':team', $team, PDO::PARAM_STR);
	$statement->bindParam(':status', $status, PDO::PARAM_STR);

	try{
		$statement->execute();
	}
	catch(PDOException $error){
		echo errorHandling($error);
	}
	while($row = $statement->fetch(PDO::FETCH_ASSOC)) {
		echo $row['name'] . " rides a " . $row['team'] . " " . $row['status'] . " MotoGP bike!<br>";
	}
?>

Save & refresh browser:

Connected to the motogp database
Jorge Lorenzo rides a Yamaha Factory MotoGP bike!
Cal Crutchlow rides a Yamaha Factory MotoGP bike!
Valentino Rossi rides a Yamaha Factory MotoGP bike!

 

 

The same example this time showing unnamed placeholders within the query on line 10, which is then used in the prepared statement on line 13 and bound to the variables on lines 16 & 17:

<?php
	ini_set('display_errors', '0');
	include_once('connect.php');
	include_once('error_handling.php');

	$team = 'Yamaha';
	$status = 'Factory';

	//Named placeholder, note colon before variable & no quotation marks
	$sqlQuery = "SELECT * from riders where team = ? AND status = ?";

	//now use the prepare method & assign to $statement to create a prepared statement
	$statement = $dbh->prepare($sqlQuery);

	//the variables now need to be bound (/associated) to the unnamed placeholders
	$statement->bindValue('1', $team, PDO::PARAM_STR); //1st placeholder bound to $team
	$statement->bindValue('2', $status, PDO::PARAM_STR); //2nd placeholder bound to $status

	try{
		$statement->execute();
	}
	catch(PDOException $error){
		echo errorHandling($error);
	}
	while($row = $statement->fetch(PDO::FETCH_ASSOC)) {
		echo $row['name'] . " rides a " . $row['team'] . " " . $row['status'] . " MotoGP bike!<br>";
	}
?>

Save & refresh browser:

Connected to the motogp database
Jorge Lorenzo rides a Yamaha Factory MotoGP bike!
Cal Crutchlow rides a Yamaha Factory MotoGP bike!
Valentino Rossi rides a Yamaha Factory MotoGP bike!

PDO Binder Function

Since the number of variables to be bound could potentially become quite large, a function can be used to build the bindValue() methods for the prepared statements.

 

First an associative array is created with the variables being associated as the values as the keys:

 

$myArr = array('team'=>$team, 'status'=>$status);

 

The array is then used within a foreach loop to build the prepared statements:

 

foreach($myArr as $key=>$value){

$statement->bindValue(':'.$key,$value);

}

 

Putting this altogether we now have a function that will build the binding prepared statements:

<?php
	ini_set('display_errors', '0');
	include_once('connect.php');
	include_once('error_handling.php');

//	include_once('pdoBinder.php');

	$team = 'Ducati';
	$status = 'Factory';

	//Named placeholder, note colon before variable & no quotation marks
	$sqlQuery = "SELECT * from riders where team = :team AND status = :status";

	//now use the prepare method & assign to $statement to create a prepared statement
	$statement = $dbh->prepare($sqlQuery);

	//create an associative array
	$myArr = array('team'=>$team, 'status'=>$status);

	//use above array to build the bind prepared statements
	foreach($myArr as $key=>$value){
		$statement->bindValue(':'.$key,$value);
	}
	try{
		$statement->execute();
	}
	catch(PDOException $error){
		echo errorHandling($error);
	}
	while($row = $statement->fetch(PDO::FETCH_ASSOC)) {
		echo $row['name'] . " rides a " . $row['team'] . " " . $row['status'] . " MotoGP bike!<br>";
	}
?>

Save & refresh browser:

Connected to the motogp database
Andrea Dovizioso rides a Ducati Factory MotoGP bike!
Nicky Hayden rides a Ducati Factory MotoGP bike!

 

 

We can even take this one step further and place the binding function into an external file:

<?php
	function arrayBinder(&$pdoStatement, &$array) {

		foreach($array as $key=>$value){
			$pdoStatement->bindValue(':'.$key,$value);
		}
	}
?>

*note: use of & denoting that references are being passed in and therefore the original values will be modified

 

The above pdoBinder.php file can now be included in the main file, as can be seen on line 5, and it's arrayBinder() function called on line 23 passing in the prepared statement and the array:

<?php
	ini_set('display_errors', '0');
	include_once('connect.php');
	include_once('error_handling.php');
	include_once('pdoBinder.php');

	$team = 'Ducati';
	$status = 'Factory';

	//Named placeholder, note colon before variable & no quotation marks
	$sqlQuery = "SELECT * from riders where team = :team AND status = :status";

	//now use the prepare method & assign to $statement to create a prepared statement
	$statement = $dbh->prepare($sqlQuery);

	//create an associative array
	$myArr = array('team'=>$team, 'status'=>$status);

	//calling the arrayBinder function from the included pdoBinder.php file
	arrayBinder($statement, $myArr);

	try{
		$statement->execute();
	}
	catch(PDOException $error){
		echo errorHandling($error);
	}
	while($row = $statement->fetch(PDO::FETCH_ASSOC)) {
		echo $row['name'] . " rides a " . $row['team'] . " " . $row['status'] . " MotoGP bike!<br>";
	}
?>

Save & refresh browser:

Connected to the motogp database
Andrea Dovizioso rides a Ducati Factory MotoGP bike!
Nicky Hayden rides a Ducati Factory MotoGP bike!

PDO utilising OOP

Building on the previous examples of moving functions into external files, we can now develop an OO class that will perform the same functions.

 

The error_handling.php and pdoBinder.php files can be placed into a single pdoClass.php file, and encapsulated within a class as follows:

<?php

	class pdoClass{

		public function arrayBinder(&$pdoStatement, &$array) {

			foreach($array as $key=>$value){
				$pdoStatement->bindValue(':'.$key,$value);
			}
		}

		public function errorHandling(Exception $error) {
			echo "Error!<br/>";

			$diag = $error->getTrace();
			if($diag[0]['class'] != ""){
				$diagClass = $diag[0]['class'];
			}
			$func = $diag[0]['function'];
			$file = $diag[0]['file'];
			$line = $diag[0]['line'];
			$errorMessage = $error->getMessage() . "<br>" .
			"Class & Method: " . $diagClass . " & " . $func . "<br>" .
			"File: " . $file . "<br>" .
			"Line: " . $line . "<br>";

			echo $errorMessage;

			file_put_contents( './dbErrors.txt', $errorMessage, FILE_APPEND );
		}
	}
?>

The only difference here is that the previous functions have now been prefixed with the public visibility keyword.

 

The pdoClass.php can now be included within the main file as per line 4, and an object instantiated upon it as per line 8. The $myObject object is then used with the access operator -> on lines 16 and 22 to perform the same functionality as previously but we are now using an object oriented approach:

<?php
	ini_set('display_errors', '0');
	include_once('connect.php');
	include_once('pdoClass.php');
	$team = 'Honda';
	$status = 'Factory';

	$myObject = new pdoClass(); //instantiates an object

	$sqlQuery = "SELECT * from riders where team = :team AND status = :status";

	$statement = $dbh->prepare($sqlQuery);

	$myArr = array('team'=>$team, 'status'=>$status);

	$myObject->arrayBinder($statement, $myArr);

	try{
		$statement->execute();
	}
	catch(PDOException $error){
		echo $myObject->errorHandling($error);
	}

	while($row = $statement->fetch(PDO::FETCH_ASSOC)) {
		echo $row['name'] . " rides a " . $row['team'] . " " . $row['status'] . " MotoGP bike!<br>";
	}
?>

Save & refresh browser:

Connected to the motogp database
Dani Pedrosa rides a Honda Factory MotoGP bike!
Marc Marquez rides a Honda Factory MotoGP bike!
Stefan Bradl rides a Honda Factory MotoGP bike!
Alvaro Bautista rides a Honda Factory MotoGP bike!

PDO Transactions

Transactions provide a method of batch processing a saved set of queries to be carried out (committed) at a later time. They also have the advantage of being able to roll back should a problem arise.

 

The 3 PDO methods utilised for this functionality are:

  • beginTransaction()
  • commit()
  • rollback()

 

After much testing, and finding the rollBack() method wasn't working, I discovered:

  • Ensure your database supports transactions!
  • MyISAM tables do not support transactions
  • Use InnoDB

 

This example has been stripped right down to purely focus on transactions:

  1. The beginTransaction() method is called by the database object, thus turning off auto-commit mode
  2. Four SQL insert queries are executed, but are not committed to the database
  3. The fifth SQL insert query contains errors
  4. The commit() method is called by the database object, but since the above contains an error an exception is thrown
  5. The exception is caught in the catch block
  6. The rollBack() method is called by the database object, and rolls back the above queries
  7. The database appears unchanged
<?php
	include_once('connect.php');
	include_once('error_handling.php');

	try{
		//begin the transaction
		$dbh->beginTransaction();

		$dbh->query("INSERT INTO riders (name, team, points, status) VALUES ('Bradley Smith', 'Yamaha', '41', 'Factory')");
		$dbh->query("INSERT INTO riders (name, team, points, status) VALUES ('Michele Pirro', 'Ducati', '30', 'Factory')");
		$dbh->query("INSERT INTO riders (name, team, points, status) VALUES ('Andrea Iannone', 'Ducati', '24', 'Factory')");
		$dbh->query("INSERT INTO riders (name, team, points, status) VALUES ('Randy De Puniet', 'ART', '15', 'CRT')");
		$dbh->query("INSERT INTO potato (dog, spoon, car, pool) VALUES ('700', 'Change', 'Bye', '99')");

		foreach($dbh->query('SELECT * from riders') as $row) {
			echo $row['name'] . " rides for " . $row['team'] . "<br>";
		}

		$dbh->commit();
	}
	catch(PDOException $error){
		$dbh->rollBack();
		echo errorHandling($error);
	}
?>

Save & refresh browser:

Connected to the motogp database
Error!
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'motogp.potato' doesn't exist
Class & Method: PDO & query
File: /path/user/public_html/pdo/index.php
Line: 13

 

 

Simply comment out line 12 with the errors to see the queries committed to the database:

<?php
	include_once('connect.php');
	include_once('error_handling.php');

	try{
		//begin the transaction
		$dbh->beginTransaction();

		$dbh->query("INSERT INTO riders (name, team, points, status) VALUES ('Bradley Smith', 'Yamaha', '41', 'Factory')");
		$dbh->query("INSERT INTO riders (name, team, points, status) VALUES ('Michele Pirro', 'Ducati', '30', 'Factory')");
		$dbh->query("INSERT INTO riders (name, team, points, status) VALUES ('Andrea Iannone', 'Ducati', '24', 'Factory')");
		$dbh->query("INSERT INTO riders (name, team, points, status) VALUES ('Randy De Puniet', 'ART', '15', 'CRT')");
	//	$dbh->query("INSERT INTO potato (dog, spoon, car, pool) VALUES ('700', 'Change', 'Bye', '99')");

		foreach($dbh->query('SELECT * from riders') as $row) {
			echo $row['name'] . " rides for " . $row['team'] . "<br>";
		}

		$dbh->commit();
	}
	catch(PDOException $error){
		$dbh->rollBack();
		echo errorHandling($error);
	}
?>

Save & refresh browser:

Connected to the motogp database
Dani Pedrosa rides for Honda
Jorge Lorenzo rides for Yamaha
Marc Marquez rides for Honda
Cal Crutchlow rides for Yamaha
Valentino Rossi rides for Yamaha
Andrea Dovizioso rides for Ducati
Stefan Bradl rides for Honda
Nicky Hayden rides for Ducati
Alvaro Bautista rides for Honda
Aleix Espargaro rides for ART
Bradley Smith rides for Yamaha
Michele Pirro rides for Ducati
Andrea Iannone rides for Ducati
Randy De Puniet rides for ART

header redirect

The header() function is used to send a raw HTTP header and must be called before any output is sent, either by normal HTML tags, blank lines in a file, or from PHP.

 

This examples gives the standard redirect to the specified URL after the Location keyword, both enclosed within quotes:

<?php

	header("Location: https://www.example.com/"); /* Redirect browser */

	/* Make sure that code below does not get executed when we redirect. */

	exit;

?>

 

A slight variation to redirect after a specified time using the refresh keyword with a chosen delay time and URL:

 

<?php header('refresh:5;url=/'); ?>

 

 

Or the sleep() function could be prior to the header() redirect function.

 

<?php

sleep(2);

header('location:/');

?>

 

 

Some other header() function examples:

 

Prevent page caching

<?php
//	Date in the past
	header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
	header( 'Cache-Control: no-store, no-cache, must-revalidate' ); 
	header( 'Cache-Control: post-check=0, pre-check=0', false ); 
	header( 'Pragma: no-cache' )
?>

 

Prompt user to download content.

(Content-Disposition header is used to supply a recommended filename and force the browser to display the save dialog box):

<?php
// We'll be outputting a PDF
header('Content-Type: application/pdf');

// It will be called downloaded.pdf
header('Content-Disposition: attachment; filename="downloaded.pdf"');

// The PDF source is in original.pdf
readfile('original.pdf');
?>

 

Basic HTTP Authentication example

<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
    header('WWW-Authenticate: Basic realm="My Realm"');
    header('HTTP/1.0 401 Unauthorized');
    echo 'Text to send if user hits Cancel button';
    exit;
} else {
    echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>";
    echo "<p>You entered {$_SERVER['PHP_AUTH_PW']} as your password.</p>";
}
?>