Free eBook - Mastering PHP 7

4.3 (7 reviews total)
By Branko Ajzele
  • A new free eBook every day on the latest in tech
  • 30 permanently free eBooks from our core tech library
  1. The All New PHP

About this book

PHP is a server-side scripting language that is widely used for web development. With this book, you will get a deep understanding of the advanced programming concepts in PHP and how to apply it practically

The book starts by unveiling the new features of PHP 7 and walks you through several important standards set by PHP Framework Interop Group (PHP-FIG). You’ll see, in detail, the working of all magic methods, and the importance of effective PHP OOP concepts, which will enable you to write effective PHP code. You will find out how to implement design patterns and resolve dependencies to make your code base more elegant and readable. You will also build web services alongside microservices architecture, interact with databases, and work around third-party packages to enrich applications. This book delves into the details of PHP performance optimization. You will learn about serverless architecture and the reactive programming paradigm that found its way in the PHP ecosystem. The book also explores the best ways of testing your code, debugging, tracing, profiling, and deploying your PHP application.

By the end of the book, you will be able to create readable, reliable, and robust applications in PHP to meet modern day requirements in the software industry.

Publication date:
June 2017
Publisher
Packt
Pages
536
ISBN
9781785882814

 

Chapter 1. The All New PHP

Programming languages nowadays are a dime a dozen. New languages spring into existence every so often. Choosing the right one for the job is so much more than just a checklist of its features. Some of them target specific problem domains, others try to position themselves for more general use. This goes to say that software development is a dynamic ecosystem where languages need to constantly adapt to ever-changing industry in order to stay relevant to its consumers. These changes are particularly challenging for already established languages such as PHP, where backward compatibility is an important consideration.

Originally created by Rasmus Lerdorf around 1995, PHP started its life as nothing more than a few Common Gateway Interface (CGI) programs in C. At that time, it was a simple scripting solution that empowered developers to build dynamic HTML pages with ease. Without the need to compile, developers could easily throw in a few lines of code into a file and see the results in the browser. This gave a rise to its early popularity. Two decades later, PHP matured into a rich general-purpose scripting language suited to web development. Throughout all these years, PHP managed to yield an impressive set of features with each new release whilst maintaining a trustworthy level of backward compatibility. Nowadays, large number of its core extensions ultimately simplify working with files, sessions, cookies, databases, web services, cryptography, and many other features common to web development. Its outstanding support for the object-oriented programming (OOP) paradigm made it truly competitive with other leading industry languages.

The decade-old ruling of PHP 5 has been overthrown by the release of PHP 7 in December 2015. It brought forth the all new execution engine, Zend Engine 3.0, which significantly improved performance and reduced memory consumption. This simple software update now allowed us to serve more concurrent users without adding any physical hardware to the mix. Acceptance among developers has been almost instant, all the more so because backward incompatibility was minimal, making migration as painless as possible.

In this chapter, we will take a detailed look into some of the new features introduced in PHP 7 and 7.1 releases:

  • Scalar type hints
  • Return type hints
  • Anonymous classes
  • Generator delegation
  • Generator return expressions
  • The null coalesce operator
  • The spaceship operator
  • Constant arrays
  • Uniform variable syntax
  • Throwables
  • Group use declarations
  • Class constant visibility modifiers
  • Catching multiple exceptions types
  • Iterable pseudo-type
  • Nullable types
  • Void return types

It is features like these that are bound to make a mark on the next generation of PHP frameworks and libraries, as well as how we write our own code.

 

Scalar type hints


By classification, PHP is a dynamically typed and weakly typed language. These are two different concepts that often get mixed together. Dynamically typed languages do not require the explicit declaration of a variable before it is used. Weakly typed languages are those in which the variable is not of any specific data type, that is, its type can change through different value-type reassignments.

Let's take a look at the following example:

// dynamic typed (no specific type defined, directly assigning value)
$name = "Branko"; // string
$salary = 4200.00; // float
$age = 33; // int

// weak typed (variable value reassigned into different type)
$salary = 4200.00; // float
$salary = $salary + "USD"; // float
$salary = $salary . "USD"; // string

In the preceding code, we see three different variables being used, none of which are predefined with a certain type. We just have values declared into them. PHP then determines the type on the go. Even when the variable type is determined, it can still be changed by simply assigning another type of value to it. These are two very powerful concepts, which, when used wisely, can save us lines and lines of code.

However, these powerful features often indirectly encourage bad design. This is particularly noticeable when writing functions, either by forcing function designers into multiple data type checks, or forcing them into multiple function return types.

Let's take a look at the following example:

function addTab($tab) {
  if (is_array($tab)) {

  } elseif (is_object($tab)) {

  } elseif (is_string($tab)) {

  } else {

  } 
}

Given the type uncertainty of the input argument, the addTab function was forced to branch its logic. Similarly, the same function might decide to return different types of data, depending on the logic branch. Designs like these are usually a result of functions that simply try to do too much. The real problem is not even in the function, it is on the consumer side of things. If it happens that the developer using a function is not aware enough of the passing parameter type, unexpected results might occur.

To help us write more correct and self-documenting programs, PHP introduced type hinting.

PHP has supported function parameter type hinting from version 5.0, but only for objects, and from version 5.1 for arrays as well. With PHP 7, scalar types can be type-hinted as well, making it one of the more exciting features of the release. The following are the scalar type hints that are supported by PHP:

  • int
  • float
  • string
  • bool

We can now write functions in either of the following ways:

  • It can  be function register($email, $age, $notify) { /* ... */}
  • It can be function register($email, int $age, $notify) { /* ... */}
  • It can be function register(string $email, int $age, bool $notify) { /* ... */}

However, simply hinting scalar types is not enough as type declarations are not enforced by default. PHP will simply attempt to convert to the specified type without complaint. By adding the declare(strict_types=1); directive as the first statement in a PHP file, we can enforce the strict type checking behavior. It is worth noting that this directive only affects the specific file it is used in, and does not affect other included files. The file-level directive was used to preserve the backward compatibility with numerous extensions and built-in PHP functions.

Let's take a look at the following example:

declare(strict_types=1);

function register(string $email, int $age, bool $notify) {
 // body
}

register('[email protected]', '33', true);

With strict types directive turned on, trying to pass an improper data type to a hinted scalar parameter would result in a \TypeError exception, as per the following output:

Fatal error: Uncaught TypeError: Argument 2 passed to register() must be of the type integer, string given, called in /test.php on line 11 and defined in /test.php:5 Stack trace: #0 /test.php(11): register('[email protected]', '33', true) #1 {main} thrown in /test.php on line 5.

Scalar type hints are a powerful new addition to the PHP language. They empower developers with an extra layer of protection during runtime, without really sacrificing the weak type system in general.

 

Return type hints


Type hinting features are not limited to function parameters only; as of PHP 7, they expand to function return values as well. The same rules that apply to function parameters hinting, apply to function return type hinting. To specify a function return type, we simply follow the parameter list with a colon and the return type, as shown in the following example:

function register(string $user, int $age) : bool {
  // logic ...
  return true;
}

Developers can still write functions with multiple conditioned return statements; its just that in this case, each of these return statements, when reached, will have to match the hinted return type, otherwise \TypeError will be thrown.

The function return type hints play nicely with super types. Let's take a look at the following example:

class A {}
class B extends A {}
class C extends B {}

function getInstance(string $type) : A {
    if ($type == 'A') {
       return new A();
       } elseif ($type == 'B') {
           return new B();
       } else {
           return new C();
       }
  }

getInstance('A'); #object(A)#1 (0) { }
getInstance('B'); #object(B)#1 (0) { }
getInstance('XYZ'); #object(C)#1 (0) { }

We see that the function executes nicely for all three types. Given that B extends A directly, and C extends B, the function accepts them as the return value.

Given the dynamic nature of PHP, function return types might seem like a step in the wrong direction at first, more so because a lot of PHP code out there already uses the PHPDoc @return annotation, which plays nicely with modern IDE tools, such as PhpStorm. However, the @return annotation is merely informative, it does not enforce an actual return type during runtime, and it really makes sense only with a powerful IDE. Using the function return type hints ensures that our functions return what we intended them to return. They do not stand in the way of PHP's dynamic nature; they merely enrich it from a function consumer point of use.

 

Anonymous classes


Instantiating objects from classes is a pretty straightforward action. We use the new keyword, followed by a class name and possible constructor parameters. The class name part implies the existence of a previously defined class. Though rare, there are cases where classes are only used during execution. These rare cases make it verbose to force a class definition separately when we know that the class is only being used once. To address this verbosity challenge, PHP introduced a new functionality called anonymous classes. While the concept of anonymous classes has been around for quite some time in other languages, PHP only got to it in the PHP 7 release.

The syntax of anonymous classes is pretty straightforward, which is as follows:

$obj = new class() {};
$obj2 = new class($a, $b) {
   private $a;
   private $b;
   public function __construct($a, $b) {
     $this->a = $a;
     $this->b = $b;
   }
};

We use the new keyword , followed by the class keyword, followed by optional constructor parameters, and finally the body of the class packed in curly braces. Both objects are instantiated as a [email protected] type. The functionality of objects instantiated through anonymous classes is no different from those instantiated via named classes.

Compared to named classes, anonymous classes are pretty much equal, in that, they can pass contractor parameters, extend other classes, implement interfaces, and use traits. However, anonymous classes cannot be serialized. Trying to serialize an instance of an anonymous class, as shown in the following code snippet, throws a fatal Serialization of [email protected] is not allowed… error.

There are few other caveats to keep in mind when using anonymous classes. Nesting an anonymous class within another class hides the private and protected methods or properties of that outer class. To circumvent the limitation, we can pass the outer class' private and protected properties into an anonymous class constructor, as follows:

interface Salary {
      public function pay();
   }

   trait Util {
      public function format(float $number) {
         return number_format($number, 2);
      }
   }

   class User {
      private $IBAN;
      protected $salary;
      public function __construct($IBAN, $salary) {
         $this->IBAN = $IBAN;
         $this->salary = $salary;
      }

      function salary() {
       return new class($this->IBAN, $this->salary) implements Salary {
         use Util;
         private $_IBAN;
         protected $_salary;

         public function __construct($IBAN, $salary) {
            $this->_IBAN = $IBAN;
            $this->_salary = $salary;
         }

        public function pay() {
           echo $this->_IBAN . ' ' . $this->format($this->_salary);
        }
     };
   } 
 }
 $user = new User('GB29NWBK60161331926819', 4500.00);
 $user->salary()->pay();

In this strip down User class example, we have a salary method that returns an anonymous class. To showcase the more robust use of anonymous classes, we make it implement the Salary interface and use the Util trait. The Salary interface forces the anonymous class to implement the pay method. Our implementation of pay method requires IBAN and salary member values from the outer class. Since an anonymous class does not allow access to private and protected members of the outer class, we pass those through anonymous class constructors. While the overall example certainly does not reflect notions of a good class design, it does showcase how to bypass the member visibility limitation.

There is also an option for an anonymous class to fetch the private and protected members of the outer class by extending the outer class itself. However, this requires the anonymous class constructor to properly instantiate the outer class; otherwise, we might end up with a warning, such as a missing argument, for User::__construct().

Even though they are namelessly defined, anonymous classes still get an internal name. Using the core PHP get_class method on an instance of an anonymous class, gets us that name, as shown in the following examples:

class User {}
class Salary {}

function gen() {
  return new class() {};
}

$obj = new class() {};
$obj2 = new class() {};
$obj3 = new class() extends User {};
$obj4 = new class() extends Salary {};
$obj5 = gen();
$obj6 = gen();

echo get_class($obj); // [email protected]/var/www/index.php0x27fe03a
echo get_class($obj2); // [email protected]/var/www/index.php0x27fe052
echo get_class($obj3); // [email protected]/var/www/index.php0x27fe077
echo get_class($obj4); // [email protected]/var/www/index.php0x27fe09e
echo get_class($obj5); // [email protected]/var/www/index.php0x27fe04f
echo get_class($obj6); // [email protected]/var/www/index.php0x27fe04f

for ($i=0; $i<=5; $i++) {
  echo get_class(new class() {}); // 5 x   
    [email protected]/var/www/index.php0x27fe2d3
}

Observing these outputs, we see that the anonymous classes created in the same position (function or a loop) will yield the same internal name. Those with the same name return true for the equal (==) operator and false for the identity operator (===), an important consideration in order to avoid potential bugs.

Support for an anonymous classes opens a door to some interesting use cases, such as mocking tests and doing the inline class overrides, both of which, when used wisely, can improve code quality and readability.

 

Generator delegation


Iterating through a list of items is among the most common things in any programming language. PHP makes it easy to iterate over a diverse collection of data using the foreach construct. Many languages differentiate various data types of collection data, such as dictionary, list, set, tuple, and alike. PHP, however, does not dwell that much on data structures and simply uses the array() or [] constructs most of the time for its collections. This, in turn, can have a negative impact on creating large arrays in memory, which could cause exceeding memory limits or even increased processing times.

Note

Aside from the primitive array type, PHP also provides the ArrayObject and ArrayIterator classes. These turn arrays into a first class citizens in an OOP application.

Generators allow us to write code that uses foreach to iterate over a set of data without needing to build an array. They are like a function that yields as many values as needed, instead of returning just one, which gives them an iterator-like behavior. While generators have been around from PHP 5.5, they lacked more advanced functionality. Generator delegation is one of the improvements made available with the release of PHP 7.

Let's take a look at the following example:

function even() {
   for ($i = 1; $i <= 10; $i++) {
     if ($i % 2 == 0) {
        yield $i;
     }
   }
}

function odd() {
    for ($i = 1; $i <= 10; $i++) {
       if ($i % 2 != 0) {
          yield $i;
       }
    }
}

function mix() {
   yield -1;
   yield from odd();
   yield 17;
   yield from even();
   yield 33;
}

// 2 4 6 8 1 0
foreach (even() as $even) {
  echo $even;
}

// 1 3 5 7 9
foreach (odd() as $odd) {
  echo $odd;
}

// -1 1 3 5 7 9 17 2 4 6 8 10 33
foreach (mix() as $mix) {
  echo $mix;
}

Here, we define three generator functions: even, odd, and mix. The mix function demonstrates the concept of generator delegation via the use of yield from <expr>. Whereas, <expr> is any expression that evaluates to a traversable object or array. We can see that the result of looping through the mix function echoes all of the yielded values from both itself as well as the even and odd functions.

The generator delegation syntax allows the factoring of yield statements into smaller conceptual units, giving generators the similar organizational functionality as methods give to classes. Used carefully, this can improve our code quality and readability.

 

Generator return expressions


Though PHP 5.5 enriched the language by introducing generator functions functionality, it lacked the return expressions alongside their yielded values. This inability of generator functions to specify return values limited their usefulness with coroutines. The PHP 7 version addressed this limitation by adding support for the return expressions. Generators are basically interruptible functions, where the yield statement flags the interruption point. Let's take a look at the following simple generator, written in the form of a self-invoking anonymous function:

$letters = (function () {
  yield 'A';
  yield 'B';
  return 'C';
})();

// Outputs: A B
foreach ($letters as $letter) {
  echo $letter;
}

// Outputs: C
echo $letters->getReturn();

Though the $letters variable is defined as a self-invoking anonymous function, the yield statements are preventing immediate function execution, turning the function into the generator. Generator itself stands still until we try to iterate over it. Once the iteration kicks in, generator yields value A followed by value B, but not C. What this means is that when used in the foreach construct, the iteration will only encompass yielded values, not the returned ones. Once the iteration is done, we are free to call the getReturn() method to retrieve the actual return value. Calling the getReturn() method prior to iterating over generator results cannot get the return value of a generator that hasn't returned an exception.

The great thing about the generators is that they are not a one-way street; they are not limited to only yielding values, they can accept them as well. By being the instances of a \Generator class, they operate with several useful methods, two of which are getReturn and send. The send method enables us to send values back to the generator, which turns the one-way communication from the generator to the caller into a two-way channel between the two, effectively, turning generators into coroutines. The addition of the getReturn method empowered generators with the return statements, giving more flexibility with coroutines.

 

The null coalesce operator


Working with variables in PHP is quite easy. Variable declaration and initialization is done via a single expression. For example, the expression $user['name'] = 'John'; will automatically declare variable $user of type array and initialize that array with a single key name of value John.

Day-to-day development often includes checking for the existence of a variable value for various branching decisions, such as if ($user['name'] =='John') { … } else { … }. As we write our code ourselves, we tend to make sure that our code does not use non-declared variables and non-initialized array keys. There are cases, however, where variables come from outside, so we are not really in a position to guarantee their existence at runtime. Calling for $user['name'] when $user is not set, or is set but with keys other than name, will result in notice undefined index--name. Like any unexpected state in code, notices are bad, more so because they do not actually break your code, they allow it to execute further. When a notice occurs, unless we have the display_errors configuration set to true, and error reporting configured to show E_ALL, we would not even see the notice in the browser.

This is bad, as we might depend on the existence of variables and their values that are not there. This dependency might not even be handled in our code, and we would not even notice it because the code will continue to execute unless a specific variable check is put in place.

The PHP language has a certain number of predefined variables called superglobals, which we can use from any function, class, or file, regardless of the scope. The most used ones are probably $_POST and $_GET superglobals, which are used to fetch the data submitted via forms or URL parameters. Since we cannot guarantee the existence of $_GET['name'] in such cases, we need to check for it. Usually, this is done using the isset and empty functions in PHP, as shown in the following code block:

// #1
if (isset($_GET['name']) && !empty($_GET['name'])) 
   {
     $name = $_GET['name'];
   } 
else {
     $name = 'N/A';
     }

// #2
if (!empty($_GET['name'])) 
   {
     $name = $_GET['name'];
   } 
else {
       $name = 'N/A';
     }

// #3

$name = ((isset($_GET['name']) && !empty($_GET['name']))) ? $_GET['name'] : 'N/A';

// #4
$name = (!empty($_GET['name'])) ? $_GET['name'] : 'N/A';

The first example is the most robust one, as it uses both, the isset and empty functions. These functions are not the same, so it's important to understand what each of them does. The good thing about an empty function is that it will not trigger a notice if we try to pass it a variable that might not be set, such as $_GET['name']; it will simply return true or false. This makes the empty function a nice helper for most cases. However, even the fourth example, written via the use of the ternary operator, is somewhat robust.

PHP 7 introduced a new type of operator called the null coalesce (??) operator. It empowers us with the ability of writing shorter expressions. The following example demonstrates the elegance of its use:

$name = $_GET['name'] ?? 'N/A';

It returns the result of its first operand if it exists and is not null, or else its second operand. In other words, reading it from left to right, the first existing value, which is not null, is the value that will be returned.

 

The spaceship operator


Comparing two values is a frequent operation in any programming language. We use various language operators to express the type of comparison we wish to execute between two variables. In PHP, these operators include equal ($a == $b), identical ($a === $b), not equal ($a != $b or $a <> $b), not identical ($a !== $b), less than ($a < $b), greater than ($a > $b), less than or equal to ($a <= $b), and greater than or equal to ($a >= $b) comparisons.

All of these comparison operators result in Boolean true or false. Sometimes, however, there are cases where a three-way comparison is needed, in which case, the result of the comparison is more than just a Boolean true or false. While we can achieve a three-way comparison using various operators through various expressions, the solution is all but elegant.

With the release of PHP 7, a new spaceship <=> operator has been introduced, with a syntax as follows:

(expr) <=> (expr)

The spaceship <=> operator offers combined comparison. After comparison, it follows these conditions:

  • It returns 0 if both operands are equal
  • It returns 1 if the left operand is greater
  • It returns -1 if the right operand is greater

Comparison rules used to yield the preceding results are the same as those used by existing comparison operators: <, <=, ==, >=, and >.

The usefulness of the new operator is especially apparent with ordering functions. Without it, the ordering functions were quite robust, as per the following example:

$users = ['branko', 'ivana', 'luka', 'ivano'];

usort($users, function ($a, $b) {
  return ($a < $b) ? -1 : (($a > $b) ? 1 : 0);
});

We can shorten the preceding example by applying the new operator to it, as follows:

$users = ['branko', 'ivana', 'luka', 'ivano'];

usort($users, function ($a, $b) {
  return $a <=> $b;
});

Applying the spaceship <=> operator, where applicable, gives the expressions simplicity and elegance.

 

Constant arrays


There are two types of constants in PHP, the constants and the class constants. The constants can be defined pretty much anywhere using the define construct, while the class constants are defined within the individual class or interface using the const keyword.

While we cannot say that one type of constant is more important than the other, PHP 5.6 made the difference between the two by allowing class constants with the array data type. Aside from that difference, both types of constants supported scalar values (integer, float, string, Boolean, or null).

The PHP 7 release addressed this inequality by adding the array data type to constants as well, making the following into valid expressions:

// The class constant - using 'const' keyword
class Rift {
  const APP = [
    'name' => 'Rift',
    'edition' => 'Community',
    'version' => '2.1.2',
    'licence' => 'OSL'
  ];
}

// The class constant - using 'const' keyword
interface IRift {
  const APP = [
    'name' => 'Rift',
    'edition' => 'Community',
    'version' => '2.1.2',
    'licence' => 'OSL'
  ];
}

// The constant - using 'define' construct
define('APP', [
  'name' => 'Rift',
  'edition' => 'Community',
  'version' => '2.1.2',
  'licence' => 'OSL'
]);

echo Rift::APP['version'];
echo IRift::APP['version'];
echo APP['version'];

Though having constants with the array data type might not be an exciting type of feature, it adds a certain flavor to the overall constant use.

 

Uniform variable syntax


The new variable syntax is probably one of the most impacting features of the PHP 7 release. It brings greater order into variable dereferencing. The impacting part, however, not only affects changes for better as it also introduces certain backwards compatibility (BC) breaks. Among the main reasons for these changes were inconsistencies with variable variable syntax.

Observing the $foo['bar']->baz expression, first a variable named $foo is fetched, then the bar offset is taken from the result, and, finally, the baz property is accessed. This is how normally variable accesses is interpreted, from left to right. However, the variable variable syntax goes against this principle. Observing the $$foo['baz'] variable, $foo is fetched first, then its baz offset, and finally looking for the variable with the name of the result is done.

The newly introduced uniform variable syntax addresses these inconsistencies as per the following example:

/*** expression syntax ***/
$$foo['bar']['baz']

// PHP 5.x meaning
${$foo['bar']['baz']}

// PHP 7.x meaning
($$foo)['bar']['baz']

/*** expression syntax ***/
$foo->$bar['baz']

// PHP 5.x meaning
$foo->{$bar['baz']}

// PHP 7.x meaning
($foo->$bar)['baz']

/*** expression syntax ***/
$foo->$bar['baz']()

// PHP 5.x meaning
$foo->{$bar['baz']}()

// PHP 7.x meaning
($foo->$bar)['baz']()

/*** expression syntax ***/
Foo::$bar['baz']()

// PHP 5.x meaning
Foo::{$bar['baz']}()

// PHP 7.x meaning
(Foo::$bar)['baz']()

Other than addressing the preceding inconsistencies, several new syntax combinations have been added that make the following expressions now valid:

$foo()['bar']();
[$obj1, $obj2][0]->prop;
getStr(){0}
$foo['bar']::$baz;
$foo::$bar::$baz;
$foo->bar()::baz()
// Assuming extension that implements actual toLower behavior
"PHP"->toLower();
[$obj, 'method']();
'Foo'::$bar;

There are quite a few different syntaxes here. While some of this might seem overwhelming and hard to find use for, it opens a door for new ways of thinking and code use.

 

Throwables


The exceptions in PHP are not a new concept. They have been around ever since PHP 5 was released. However, they did not encompass all of PHP's error handling because errors were not considered to be exceptions. PHP, at the time, had two-error handling systems. This made it tricky to deal with, as traditional errors were not catchable via the try...catch blocks exceptions. Certain tricks were possible, where one could have used the set_error_handler() function in order to set a user-defined error handler function, basically listening for errors and turning them into exceptions.

Let's look at the following example:

<?php

class Mailer
{
    private $transport;

    public function __construct(Transport $transport)
    {
        $this->transport = $transport;
    }
}

$transport = new stdClass();

try {
    $mailer = new Mailer($transport);
} catch (\Exception $e) {
    echo 'Caught!';
} finally {
    echo 'Cleanup!';
}

PHP 5 would not be able to catch this, and instead throws Catchable fatal error, as shown here:

Catchable fatal error: Argument 1 passed to Mailer::__construct() must be an instance of Transport, instance of stdClass given, called in /index.php on line 18 and defined in /index.php on line 6.

By adding the implementation of set_error_handler() before this code, as follows, we could turn that fatal error into an exception:

set_error_handler(function ($errno, $errstr) {
  throw new \Exception($errstr, $errno);
});

With the preceding code in place, the try...catch...finally blocks would now kick in as intended. However, there were error types that could not be caught with set_error_handler, such as E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT raised in the file where set_error_handler is called.

The PHP 7 release improved the overall error handling system by introducing the Throwable interface, and moving the errors and exceptions under its umbrella. It is now the base interface for any object that can be thrown via a throw statement. While we cannot extend it directly, we can extend the \Exception and \Error classes. While \Exception is the base class for all PHP and user exceptions, \Error is the base class for all internal PHP errors.

We could now easily rewrite our preceding try...catch...finally block into one of the following:

<?php

// Case 1
try {
    $mailer = new Mailer($transport);
} catch (\Throwable $e) {
    echo 'Caught!';
} finally {
    echo 'Cleanup!';
}

// Case 2
try {
    $mailer = new Mailer($transport);
} catch (\Error $e) {
    echo 'Caught!';
} finally {
    echo 'Cleanup!';
}

Notice the use of \Throwable in the first example catch block. Even though we cannot extend it, we can use it as a shorthand for catching both \Error and \Exception in a single catch statement.

Implementation of \Throwable brings a much needed alignment between errors and exceptions, making them easier to reason with.

 

Group use declarations


PHP introduced namespaces as part of the 5.3 release. It provided a way to group related classes, interfaces, functions, and constants, thus making our code base more organized and readable. However, dealing with modern libraries usually involves a lot of verbosity in terms of numerous use statements used to import classes from various namespaces, as shown in the following example:

use Magento\Backend\Block\Widget\Grid;
use Magento\Backend\Block\Widget\Grid\Column;
use Magento\Backend\Block\Widget\Grid\Extended;

To address this verbosity, the PHP 7 release introduced the group use declarations, allowing the following syntax:

use Magento\Backend\Block\Widget\Grid;
use Magento\Backend\Block\Widget\Grid\{
  Column,
  Extended
};

Here, we condensed Column and Extend under a single declaration. We can further condense this using the following compound namespaces:

use Magento\Backend\Block\Widget\{
  Grid
  Grid\Column,
  Grid\Extended
};

The group use declarations act as a shorthand to condense use declarations, making it slightly easier to import classes, constants, and functions in a concise way. While their benefits seem somewhat marginal, their use is completely optional.

 

Catching multiple exceptions types


With the introduction of throwables, PHP pretty much aligned its efforts around error detection, reporting, and handling. Developers are able to use the try...catch...finally blocks to handle the exceptions as they see fit. The possibility to use multiple catch blocks can give finer control over the response to certain types of exceptions. Sometimes, however, there are groups of exceptions we would like to respond equally. In PHP 7.1, exception handling was further refined to accommodate this challenge.

Let's take a look at the following PHP 5.x example:

try {
      // ...
    } 
catch (\InvalidArgumentException $e) 
    {
      // ...
    } 
catch (\LengthException $e)
    {
      // ...
    }
catch (Exception $e) 
   {
     // ...
   } 
finally 
  {
    // ...
  }

Here, we are handling three exceptions, two of which are quite specific, and a third one that catches in if the previous two are not matched. The finally block is merely a cleanup, if it happens that one is needed. Imagine now that the same response is needed for both the \InvalidArgumentException and \LengthException blocks. The solution would be to either copy an entire chunk of code from one exception block into another, or, at best, write a function that wraps the response code and then calls that function within each exception block.

The newly added exception handling syntax is enabled to catch multiple exception types. By using a single vertical bar (|), we can define multiple exception types for the catch parameter, as per the following PHP 7.x example:

try {
      // ...
    } 
catch (\InvalidArgumentException | \LengthException $e)
   {
     // ...
   }  
catch (\Exception $e) 
   {
     // ...
   }
 finally 
   {
     // ...
   }

Aside from a touch of elegance, the new syntax directly affects code reuse for the better.

 

Class constant visibility modifiers


There are five types of access modifier in PHP: public, private, protected, abstract, and final. Often called visibility modifiers, not all of them are equally applicable. Their use is spread across classes, functions, and variables, as follows:

  • Functions: public, private, protected, abstract, and final
  • Classes: abstract and final
  • Variables: public, private, and protected

Class constants, however, are not on the list. The older versions of PHP did not allow a visibility modifier on the class constant. By default, class constants were merely assigned public visibility.

The PHP 7.1 release addresses this limitation by introducing the public, private, and protected class constant visibility modifiers, as per the following example:

class Visibility 
 {
   // Constants without defined visibility
   const THE_DEFAULT_PUBLIC_CONST = 'PHP';

   // Constants with defined visibility
   private const THE_PRIVATE_CONST = 'PHP';
   protected const THE_PROTECTED_CONST = 'PHP';
   public const THE_PUBLIC_CONST = 'PHP';
 }

Similar to the old behavior, class constants declared without any explicit visibility default to public.

 

Iterable pseudo-type


Quite often, functions in PHP either accept or return an array or object implementing the \Traversable interface. Though both types can be used in the foreach constructs, fundamentally, an array is a primitive type; objects are not. This made it difficult for functions to reason about these types of iterative parameters and return values.

PHP 7.1 addresses this need by introducing the iterable pseudo-type to the mix. The idea is to use it as a type declaration on a parameter or return type to indicate that the value is iterable. The iterable type accepts any array, any object implementing Traversable, and generators.

The following example demonstrates the use of iterable as a function parameter:

function import(iterable $users) 
 {
   // ...
 }

function import(iterable $users = null) 
 {
   // ...
 }

function import(iterable $users = []) 
 {
   // ...
 }

Trying to pass the value to the preceding import function other than an array instance of Traversable or generator will throw \TypeError. If, however, the default value is assigned, be it null or an empty array, the function will work.

The following examples demonstrates the use of iterable as a function return value:

 function export(): iterable {
   return [
     'Johny',
     'Tom',
     'Matt'
   ];
 }

 function mix(): iterable {
   return [
     'Welcome',
      33,
      4200.00
   ];
 }

 function numbers(): iterable {
    for ($i = 0; $i <= 5; $i++) {
       yield $i;
    }
 }

One thing to be careful about is that iterable is implemented as a reserved class name in PHP. What this means is that any user class, interface, or trait named iterable will throw an error.

 

Nullable types


Many programming languages allow some sort of optional or nullable types, depending on terminology. The PHP dynamic type already supports this notion via the built-in null type. A variable is considered to be of the null type if it has been assigned a constant value null, it has not been assigned any value, or it has been unset using the unset() construct. Aside from variables, the null type can also be used against the function parameters, by assigning them a default value of null.

However, this imposed a certain limitation, as we could not declare a parameter that might be null without flagging it as optional at the same time.

PHP 7.1 addressed this limitation by adding a leading question mark symbol (?) to indicate that a type can be null, unless specifically assigned to some other value. This also means that type could be null and mandatory at the same type. These nullable types are now permitted pretty much anywhere where type declarations are permitted.

The following is an example of the nullable type with a mandatory parameter value:

function welcome(?string $name) {
   echo $name;
}

welcome(); // invalid
welcome(null); // valid

The first call to the welcome function throws an \Error, because its declaration is making the parameter mandatory. Goes to say that the nullable type should not be mistaken with null being passed as a value.

The following is an example of a nullable type with an optional parameter value, optional in the sense that it has been assigned a default value of null already:

function goodbye(?string $name = null)
 {
   if (is_null($name)) 
     {
       echo 'Goodbye!';
     } 
   else
     { 
       echo "Goodbye $name!";
     }
 }

goodbye(); // valid
goodbye(null); // valid
goodbye('John'); // valid

The following is an example of function declaration using the nullable return type:

function welcome($name): ?string 
  {
    return null; // valid
  }

function welcome($name): ?string 
  {
    return 'Welcome ' . $name; // valid
  }

function welcome($name): ?string 
 {
   return 33; // invalid
 }

The nullable types work both with scalar types (Boolean, Integer, Float, String) and compound types (Array, Object, Callable).

 

Void return types


With all the power of function parameter types and function return types introduced in PHP 7, there was one thing missing from the mix function. While function return types allowed specifying a desired return type, they did not allow specifying the lack of return value. To address this inconsistency, the PHP 7.1 release introduced a void return type feature.

Why is this important, we might ask ourselves? As with previously mentioned function return types, this feature can be extremely useful for documentation and error-checking purposes. By its nature, PHP does not require a return statement in its function definitions, making it unclear at first look if the function simply executes certain actions or returns a value. Using the void return type makes it clearer that a function's purpose is to perform an action, rather than producing a result.

Let's take a look at the following example:

function A(): void {
   // valid
}

function B(): void {
   return; // valid
}

function C(): void {
   return null; // invalid
}

function D(): void {
   return 1; // invalid
}

The function A and function B methods showcase a valid use of the void type parameter. The  function A method has no explicitly set return value, but that's OK, as PHP implicitly always returns null. The function B method simply uses the return statement without any following type, which also makes it valid. The function C method is a bit strange, as it looks like it might be valid at first, but it's not. How is it that function C is invalid while the function A method is, even though they do the same thing? Even though return and return null are technically equivalent in PHP, they are not really the same. The existence of a return type, or its lack, denotes a function intent. Specifying return values, even if its null, suggests the value is significant. With a void return type, the return value is insignificant. The use of the void return type, therefore, signifies an unimportant return value, the one that won’t be used anywhere after the function is called.

Note

The differentiation between explicit void and implicit null return might come as somewhat foggy. The takeaway here is that using void return types conveys that the function is not supposed to return any kind of value. While they do not make any major impact on the code itself, and their use is fully optional, they do bring a certain richness to the language.

 

Summary


The PHP 7 and 7.1 releases have introduced quite a few changes. Some of these changes transform the language beyond what PHP once was. While still pertaining the dynamic typing system, function parameters and return types can now be strictly defined. This changes the way we look and work with functions. Among function-related changes, there are several others targeting improvements over a decade old PHP 5. The ecosystem, as a whole, will take some time to catch up. For developers with experience in PHP 5, these changes are not merely technical in nature; they require change of mindset in order to successfully apply what is now possible.

Moving forward, we will look into the current state of PHP standards, who defines them, what they describe, and how can we benefit from embracing them.

About the Author

  • Branko Ajzele

    Branko Ajzele is a respected and highly accomplished software developer, book author, solution specialist, consultant, and team leader. He currently works for Interactive Web Solutions Ltd (iWeb), where he holds the role of senior developer and is the director of iWeb's Croatia office.

    Branko holds several respected IT certifications, including Zend Certified PHP Engineer, Magento Certified Developer, Magento Certified Developer Plus, Magento Certified Solution Specialist, Magento 2 Certified Solution Specialist, Magento 2 Certified Professional Developer, to mention just a few.

    He was crowned the e-commerce Developer of the Year by the Digital Entrepreneur Awards in October 2014 for his excellent knowledge and expertise in e-commerce development.

    Browse publications by this author

Latest Reviews

(7 reviews total)
RAS
The book did not contain the information I had hoped for.
I bought this e-book in part of a discount offer. I like that a lot!

Recommended For You

The PHP Workshop

Cut through the noise and get real results with a step-by-step approach to learning PHP programming

By Alexandru Busuioc and 6 more
The PHP Workshop

Cut through the noise and get real results with a step-by-step approach to learning PHP programming

By Alexandru Busuioc and 6 more
PHP 7 Programming Cookbook

Over 80 recipes that will take your PHP 7 web development skills to the next level!

By Doug Bierer
PHP 7 Programming Cookbook

Over 80 recipes that will take your PHP 7 web development skills to the next level!

By Doug Bierer