Exceptions - PHP Programming: Learn PHP Programming - CRUSH IT IN ONE DAY. Learn It Fast. Learn It Once. Get Coding Today, First Edition(2015)

PHP Programming: Learn PHP Programming - CRUSH IT IN ONE DAY. Learn It Fast. Learn It Once. Get Coding Today, First Edition (2015)

Chapter 7. Exceptions

An exception model is present in PHP just like any other programming language. Within PHP, exceptions thrown can be caught. The code can be written in 'try' block for catching potential exceptions. For every try block there should be at least one finally or catch block corresponding to it.

If an exception, which is not a direct instance of the exception class or the exception subclass is thrown, there will be a PHP fatal error.

Catch

Multiple catch blocks can be used to catch exceptions of different classes. After the last catch block is defined in sequence, normal execution will continue. Exceptions can be thrown or re-thrown in a catch block. This happens when an exception is thrown outside a try block or when the catch block cannot match the exception to any of the exception classes.

When an exception is thrown, the immediate steps that PHP takes is to find a catch box which matches with the exception without executing the next statement.

Unless there is a handler which was defined using the set_exception_handling(), an "Uncaught Exception " message will displayed if a thrown exception is not caught, indicating that a PHP fatal error has occurred.

finally

Instead of the catch blocks, finally block can be specified. Regardless of the exception being thrown or not, the code inside the finally block will be executed after the try and catch blocks. This execution will take place before the normal execution.

Generators

Generators can be used to implement simple iterations, which are without the complexity of implementing a class which in turn increments the interface of the iterator.

Generators allow the users to write code which uses the for each construct over a set of data to iterate without the need to build an array. These arrays will require large memory space and a lot of processing time to generate. This may also cause the user to exceed the memory limit.

Generators use the yield keyword in place of return. The yield is similar to return but it saves the state of the function instead of removing it from the stack. With yield, the values can be iterated unlike normal functions, which only return once.

Using the standard_range() function, an array has to be generated with every value in it. It should also return the values which result in large arrays. For instance, calling range(0,2048000) will result in over 200MB of memory being used.

You can implement an alternate xrange() generator. The xrange generator() internally tracks the generator's current state. It will only need the memory enough to create an iterator object. The size of the object will be less than a KB.

Example 1: Implementing range() as a generator

<?php

function xrange($start, $limit, $step = 1) {

if ($start< $limit) {

if ($step <= 0) {

throw new LogicException('Step must be +ve’);

}

for ($i = $start; $i <= $limit; $i += $step) {

yield $i;

}

} else {

if ($step >= 0) {

throw new LogicException('Step must be -ve');

}

for ($i = $start; $i >= $limit; $i += $step) {

yield $i;

}

}

}

/*

* Note that both range() and xrange() result in the same output below

*/

echo 'Single digit odd numbers from range(): ';

foreach (range(1, 9, 2) as $number) {

echo"$number ";

}

echo "\n";

echo 'Single digit odd numbers from xrange(): ';

foreach (xrange(1, 9, 2) as $number) {

ech0 "$number ";

}

?>

Both normal function and generator functions look similar, except in one way. A generator function, yields as many values as it needs to, instead of returning a value. Whenever we call a generator function, it returns an object that can be iterated over. PHP will call the generator function each time it needs a value and when the generator yields a value it saves the state of the generator so that it can be resumed when the next value is required, whenever we iterate over that object.

The generator function simply exits when there are no more values to be yielded. And just as if an array has run out of values, the calling code continues.

Yield keyword

Yield keyword is the heart of a generator function. A yield statement in its simplest form looks similar to a return statement except in one way. Instead of stopping execution of the function and returning, yield pausesthe generator function’s execution and provides a value to the code looping over the generator.

Example 1: A simple example of yielding values

<?php

function gen_one_to_three() {

for ($i = 1; $i <= 3; $i++) {

// Note that $i is preserved between yields.

yield $i;

}

}

$generator = gen_one_to_three();

foreach ($generator as $value) {

echo "$value\n";

}

?>

The output of the above sample

1

2

3

Comparing generators with Iterator objects

Simplicity is the main advantage of generators. Much less boilerplate code will be enough when compared to the iterator class implementation. And in general the code is much more readable here. For example,

<?php

function getLinesFromFile($fileName) {

if (!$fileHandle = fopen($fileName, 'r')) {

return;

}

while (false !== $line = fgets($fileHandle)) {

yield $line;

}

fclose($fileHandle);

}

// versus...

class LineIterator implements Iterator {

protected $fileHandle;

protected $line;

protected $i;

public function __construct($fileName) {

if(!$this->fileHandle = fopen($fileName, 'r')) {

throw new RuntimeException('Couldn\'t open file' . $fileName . '"');

}

}

public function rewind() {

fseek($this->fileHandle, 0);

$this->line = fgets($this->fileHandle);

$this->i = 0;

}

public function valid() {

return false !== $this->line;

}

public function current() {

return $this->line;

}

public function key() {

return $this->i;

}

public function next() {

if (false!== $this->line) {

$this->line = fgets($this->fileHandle);

$this->i++;

}

}

public function __destruct() {

fclose($this->fileHandle);

}

}

?>

There is one limitation here. Once the iteration has started, the generator cannot be rewound. This is because the iterations are forward-only type. Given generator can only be iterated once. If you want to iterate the generator multiple times, you will have to clone the generator using the keyword clone or you can call the generator function again

References

What are References?

In PHP, in order to access the same variable content by different names, references are useed. They are unlike C pointers. They are not memory addresses; one cannot perform pointer arithmetic using them. References are symbol table aliases. Here in PHP same content can have different names because the variable content and variable name are different here. It has closest analogy with Unix filenames and files. Here variable names are directory entries, and variable content is the file itself. hard linking in Unix file system can be likened to References.

What References Do?

Three basic operations can be performed using references. They are

1. Assigning by reference,

2. Passing by reference and

3. Returning by reference.

Assigning by Reference

In PHP it is possible to make two different variables referring to a same content. This means, when you do,

<?php

$a =& $b;

?>

Here, in the above piece of code you can see that both $a and $b pointing to the same content.

Example 1: Using References with undefined variables:

<?php

function foo(&$var) { }

foo($a); // $a is "created"and assigned to null

$b = array();

foo($b['b']);

var_dump(array_key_exists('b', $b)); // bool(true)

$c = new StdClass:

foo($c->d);

var_dump(property_exists($c, 'd’)); // bool(true)

?>

Example 2: Referencing global variables inside functions

<?php

$var1 = "Example variable";

$var2 = "";

function global_references($use_globals)

{

global $var1, $var2;

if (!$use_globals) {

$var2 =& $var1; //visible only inside the function

} else {

$GLOBALS["var2"] =& $var1; //visible also in global context

}

}

global_references(false);

echo "var2 is set to '$var2'\n”; // var2 is set to ''

global_references(true);

echo "var2 is set to '$var2'\n"; // var2 is set to 'Example variable”

?>

Example :3 References statements and foreach statements

<?php

$ref = 0;

$row =& $ref;

foreach (array(1, 2, 3) as $row)

{

// do something

}

echo $ref; // 3 - last element of the iterated array

?>

Expressions which are created with the language construct array() can behave like that by prefixing and to the array element to add.

Example:

<?php

$a = 1;

$b = array(2,3);

$arr = array(&$a, &$b[0], &$b[1]);

$arr[0]++; $arr[1]++; $arr[2]++;

/* $a == 2, $b == array(3,4); */

?>

Assignment of scalar variables:

<?php

/* Assignment of scalar variables */

$a = 1;

$b =& $a;

$c = $b;

$c = 7; //$c is not a reference; no change to $a or $b

/* Assignment of array variables */

$arr = array(1);

$a =& $arr[0]; //$a and $arr[0] are in the same reference set

$arr2 = $arr; //not an assignment-by-reference!

$arr2[0]++;

/*$a==2, $arr==(2)*/

Even though it is not a reference, the contents of $arr are not changed */

?>

In other words, the reference behavior of arrays is defined in an element-by-element basis; the reference behavior of individual elements is dissociated from the reference status of the array container.

Pass By Reference

Passing variables by reference is the second thing. Passing by reference can be done by creating a local variable in the function and another variable for referencing the same content.

Example:

<?php

function foo(&$var)

{

$var++;

}

$a=5;

foo($a);

?>

This will make $a to be 6. This happens because in the function foo the variable $var refers to the same content as $a)

Return by Reference

Return by Reference is the third and final thing references can do.