Part 1 - New Features

Perhaps the most exciting part of PHP 7 are the new features! This part of the book covers these features in detail, including examples of useful applications.

Chapter 1: Scalar Type Hints

PHP 5 introduced the ability to require function parameters to be of a certain type. This provides a safeguard against invalid uses, like passing a UNIX timestamp to a method which expects a DateTime object. It also makes it clear to other developers how the function should be used. For example, compare the following method signatures with and without type hints:

// No type hint:
function getNextWeekday($date) { /*...*/ }

// Class type hint (PHP 5):
function getNextWeekday(DateTime $date) { /*...*/ }

These hints were initially limited to just classes and interfaces, but was soon expanded to allow array and callable types too. PHP 7 extends this further by allowing scalar types like int, float, string and bool:

// Scalar type hint (PHP 7):
function getNextWeekday(int $date) { /*...*/ }

Type Checking Modes

PHP’s flexible type system is one of its most-useful features, allowing numeric strings to be used as integers and vice-versa. This tradition is continued in PHP 7 but now you have the option of enabling strict type enforcement like you’d see in other languages (such as Java or C#). This setting can be enabled using the declare construct.

Weak (“Coercive”) Type Checking

“Coercive” mode is the default type checking mode in PHP 7. This is identical to how previous versions of PHP handled scalar type hints for built-in functions. For example, take a look at the method signature for the floor function:

float floor ( float $value )

When you pass in numeric strings or integers PHP auto-converts them to a float automatically. This behavior has simply been extended to userland functions as well.

Here’s a table showing which scalar types are accepted in “Coercive” mode based on the declared type:

Allowed types, coercive mode
Type declaration int float string bool object
int yes yes* yes† yes no
float yes yes yes† yes no
string yes yes yes yes yes‡
bool yes yes yes yes no

    * Only non-NaN floats between PHP_INT_MIN and PHP_INT_MAX accepted.

    † If it’s a numeric string

    ‡ Only if object has a __toString() method

Strong (“Strict”) Type Checking

PHP 7 introduces a new “strict” mode which is enabled by placing declare(strict_types=1); at the top of your script like so:

<?php
declare(strict_types=1);

function welcome(string $name) {
    echo 'Hello ' . $name;
}

welcome('World'); // Prints: Hello World

This declaration enables strict mode for all uses in the entire file, including built-in PHP function calls and return values (see next chapter). It does not affect any included files nor any other files which include it.

Strict mode essentially requires you to pass in exact types declared. If you don’t, a TypeError will be thrown. For example, in strict mode, you cannot use numeric strings when a float or int is expected (like you can in weak/coercive mode):

This throws an error in strict mode
<?php
declare(strict_types=1);

function welcome(string $name) {
    echo 'Hello ' . $name;
}

welcome(3);

// Fatal error: Uncaught TypeError: Argument 1 passed to welcome() must be of th\
e type string, integer given

There’s one exception to this rule though, which is that float declarations can accept int values:

Scope widening example
<?php
declare(strict_types=1);

function add(float $a, float $b): float {
    return $a + $b;
}

add(1, 2); // float(3)
Allowed types, strict mode
Type declaration int float string bool object
int yes no no no no
float yes* yes no no no
string no no yes no no
bool no no no yes no

    * Allowed due to widening primitive conversion

Mixing Modes

Because the directive is set per-file, it’s entirely possible to mix modes in your application. For example, your strictly-checked app could use a weakly-checked library (or vice versa) without any issues or complications.

Backwards Compatibility

You may no longer create classes named int, float, string or bool as these would conflict with the new scalar type hints.

Further Reading

Chapter 3: Combined Comparison (Spaceship) Operator

PHP 7 introduces a new three-way comparison operator <=> (T_SPACESHIP) which takes two expressions: (expr) <=> (expr). It compares both sides of the expression and, depending on the result, returns one of three values:

0 If both expressions are equal
1 If the left is greater
-1 If the right is greater

You may be familiar with this if you’ve worked with existing comparison functions like strcmp before.

It has the same precedence as the equality operator (==, ===, !=, !==) and behaves identically to the existing comparison operators (<, <=, ==, >=, >).

Comparing Values

You can compare everything from scalar values (like ints and floats) to arrays and even objects too. Here are some examples from the relevant RFC:

PHP RFC examples (licensed under CC BY 3.0)
// 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

echo "a" <=> "aa"; // -1
echo "zz" <=> "aa"; // 1

// Arrays
echo [] <=> []; // 0
echo [1, 2, 3] <=> [1, 2, 3]; // 0
echo [1, 2, 3] <=> []; // 1
echo [1, 2, 3] <=> [1, 2, 1]; // 1
echo [1, 2, 3] <=> [1, 2, 4]; // -1

// Objects
$a = (object) ["a" => "b"];
$b = (object) ["a" => "b"];
echo $a <=> $b; // 0

$a = (object) ["a" => "b"];
$b = (object) ["a" => "c"];
echo $a <=> $b; // -1

$a = (object) ["a" => "c"];
$b = (object) ["a" => "b"];
echo $a <=> $b; // 1

// only values are compared
$a = (object) ["a" => "b"];
$b = (object) ["b" => "b"];
echo $a <=> $b; // 0

Sorting

Perhaps the best application of this operator is to simplify sorting, as functions like usort expect you to perform a comparison and return -1, 0, or 1 accordingly:

This simplification is especially apparent when comparing objects by some property value:

class Spaceship {
    public $name;
    public $maxSpeed;

    public function __construct($name, $maxSpeed) {
        $this->name = $name;
        $this->maxSpeed = $maxSpeed;
    }
}

$spaceships = [
    new Spaceship('Rebel Transport', 20),
    new Spaceship('Millenium Falcon', 80),
    new Spaceship('X-Wing Starfighter', 80),
    new Spaceship('TIE Bomber', 60),
    new Spaceship('TIE Fighter', 100),
    new Spaceship('Imperial Star Destroyer', 60),
];

// Sort the spaceships by name (in ascending order)
usort($spaceships, function ($ship1, $ship2) {
    return $ship1->name <=> $ship2->name;
});

echo $spaceships[0]->name; // "Imperial Star Destroyer"

// Sort the spaceships by speed (in descending order)
// Notice how we switch the position of $ship1 and $ship2
usort($spaceships, function ($ship1, $ship2) {
    return $ship2->maxSpeed <=> $ship1->maxSpeed;
});

echo $spaceships[0]->name; // "TIE Fighter"

Without the comparison operator, these functions would be much more complex:

usort($spaceships, function ($ship1, $ship2) {
    if ($ship1->maxSpeed == $ship2->maxSpeed) {
        return 0;
    } elseif ($ship1->maxSpeed < $ship2->maxSpeed) {
        return 1;
    } else {
        return -1;
    }
});

Sorting by multiple values

You can take advantage of the array comparison behavior to easily sort by multiple fields:

// Sort by speed (asc), then by name (asc)
usort($spaceships, function ($ship1, $ship2) {
    return [$ship1->maxSpeed, $ship1->name] <=> [$ship2->maxSpeed, $ship2->name];
});

foreach ($spaceships as $ship) {
    printf("%3d is the max speed of a(n) %s\n", $ship->maxSpeed, $ship->name);
}

// Outputs:
//  20 is the max speed of a(n) Rebel Transport
//  60 is the max speed of a(n) Imperial Star Destroyer
//  60 is the max speed of a(n) TIE Bomber
//  80 is the max speed of a(n) Millenium Falcon
//  80 is the max speed of a(n) X-Wing Starfighter
// 100 is the max speed of a(n) TIE Fighter

Further Reading

Chapter 5: Unicode Codepoint Escape Syntax

PHP’s lack of native Unicode support can make things difficult when coding for the web. While libraries like iconv and mbstring have simplified working with strings, there were no simple mechanisms available to create Unicode characters or strings without converting them from an HTML or JSON representation:

Workaround examples
$char = html_entity_decode('&#x2603', 0, 'UTF-8');
$char = mb_convert_encoding('&#x2603', 'UTF-8', 'HTML-ENTITIES');
$char = json_decode('"\\u2603"');

PHP 7 finally introduces native support for Unicode character escape sequences within strings, just like you’d see in Ruby or ECMAScript 6:

$char = "\u{2603}";

This makes it much easier (and quicker) to embed Unicode characters, especially ones that aren’t easily typed. These can be used in strings alongside other characters too. For example, here’s the U+202E RIGHT-TO-LEFT OVERRIDE character being used to display the string in reverse:

echo "\u{202E}This is backwards"; // displays: sdrawkcab si sihT

You can omit leading 0s if you’d like:

echo "\u{58}"; // "X"
echo "\u{0058}"; // "X"

Why the {}s?

Some other languages (C/C++/Java) use a format without the {} characters: \uXXXX. Unfortunately this limits their use to the Basic Multilingual Plane (U+0000 to U+FFFF). However, Unicode supports other characters beyond 16 bits.

For example, if we wanted to represent the U+1F427 PENGUIN emoji, our escape sequence would look something like this: \u1F427. Most languages would intepret this as U+1F42 GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA plus a 7, which is not what we want. In these languages, you’d have to encode it using two 16-bit sequences like this: \uD83D\uDC27. This isn’t very clear though.

Wrapping with {} characters allows us to easily go beyond that 16-bit limitation without sacrficing clarity: \u{1F427}

Limitations

This feature follows the behavior of all other escape sequences in PHP - they can only be used within double-quoted strings and heredocs:

Example usage
$foo = "\u{2109}\u{2134}\u{2134}";
// ℉ℴℴ

$bar = <<<EOT
    \u{212C}\u{212B}\u{211D}
EOT;
// ℬÅℝ

And like other sequences such as \t, they will not be expanded when they occur in single-quoted strings or nowdocs:

These will not work
$foo = '\u{2109}\u{2134}\u{2134}';
// \u{2109}\u{2134}\u{2134}

$bar = <<<'EOT'
    \u{212C}\u{212B}\u{211D}
EOT;
// \u{212C}\u{212B}\u{211D}

Backwards Compatibility

Double-quoted strings and heredocs containing \u{ followed by an invalid sequence will now result in an error. This can be avoided by escaping the leading backslash with another backslash (\\u{).

Further Reading: