PHP7 Features

At eNotes we use PHP7, the newest release in the evolution of the PHP programming language. Here is a quick highlight of some of the new language features, including some best practice recommendations.

Addition of the null-coalesce operator, ??.

This operator returns the lefthand operand if it is non-null, and the righthand operand otherwise. Additionally, and most importantly, this will not raise a notice if a variable or array key does not exist. It can also be chained, returning the first non-null value.

echo null ?? 42; // 42
echo $_GET['not-set'] ?? 42; // 42, no notice
echo $undefined ?? null ?? $_GET['not-set'] ?? 42; // 42, no notice

The most common place this will show up is when you want to use an array key from an external source (e.g. $_GET or $_POST) and fall back on a default value if it’s not set. For example:

$name = isset($_POST['name']) ? $_POST['name'] : 'Some User';

can now be written as:

$name = $_POST['name'] ?? 'Some User';

Recommendation: Use whenever necessary, and prefer null coalesce operator over isset() when appropriate.

Errors as exceptions

Many PHP errors (i.e. notices, warnings, some previously-fatal errors, etc.) are now thrown as exceptions. These extend Error and not Exception, but are still thrown like exceptions. This probably won’t make much of a difference in day-to-day development, but is nonetheless important to understand.

Return type declarations

Similar to how you can typehint on a parameter, (e.g. function foo(SomeClass $bar)), we can now also specify return types. Return types are specified by placing a colon followed by the type name after the function declaration, before the opening curly brace.

public function getQuestion() : Question {
    return Question::factory($this->questionID);
}

This check happens at run-time, and if an invalid type is returned, a TypeError will be thrown.

Some methods return a specific type or null. If null is a valid return, the type can be prefixed with ? to mark it as nullable:

public function getQuestionIfExists() : ?Question {
    return Question::getByID($this->questionID);
}

Recommendation: Always specify a return type.

Scalar type declarations (i.e. typehints for int, float, string, and bool).

Similar to how we can currently typehint with classes, PHP7 introduces type hints for scalar types. There’s two different methods for how scalar type hints are applied: non-strict mode (default) and strict mode. In non-strict mode, types will be coerced into expected type, if reasonable. In strict mode, the exact type must be passed in. This is set on a per-file basis, and applies only to calling a function, not the function being called.

example.php

<?php
function example(int $i) {
    var_dump($i);
}

non-strict.php

<?php
require_once 'example.php';
example(42); // int(42)
example("42"); // int(42)
example('obviously not an int'); // Fatal error: Uncaught TypeError: Argument 1 passed to example() must be of the type integer, string given

strict.php

<?php
//enable strict mode in this file
declare(strict_types=1);

require_once 'example.php';

example(42); // int(42)

// since we're now in strict mode, this will fail
example("42"); // Fatal error: Uncaught TypeError: Argument 1 passed to example() must be of the type integer, string given

Note that the same strict/non-strict rules also apply to return type declarations.

Recommendation: Always specify scalar type declarations for parameters and return types.

Recommendation: Prefer strict-mode over non-strict mode.

Anonymous classes

Anonymous classes are unnamed classes that can be used to eliminate the overhead of additional class files when they are not really needed. There are not a ton of everyday use cases for anonymous classes, but the anonymous class documentation expands on possible applications.

Recommendation: Use anonymous classes if you feel it’s appropriate, but in general or if unsure, just define a named class.

Expectations (zero-cost assertions).

PHP has long since provided an assert() function, that will evaluate an expression, and fail if the expression does not return true.

assert(1 == 2); // Fatal error: Uncaught AssertionError: assert(1 == 2)

As of PHP7, this can now be completely disabled in production (via php.ini). This causes the parser to completely ignore the code, meaning there is zero impact on performance. As such, this can be a handy tool to use in development. Note that this meant as a development tool and is not a substitute for proper error handling.

Recommendation (php.ini): make sure zend.assertions is set to 1 in development environments and -1 in production environments; make sure assert.exception is set to 1 in development environments and 0 in production environments.

Recommendation: Feel free to make use of assert(), but remember that this is a development-only tool and not a substitute for proper error handling. Operate under the assumption that assertions are disabled, but that you’ll get useful debug information if enabling them. Think of an assertion like a comment.

The syntax of how some variables are defined has changed.

Read the new syntax documentation and make sure you understand the differences.

These are some of the newer features that I feel are most important. For complete reference, see the complete feature list.