In this chapter of the PHP tutorial, we continue description of the OOP in the PHP language.
The shallow copy copies all values and references into a new instance. The data to which a referece is pointing is not copied; only the pointer is copied. The new references are pointing to the original objects. Any changes to the reference members affect both objects.
The deep copy copies all values into a new instance. In case of members that are stored as references a deep copy performs a deep copy of data, that is being referenced. A new copy of a referenced object is created. And the pointer to the newly created object is stored. Any changes to those referenced objects will not affect other copies of the object. Deep copies are fully replicated objects.
In PHP, we have a
The next two examples will perform a shallow and a deep copy on objects.
To change this behaviour, we will do a deep copy next.
During the execution of our application, many things might go wrong. A disk might get full and we cannot save our file. An internet connection might go down and our application tries to connect to a site. All these might result in a crash of our application. To prevent happening this, we must cope with all possible errors that might occur. For this, we can use the exception handling.
Exceptions were only recently built into the PHP 5 language. Most PHP errors still use the old error reporting and not exceptions. With a
The
The first solution is based on the
The next code example simulates constructor overloading using the factory pattern. It is one of the creational patterns in OOP. The pattern creates objects without specifying the exact class of object that will be created. More generally, the term factory method is often used to refer to any method whose main purpose is creation of objects. (from Wikipedia)
In this part of the PHP tutorial, we continued the discussion of the object-oriented programming in PHP.
The static keyword
We can declare class properties and methods to bestatic
. The static
properties and methods do not belong to the instance of the class. They belong to the class itself. They are accessible through the scope resolution operator (::). <?phpIn the above PHP script, we have a
class Sys {
public static function println($string) {
echo "$string\n";
}
}
Sys::println("PHP");
Sys::println("PERL");
Sys::println("Python");
Sys::println("Pike");
?>
static
println() method. It prints a string and starts a new line. This example is inspired by the Java language. Sys::println("PHP");We do not need an object to call println() method. We call
static
methods by specifying the class name, followed by double colon operator and the method name. $ php static1.phpThis is the output of the script.
PHP
PERL
Python
Pike
<?phpAnd now we have an example with a
class Math {
public static $PI = 3.14159265359;
}
echo Math::$PI . "\n";
?>
static
variable. echo Math::$PI . "\n";We access the variable by specifying the class name, followed by the scope resolution operator and the variable name.
The final keyword
Final methods cannot be overriden and final classes cannot be extended. Thefinal
keyword is a matter of design of the application. Some classes should not be extended and some methods should not be overriden. This behaviour is enforced by the final
keyword. <?phpThis PHP script won't compile. We get an error "Cannot override final method Base::say()".
class Base {
final function say() {
echo "Base class";
}
}
class Derived extends Base {
function say() {
echo "Derived class";
}
}
?>
<?phpIn the previous PHP script, we have a prototype base Math class. The sole purpose of this class is to provide some helpful methods and constants to the programmer. (In our case we have only one method for simplicity reasons.) It is not created to be extended. To prevent uninformed other programmers to derive from this class, the creators made the class
final class Math {
static function getPI() {
return 3.141592;
}
}
class DerivedMath extends Math {
function say() {
echo "DerivedMath class";
}
}
?>
final
. If you try to run this PHP script, you get the following error: "Fatal error: Class DerivedMath may not inherit from final class (Math)". Deep copy vs shallow copy
Copying of data is an important task in programming. Object is a composite data type in OOP. Member field in an object may be stored by value or by reference. Copying may be performed in two ways.The shallow copy copies all values and references into a new instance. The data to which a referece is pointing is not copied; only the pointer is copied. The new references are pointing to the original objects. Any changes to the reference members affect both objects.
The deep copy copies all values into a new instance. In case of members that are stored as references a deep copy performs a deep copy of data, that is being referenced. A new copy of a referenced object is created. And the pointer to the newly created object is stored. Any changes to those referenced objects will not affect other copies of the object. Deep copies are fully replicated objects.
In PHP, we have a
copy
keyword, which performs a shallow copy by default. It calls the object's __clone()
method. We can implement the method to create our custom deep copy. In PHP 5, all objects are assigned by reference. The next two examples will perform a shallow and a deep copy on objects.
<?phpIn the above PHP script, we define two custom objects. Object and Color. The Object object will have a reference to the Color object.
class Object {
public $id;
public $size;
public $color;
function __construct($id, $size, $color) {
$this->id = $id;
$this->size = $size;
$this->color = $color;
}
}
class Color {
public $red;
public $green;
public $blue;
function __construct($red, $green, $blue) {
$this->red = $red;
$this->green = $green;
$this->blue = $blue;
}
}
$color = new Color(23, 42, 223);
$object1 = new Object(23, "small", $color);
$object2 = clone $object1;
$object2->id++;
$object2->color->red = 255;
$object2->size = "big";
print_r($object1);
print_r($object2);
?>
$color = new Color(23, 42, 223);We create an instance of the Color object.
$object1 = new Object(23, "small", $color);An instance of the Object object is created. It passes the instance of the Color object to its constructor.
$object2 = clone $object1;We perform a shallow copy of the Object object.
$object2->id++;Here we modify the member fields of the cloned object. We increment the id, change the red part of the color object and change the size to "big".
$object2->color->red = 255;
$object2->size = "big";
print_r($object1);We use the
print_r($object2);
print_r()
function to compare the results. $ php shallowcopy.phpWe can see, that the ids are different. 23 vs 24. The size is different. "small" vs "big". But the red part of the color object is same for both instances. 255. Changing member values of the cloned object did not affect the original object. Changing members of the referenced object has affected the original object too. In other words, both objects refer to the same color object in memory.
Object Object
(
[id] => 23
[size] => small
[color] => Color Object
(
[red] => 255
[green] => 42
[blue] => 223
)
)
Object Object
(
[id] => 24
[size] => big
[color] => Color Object
(
[red] => 255
[green] => 42
[blue] => 223
)
)
To change this behaviour, we will do a deep copy next.
<?phpIn this PHP script, we have implemented the
class Object {
public $id;
public $size;
public $color;
function __construct($id, $size, $color) {
$this->id = $id;
$this->size = $size;
$this->color = $color;
}
function __clone() {
$red = $this->color->red;
$green = $this->color->green;
$blue = $this->color->blue;
$this->color = new Color($red, $green, $blue);
}
}
class Color {
public $red;
public $green;
public $blue;
function __construct($red, $green, $blue) {
$this->red = $red;
$this->green = $green;
$this->blue = $blue;
}
}
$color = new Color(23, 42, 223);
$object1 = new Object(23, "small", $color);
$object2 = clone $object1;
$object2->id++;
$object2->color->red = 255;
$object2->size = "big";
print_r($object1);
print_r($object2);
?>
__clone()
method. function __clone() {Inside the
$red = $this->color->red;
$green = $this->color->green;
$blue = $this->color->blue;
$this->color = new Color($red, $green, $blue);
}
__clone()
method, we copy the red, green and blue member fields and create a new Color object. Now, the $color field points to a different Color object. $ php deepcopy.phpNow the red part of the referenced Color object is not the same. The original object has retained its previous 23 value.
Object Object
(
[id] => 23
[size] => small
[color] => Color Object
(
[red] => 23
[green] => 42
[blue] => 223
)
)
Object Object
(
[id] => 24
[size] => big
[color] => Color Object
(
[red] => 255
[green] => 42
[blue] => 223
)
)
Exceptions
Exceptions are designed to handle the occurrence of exceptions, special conditions that change the normal flow of program execution. Exceptions are raised or thrown, initiated.During the execution of our application, many things might go wrong. A disk might get full and we cannot save our file. An internet connection might go down and our application tries to connect to a site. All these might result in a crash of our application. To prevent happening this, we must cope with all possible errors that might occur. For this, we can use the exception handling.
Exceptions were only recently built into the PHP 5 language. Most PHP errors still use the old error reporting and not exceptions. With a
set_error_handler()
we can do a workaround for this. <?phpIn the above PHP script, we intentionally divide a number by zero. This leads to an error. The error is not an exception and is not caught by the
set_error_handler("error_handler");
function error_handler($errno, $errstring, $errfile, $line, $trace) {
throw new ErrorException($errstring, $errno, 0, $errfile, $line);
}
try {
$a = 0;
$b = 32;
$c = $b / $a;
} catch(ErrorException $e) {
echo "Error occurred\n";
echo $e->getMessage(), "\n";
}
?>
catch
keyword. set_error_handler("error_handler");The
set_error_handler()
function sets a user defined error handler function. function error_handler($errno, $errstring, $errfile, $line, $trace) {Inside the
throw new ErrorException($errstring, $errno, 0, $errfile, $line);
}
set_error_handler()
function, we throw an ErrorException
. This exception is later caught by the catch
keyword. try {The code, that we are checking against an error is put inside the block following the
$a = 0;
$b = 32;
$c = $b / $a;
}
try
keyword. } catch(Exception $e) {The
echo $e->getMessage();
}
catch
keyword is used to catch an exception, which occurred. To find out more info, we call the getMessage()
method on the exception object. $ php zerodiv.phpThis is the output of our PHP script.
Error occurred
Division by zero
The
Exception
is a base class for all exceptions. We can create our own exceptions derived from this base class. <?phpLet's say, we have a situation in which we cannot deal with big numbers.
define("LIMIT", 333);
class BigValueException extends Exception {
public function __construct($message) {
parent::__construct($message);
}
}
$a = 34325;
try {
if ($a > LIMIT) {
throw new BigValueException("Exceeded the maximum value allowed\n");
}
} catch (BigValueException $e) {
echo $e->getMessage();
}
?>
define("LIMIT", 333);Numbers bigger than this constant are considered to be "big" by our PHP script.
class BigValueException extends Exception {We have a BigValueException class. This class derives from the
Exception
class through the extends
keyword. public function __construct($message) {Inside the constructor, we call the parent's constructor.
parent::__construct($message);
}
if ($a > LIMIT) {If the value is bigger than the limit, we throw our custom exception. We give the exception a message "Exceeded the maximum value allowed".
throw new BigValueException("Exceeded the maximum value allowed\n");
}
} catch (BigValueException $e) {We catch the exception and print its message to the console.
echo $e->getMessage();
}
Constructor oveloading
At the time of writing this tutorial (February 2010), constructor overloading is not supported in PHP language. In other words, each class can only have one constructor defined. Many programmers, that know Java or C# languages are looking for a similar feature in PHP. There are two ways, how we can handle this.The first solution is based on the
func_get_args()
function. The second solution uses a factory pattern. <?phpIn the above script, we have a Book class. We instantiate the class with 2 and 3 parameters.
class Book {
private $title = "not specified";
private $author = "not specified";
private $year = "not specified";
public function __construct() {
$args = func_get_args();
foreach(array("title", "author", "year") as $item)
{
if(empty($args)) {
break;
}
$this->$item = array_shift($args);
}
}
public function __toString() {
return "Author: $this->author\nTitle: $this->title\nPublished: $this->year\n\n";
}
}
$book1 = new Book("Stephen Prata", "C Primer Plus");
echo $book1;
$book2 = new Book("Joshua Bloch", "Effective Java", 2008);
echo $book2;
?>
private $author = "not specified";We have three member fields defined. Their initial value is "not specified".
private $title = "not specified";
private $year = "not specified";
$args = func_get_args();The
func_get_args()
function returns an array comprising a function's argument list. So the idea is: the code inside the constructor is dynamic, it depends on the arguments passed to it. foreach(array("title", "author", "year") as $item)We go through all member fields using the
foreach
keyword. $this->$item = array_shift($args);One of the most basic tasks in constructors is to initialize the member fields of the class. This is done by the above code line. The
array_shift()
function removes the first item from the array and returns it. $book1 = new Book("Stephen Prata", "C Primer Plus");We have two different constructors. The first takes 2 parameters, the second takes 3.
...
$book2 = new Book("Joshua Bloch", "Effective Java", 2008);
$ php constructors.phpThis is the outcome of the script.
Author: C Primer Plus
Title: Stephen Prata
Published: not specified
Author: Effective Java
Title: Joshua Bloch
Published: 2008
The next code example simulates constructor overloading using the factory pattern. It is one of the creational patterns in OOP. The pattern creates objects without specifying the exact class of object that will be created. More generally, the term factory method is often used to refer to any method whose main purpose is creation of objects. (from Wikipedia)
<?phpWe have a Cat factory class in the above PHP script. It has three different static functions. Each of them returns a specific cat object.
class Cat {
private $name = "unspecified";
private $age = "unspecified";
public static function withName($name) {
$cat = new Cat();
$cat->name = $name;
return $cat;
}
public static function withAge($age) {
$cat = new Cat();
$cat->age = $age;
return $cat;
}
public static function fullCat($name, $age) {
$cat = new Cat();
$cat->name = $name;
$cat->age = $age;
return $cat;
}
public function __toString() {
return "Name: $this->name, Age: $this->age\n";
}
}
$cici = Cat::withName("Cici");
echo $cici;
$missy = Cat::withAge(6);
echo $missy;
$lucky = Cat::fullCat("Lucky", 4);
echo $lucky;
?>
private $name = "unspecified";We have two member fields. Their initial value is "unspecified".
private $age = "unspecified";
public static function withName($name) {Here is a withName() static function. This function creates an instance of the Cat class. Set the name member field and returns the object.
$cat = new Cat();
$cat->name = $name;
return $cat;
}
$cici = Cat::withName("Cici");We create an instance of the cat with one of the factory methods. We echo the object. e.g. call the
echo $cici;
__toString()
method of the class. $ php factory.phpThe output of the script.
Name: Cici, Age: unspecified
Name: unspecified, Age: 6
Name: Lucky, Age: 4
In this part of the PHP tutorial, we continued the discussion of the object-oriented programming in PHP.
0 comments:
Post a Comment