In the previous part, we discussed two of new major features that come with PHP 7, the scalar type declaration, and returned type declaration. This article will conclude the rest of the features.

Null Coalescing Operator

This operator looks like this ?? (two question marks). This operator is used on a value that needs to be checked if it exists and is not null. Otherwise, it returns a specified default value. We can use it on multiple chained values as well, and it will return the first value that exists and not null. The following example will help in the illustration.

$user = isset($_GET['user']) ? $_GET['user'] : 'guest';

Starting by grabbing the user from the $_GET[] superglobal, and checking if it exists to where we are assigning it to $user, and if it doesn't, we are just returning a default value. This should look familiar and probably this is what most of the developers do. However, we don't have to write all that code anymore with the null coalescing operator.

$user = $_GET['user'] ?? 'guest';

The above will achieve the same functionality, but with less code to write. And as mentioned, it is "chainable".

$value = $first ?? $second ?? $third ?? 'default value';

Spaceship Operator

The PHP team named it "spaceship operator" regarding of the look of it. It looks like this: <=>. It looks more like the starfighter TIE Interceptor from Star Wars.
Anyways, the spaceship operator is used, like other comparison operators, for comparing two expressions, but the difference is that it combines the comparisons, and that's why it's known as "combined comparison operator."

It returns -1, 0 or 1 when the first value/expression is respectively less than, equal to, or greater than the second value/expression. It can be achievable, with the same result, using the same old comparison operators, but we just have to combine those, and that's why it's called the "combined comparison operator."

// Integers
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1

// Floats
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1

// Strings
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1

This is how simple it could be used, but it is stronger than that. The following example shows another real world example.

$users = [
    ['name' => 'John', 'age' => 25],
    ['name' => 'Jack', 'age' => 30],
    ['name' => 'James', 'age' => 35],
];

usort($users, function ($firstUser, $secondUser) {
    return $firstUser['age'] <=> $secondUser['age'];
});

We have an array of users, and each element is an array with the name and the age. I want to sort this array of users by their age, and for this, I used the usort() function which is a function to sort an array by values using a user-defined comparison function (the callback). The usort() function expects us to return a -1, 0, or 1, and it just happened that the spaceship operator does that. While that will give us the ascending order of users, we can achieve the descending order by just flipping the $firstUser and $secondUser values in the return statement.

$users = [
    ['name' => 'John', 'age' => 25],
    ['name' => 'Jack', 'age' => 30],
    ['name' => 'James', 'age' => 35],
];

usort($users, function ($firstUser, $secondUser) {
    return $secondUser['age'] <=> $firstUser['age'];
});

Constant Arrays Using define()

Defining an array as a constant was introduced in PHP 5.6 where it can only be used with const. However, with PHP 7, array constants can now be defined with define()

define('API_VERSIONS', [
'v1',
'v2',
'v3'
]);

echo API_VERSIONS[1]; // v2

How is that different? Well, when you define a constant using const, it is being defined at compile time, while using define(), it will be defined at the run time. That means you can't use const during some point of your code, for example in a conditional statement, while you can do that with define().

Anonymous Classes

As the name suggests, this feature in allows us to create a class on the fly. A good example for this could be the Logger class. We used to define a class for debugging and testing reasons.

class Logger {
    public function log($msg)
    {
        echo $msg;
    }
}

class App {

    // Some methods and properties
    // ...

    // setLogger() method
    public function setLogger(Logger logger)
    {
        $this->logger = logger;
    }

    public function someMethod()
    {
        // Some logic here
        $this->logger->log('someMethod() finished!');
    }
}

$app = new App;
$app->setLogger(new Logger());
$app->someMethod(); // someMethod() finished!

We used that, and we will continue to use it, which is fine if we have a bunch of methods in the Logger, but for a simple use of that class, like the above, maybe we'll find anonymous classes helpful. Let's see the example.

class App {

    // Some methods and properties
    // ...

    // setLogger() method
    public function setLogger(Logger logger)
    {
        $this->logger = logger;
    }

    public function someMethod()
    {
        // Some logic here
        $this->logger->log('someMethod() finished!');
    }
}

// Define Logger as anonymous class

$app = new App;
$app->setLogger(new class {

    public function log($msg)
    {
        echo $msg;
    }
});
$app->someMethod(); // someMethod() finished!

We defined and instantiate the Logger class directly from the setLogger() method on the App class. We can use all the goodies that come with the class, like extending another class, implementing interface..., etc.

//...
$app->setLogger( new class implements LoggerInterface { ... }
//...

Unicode Codepoint Escape Syntax

Codepoint is a code that represents a character (letter, number, or symbol), and it is in Hexadecimal system. Using codepoints requires placing them in a double quote and escape syntax. It looks like this "\u{codepoint}", and any valid codepoint is accepted, with leading 0's being optional.

echo "\u{009999}"; // With leading zeroes
echo "\u{9999}"; // Without leading zeroes
echo "\u{1F604}"; // Smaily face :D

Closure::call()

We used bindTo() method in PHP 5 to bind a closure and then call it later from the object, which is a multi-step process. Using this feature, we can bind and call at once using call() method on that closure.

class User {private $name = 'John Doe';}

// Pre PHP 7 code
$getNameCallBack = function() { return $this->x;};
$getName = $getNameCallBack->bindTo(new User, 'User'); // intermediate closure
echo $getName();

// PHP 7+ code
$getName = function() {return $this->name;};
echo $getName->call(new User);

Filtered unserialize()

If you are not familiar with serialize() and unserialize(), here's an introduction to them. If you are, you can skip the intro.

Introduction

When creating a web application, we probably will send data back and forth between the back-end and the front-end, and simple data structures like numbers, strings and booleans works fine. However, in case we are dealing with more complex data structures like PHP arrays, this won't reflect as expected in JavaScript, to which we communicate in JSON or XML.

While this is the case between two languages, we still can send such complex data structures between two back-end applications using PHP in the "PHP style", and that's using serialize() and unserialize().

For example, let's say that we have an array and we want to send that array from one app to another. We serialize() it in the app where we want to send it from, and unserialize() it in the other app where we can use it as a regular PHP array.

// App 1 side
$data = ['some', 'data', 'to', 'send'];
$serializedData = serialize($data);
// The serializedData will be like this
// a:4:{i:0;s:4:"some";i:1;s:4:"data";i:2;s:2:"to";i:3;s:4:"send";}

// App 2 side
$data = unserialize($serializedData);
echo $data[1]; // data
// You get an array

The thing is, it's not limited to only arrays, we can serialize pretty much every complex data structure, including objects, and there comes the problem.

Serializing and Unserializing Objects

Let's say that we have an application that serves an API, and we have business plans that will limit the API calls number per hour to 100 in the free plan, and to 500 in the business plan. Both FreePlan and BusinessPlan implements Plan interface, and each has a property called callLimit which is the "calls limiter".

// Limits are being taken care of on the second application

class FreePlan implements Plan {
    private $limit = 100;
    //...
}

class BusinessPlan implements Plan {
    private $limit = 500;
    //...
}

Now somewhere in the middle of our application, we are serializing and sending our data to another application where we are calculating the calls that the user has done in the current hour, and according to the number of calls, we will accept or reject his/her request. For some reason, we are not using HTTPS, and in a middle-man attack, the attacker sniffed our data and change it from "FreePlan" to "BussinessPlan" in the serialized data (and of course, changed the associated data with it).

On the other side where we are checking the count of API calls, PHP will still "unserialize" the data that have been sent, and the values will still be valid. And so we will end up with a valid object of BusinessPlan where we should have FreePlan.

Filtering What Should Be Unserialized

This feature seeks to provide a primary security when unserializing objects on untrusted data. It prevents possible code injections by enabling the developer to whitelist classes that can be unserialized. So you can actually "turn off" serializing for all classes or add a list of those which can be serialized. PHP will take care of the classes that can't be unserialized by returning a special class called __PHP_Incomplete_Class.

// converts all objects into __PHP_Incomplete_Class object
$data = unserialize($foo, ["allowed_classes" => false]);

// converts all objects into __PHP_Incomplete_Class object except those of MyClass and MyClass2
$data = unserialize($foo, ["allowed_classes" => ["MyClass", "MyClass2"]]);

// default behaviour (same as omitting the second argument) that accepts all classes
$data = unserialize($foo, ["allowed_classes" => true]);

New Utility Class IntelChar

PHP 7 adds a new utility class called IntelClass which provides utility methods and constants for working with Unicode characters and codepoints. To use this class, the Intl extension must be installed. The full documentation on the class could be found here.

printf('%x', IntlChar::CODEPOINT_MAX); // 10ffff
echo IntlChar::charName('@'); // COMMERCIAL AT
var_dump(IntlChar::ispunct('!')); // bool(true)

Expectations

Expectations are backwards-compatible enhancement to the older assert() function. They allow for zero-cost assertions in production code and provide the ability to throw custom exceptions when the assertion fails.

While the old API continues to be maintained for compatibility, assert() is now a language construct, allowing the first parameter to be an expression rather than just a string to be evaluated or a boolean value to be tested.

Not only assert() can throw custom exceptions but also it can be configured to be ignored, where you might see that useful while in production.

Full details on this feature, including how to set it in both development and production environments, can be found in the expectations section of the assert() reference.

ini_set('assert.exception', 1);

class CustomError extends AssertionError {}

assert(false, new CustomError('Some error message'));

// Fatal error: Uncaught CustomError: Some error message

Group use Declarations

We used to call classes, functions, and constants from another namespace by using that namespace in our code. But it was painful that we have to repeat all the time the use statement with the same namespace. PHP 7 solved that with a new syntax called the group use declarations.

// Pre PHP 7 code
use some\namespace\ClassA;
use some\namespace\ClassB;
use some\namespace\ClassC as C;

use function some\namespace\fn_a;
use function some\namespace\fn_b;
use function some\namespace\fn_c;

use const some\namespace\ConstA;
use const some\namespace\ConstB;
use const some\namespace\ConstC;

// PHP 7+ code
use some\namespace\{ClassA, ClassB, ClassC as C};
use function some\namespace\{fn_a, fn_b, fn_c};
use const some\namespace\{ConstA, ConstB, ConstC};

However, I would love it if I can do something like the following to use every class, function, or constant in the namespace.

use some\namespace\*;
use function some\namespace\*;
use const some\namespace\*;

Generator Return Expressions

This feature builds upon the generator functionality introduced in PHP 5.5. It enables for a return statement to be used in a generator to allow for a final expression to be returned (return by reference is not allowed). This value can be fetched using the new Generator::getReturn() method, which may only be used once the generator has finished yielding values.

$gen = (function() {
    yield 1;
    yield 2;

    return 3;
})();

foreach ($gen as $val) {
    echo $val, PHP_EOL;
}

echo $gen->getReturn(), PHP_EOL;

// 1
// 2
// 3

Being able to return a final value from a generator explicitly is a handy ability to have. This is because it enables for a final value to be returned by a generator (from perhaps some form of coroutine computation) that can be explicitly handled by the client code executing the generator. This is far simpler than forcing the client system first to check whether the final value has been yielded, and then if so, to handle that value individually.

Generator Delegation

Generators can now delegate to another generator, Traversable objects or arrays automatically, without needing to write boilerplate in the external generator by using the yield from construct.

function gen()
{
    yield 1;
    yield 2;
    yield from gen2();
}

function gen2()
{
    yield 3;
    yield 4;
}

foreach (gen() as $val)
{
    echo $val, PHP_EOL;
}

// 1
// 2
// 3
// 4

Integer Division With intdiv()

The new intdiv() function performs an integer division of its operands and returns it. We can, of course, achieve the same results with "casting" to int, or using floor() function, but those two ways are not so accurate with big numbers.

var_dump((int)(10 / 3)); // int(3)
var_dump(intdiv(10, 3)); // int(3)

var_dump((int)(PHP_INT_MAX / 3)); // int(3074457345618258432)
var_dump(intdiv(PHP_INT_MAX, 3)); // int(3074457345618258602)

Session Options

Now session_start() accepts an array of options which will override the session configurations that have been set in php.ini. Before PHP 7, we would have to change those configurations directly in the php.ini file, but now they're being changed in runtime.

These options have also been expanded to support session.lazy_write, which is on by default and causes PHP only to overwrite any session file if the session data has changed. And read_and_close, which is an option that can only be passed to session_start() to indicate that the session data should be read and then the session should immediately be closed unchanged.

For example, to set session.cache_limiter to private and immediately close the session after reading it, we would do the following.

session_start([
    'cache_limiter' => 'private',
    'read_and_close' => true,
]);

New Function preg_replace_callback_array()

The new preg_replace_callback_array() function enables code to be written more cleanly when using the preg_replace_callback() function. Prior to PHP 7, callbacks that needed to be executed per regular expression required the callback function to be polluted with lots of branching.

Now, callbacks can be registered to each regular expression using an associative array, where the key is a regular expression, and the value is a callback.

CSPRNG Functions

CSPRNG stands for "cryptographically secure pseudo-random number generator," which is the way that PHP creates cryptographically secure numbers. Two new functions have been added to generate cryptographically secure integers and strings in a cross-platform way: random_bytes() and random_int().

list() can always unpack objects implementing ArrayAccess

Previously, list() was not guaranteed to operate correctly with objects implementing ArrayAccess. This has been fixed.