Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Mastering PHP 7
Mastering PHP 7

Mastering PHP 7: Design, configure, build, and test professional web applications

By Branko Ajzele
$39.99 $27.98
Book Jun 2017 536 pages 1st Edition
eBook
$39.99 $27.98
Print
$48.99
Subscription
$15.99 Monthly
eBook
$39.99 $27.98
Print
$48.99
Subscription
$15.99 Monthly

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Buy Now

Product Details


Publication date : Jun 27, 2017
Length 536 pages
Edition : 1st Edition
Language : English
ISBN-13 : 9781785882814
Category :
Table of content icon View table of contents Preview book icon Preview Book

Mastering PHP 7

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('user@mail.com', '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('user@mail.co...', '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 class@anonymous 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 class@anonymous 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); // class@anonymous/var/www/index.php0x27fe03a
echo get_class($obj2); // class@anonymous/var/www/index.php0x27fe052
echo get_class($obj3); // class@anonymous/var/www/index.php0x27fe077
echo get_class($obj4); // class@anonymous/var/www/index.php0x27fe09e
echo get_class($obj5); // class@anonymous/var/www/index.php0x27fe04f
echo get_class($obj6); // class@anonymous/var/www/index.php0x27fe04f

for ($i=0; $i<=5; $i++) {
  echo get_class(new class() {}); // 5 x   
    class@anonymous/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.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Leverage the newest tools available in PHP 7 to build scalable applications
  • Embrace serverless architecture and the reactive programming paradigm, which are the latest additions to the PHP ecosystem
  • Explore dependency injection and implement design patterns to write elegant code

Description

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.

What you will learn

[*] Grasp the current state of PHP language and the PHP standards [*] Effectively implement logging and error handling during development [*] Build services through SOAP and REST and Apache Trift [*] Get to know the benefits of serverless architecture [*] Understand the basic principles of reactive programming to write asynchronous code [*] Practically implement several important design patterns [*] Write efficient code by executing dependency injection [*] See the working of all magic methods [*] Handle the command-line area tools and processes [*] Control the development process with proper debugging and profiling

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Buy Now

Product Details


Publication date : Jun 27, 2017
Length 536 pages
Edition : 1st Edition
Language : English
ISBN-13 : 9781785882814
Category :

Table of Contents

24 Chapters
Title Page Chevron down icon Chevron up icon
Credits Chevron down icon Chevron up icon
About the Author Chevron down icon Chevron up icon
About the Reviewers Chevron down icon Chevron up icon
www.PacktPub.com Chevron down icon Chevron up icon
Customer Feedback Chevron down icon Chevron up icon
Preface Chevron down icon Chevron up icon
The All New PHP Chevron down icon Chevron up icon
Embracing Standards Chevron down icon Chevron up icon
Error Handling and Logging Chevron down icon Chevron up icon
Magic Behind Magic Methods Chevron down icon Chevron up icon
The Realm of CLI Chevron down icon Chevron up icon
Prominent OOP Features Chevron down icon Chevron up icon
Optimizing for High Performance Chevron down icon Chevron up icon
Going Serverless Chevron down icon Chevron up icon
Reactive Programming Chevron down icon Chevron up icon
Common Design Patterns Chevron down icon Chevron up icon
Building Services Chevron down icon Chevron up icon
Working with Databases Chevron down icon Chevron up icon
Resolving Dependencies Chevron down icon Chevron up icon
Working with Packages Chevron down icon Chevron up icon
Testing the Important Bits Chevron down icon Chevron up icon
Debugging, Tracing, and Profiling Chevron down icon Chevron up icon
Hosting, Provisioning, and Deployment Chevron down icon Chevron up icon

Customer reviews

Filter icon Filter
Top Reviews
Rating distribution
Empty star icon Empty star icon Empty star icon Empty star icon Empty star icon 0
(0 Ratings)
5 star 0%
4 star 0%
3 star 0%
2 star 0%
1 star 0%

Filter reviews by


No reviews found
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

How do I buy and download an eBook? Chevron down icon Chevron up icon

Where there is an eBook version of a title available, you can buy it from the book details for that title. Add either the standalone eBook or the eBook and print book bundle to your shopping cart. Your eBook will show in your cart as a product on its own. After completing checkout and payment in the normal way, you will receive your receipt on the screen containing a link to a personalised PDF download file. This link will remain active for 30 days. You can download backup copies of the file by logging in to your account at any time.

If you already have Adobe reader installed, then clicking on the link will download and open the PDF file directly. If you don't, then save the PDF file on your machine and download the Reader to view it.

Please Note: Packt eBooks are non-returnable and non-refundable.

Packt eBook and Licensing When you buy an eBook from Packt Publishing, completing your purchase means you accept the terms of our licence agreement. Please read the full text of the agreement. In it we have tried to balance the need for the ebook to be usable for you the reader with our needs to protect the rights of us as Publishers and of our authors. In summary, the agreement says:

  • You may make copies of your eBook for your own use onto any machine
  • You may not pass copies of the eBook on to anyone else
How can I make a purchase on your website? Chevron down icon Chevron up icon

If you want to purchase a video course, eBook or Bundle (Print+eBook) please follow below steps:

  1. Register on our website using your email address and the password.
  2. Search for the title by name or ISBN using the search option.
  3. Select the title you want to purchase.
  4. Choose the format you wish to purchase the title in; if you order the Print Book, you get a free eBook copy of the same title. 
  5. Proceed with the checkout process (payment to be made using Credit Card, Debit Cart, or PayPal)
Where can I access support around an eBook? Chevron down icon Chevron up icon
  • If you experience a problem with using or installing Adobe Reader, the contact Adobe directly.
  • To view the errata for the book, see www.packtpub.com/support and view the pages for the title you have.
  • To view your account details or to download a new copy of the book go to www.packtpub.com/account
  • To contact us directly if a problem is not resolved, use www.packtpub.com/contact-us
What eBook formats do Packt support? Chevron down icon Chevron up icon

Our eBooks are currently available in a variety of formats such as PDF and ePubs. In the future, this may well change with trends and development in technology, but please note that our PDFs are not Adobe eBook Reader format, which has greater restrictions on security.

You will need to use Adobe Reader v9 or later in order to read Packt's PDF eBooks.

What are the benefits of eBooks? Chevron down icon Chevron up icon
  • You can get the information you need immediately
  • You can easily take them with you on a laptop
  • You can download them an unlimited number of times
  • You can print them out
  • They are copy-paste enabled
  • They are searchable
  • There is no password protection
  • They are lower price than print
  • They save resources and space
What is an eBook? Chevron down icon Chevron up icon

Packt eBooks are a complete electronic version of the print edition, available in PDF and ePub formats. Every piece of content down to the page numbering is the same. Because we save the costs of printing and shipping the book to you, we are able to offer eBooks at a lower cost than print editions.

When you have purchased an eBook, simply login to your account and click on the link in Your Download Area. We recommend you saving the file to your hard drive before opening it.

For optimal viewing of our eBooks, we recommend you download and install the free Adobe Reader version 9.