PHP 7 – Accelerating web development

This entry is part 3 of 3 in the series: PHP new features

PHP

The PHP community continues to add new features and functionality to this popular programming language in order to make it more attractive, functional and useful. The version 7 (released on December 3) is considered one of the major updates in all the history of PHP due the Zend Engine was refactored to achieve best performance and best use of the memory. Also new features that facilitate and accelerate web development were introduced.

Improved performance

Thanks to the version 3.0 of the Zend engine, PHP 7 is 2 times faster and consumes 50% less memory than PHP 5.6. For speed and performance comparisons see:

New types of variables as arguments

PHP is becoming a mixed language: dynamic and typed, example of this is that now are accepted new types of variables as argument. If the given value is of an incorrect type a TypeError exception will be thrown. Before PHP 7 we could use classes names or interface names, self, array and callable now PHP7 accepts the following types: int, float, bool and string.

The value of the strict_types directive affects the behavior of type declarations either as arguments or return, by default PHP will convert automatically from one type to another. In strict mode, the return value must be of the correct type.

Weak mode (default)

Fork me on Github
<?php
function test_param(int $a, bool $is_ready, string $str) {
    echo $a, ' => ', gettype($a), "\n";
    echo $is_ready, ' => ', gettype($is_ready), "\n";
    echo $str, ' => ', gettype($str), "\n";
}

class Test {
    public function __toString() {
        return Test::class;
    }
}

test_param(5.5, true, new Test());

// Print
// 5 => integer
// 1 => boolean
// Test => string

Strict mode

Fork me on Github
<?php
declare(strict_types = 1);

function test_param(int $a, bool $is_ready, string $str) {
    echo $a, ' => ', gettype($a), "\n";
    echo $is_ready, ' => ', gettype($is_ready), "\n";
    echo $str, ' => ', gettype($str), "\n";
}

class Test {
    public function __toString() {
        return Test::class;
    }
}

test_param(5, true, new Test());

// Print
// Fatal error: Uncaught TypeError: Argument 3 passed to test_param()
// must be of the type string, object given...

Return type declarations

Now function declaration accepts a return type similar to argument type declaration.

Weak mode (default)

Fork me on Github
<?php
function sum($a, $b): int {
    return $a + $b;
}

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

class Test {
    public function __toString() {
        return __CLASS__;
    }
}

function return_str(): string {
    return (new Test());
}

// Print 5
echo sum(3.5, 2), PHP_EOL;

// Print 5.5
echo sum_f(3.5, 2), PHP_EOL;

// Print Test
echo return_str(), PHP_EOL;

Strict mode

Fork me on Github
<?php
declare(strict_types=1);

function sum($a, $b): int {
    return $a + $b;
}

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

class Test {
    public function __toString() {
        return __CLASS__;
    }
}

function return_str(): string {
    return (new Test());
}

// Print
// Fatal error: Uncaught TypeError: Return value of sum() must be of the type
// integer, float returned...
echo sum(3.5, 2), PHP_EOL;

// Print 5.5
echo sum_f(3.5, 2), PHP_EOL;

// Print
// Fatal error: Uncaught TypeError: Return value of return_str() must be of the
// type string, object returned...
echo return_str(), PHP_EOL;

Null coalescing operator

Null coalescing operator (??) has been added as a shortcut to the ternary operator when the latter is used with isset. Returns the first operand if exists and not is null in other hand returns the second operand.

Fork me on Github
<?php
// Fetches the value of $_GET['user'] and returns 'nobody' if it does not exist.
$username = $_GET['user'] ?? 'nobody';

// This is equivalent to:
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';

Several ?? can be concatenated returning the first value defined and is not null.

Fork me on Github
<?php
$username = $_GET['user'] ?? $_POST['user'] ?? 'nobody';

The operator ?? has a meaning similar to the operator || used in other programming languages such as Javascript, Perl or Bash, but keep in mind that the operator || also checks if the value is false while ?? only checks if the value exists and is not null.

Fork me on Github
#!/bin/bash
# Print yes
perl -e '$a = $b || "yes"; print($a);'

# Print yes
php -r '$a = $b ?? "yes"; print_r($a);'

# Print yes
perl -e '$a = 0 || "yes"; print($a);'

# Print 0
php -r '$a = 0 ?? "yes"; print_r($a);'

# Print I have a value
perl -e '$a = "I have a value" || "yes"; print($a);'

# Print I have a value
php -r '$a = "I have a value" ?? "yes"; print_r($a);'

Spaceship operator

The spaceship operator is used to compare two expressions. If we compare $a with $b, then returns – 1 if $a < $b, 0 if $a = $b and 1 if $a > $b.

Fork me on Github
<?php
echo 1 <=> 1; //  0
echo 1 <=> 2; // -1
echo 2 <=> 1; //  1

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

Assigning an array to a constant

From PHP 7 you can assign an array to a constant using define statement.

Fork me on Github
<?php
define('DAYS', [
  'sun' => 'Sunday',
  'mon' => 'Monday',
  'tue' => 'Tuesday',
  'wed' => 'Wednesday',
  'thu' => 'Thursday',
  'fri' => 'Friday',
  'sat' => 'Saturday'
]);

// PrSunday
echo DAYS['sun'], PHP_EOL;

Anonymous classes

PHP 5.3 introduced the concept of anonymous functions now PHP 7 introduces the concept of anonymous classes and as its name tell us is a class that has not name. An anonymous class does a specific function and must never violate the principle of single responsibility. You can use an anonymous class if:

  • Your class not need to be documented
  • Your class has few methods and properties
  • Just need an instance during the execution of your application
  • Used immediately after its definition
  • Need to create objects on the flight
  • The name does not add clarity and legibility to the source code
  • You want to add or modify the behaviour of an existing class
  • Want to avoid the impact of load the class definition from an external file (hard disk)

From my point of view an anonymous class can be very useful for returning multiple values in a returning function and has a similar use as the object {} in Javascript.

Fork me on Github
<?php

define('PLAYERS', [
  '1' => ['Garry Kasparov', 2851],
  '2' => ['Anatoly Karpov', 2780],
  '3' => ['Magnus Carlsen', 2882],
  '4' => ['Bobby Fischer',  2785]
]);

interface PlayerDetailsInterface 
{
  public function getName();
  public function getRanking();
}

/**
 * Get Chess player detail
 * 
 * @return PlayerDetailsInterface
 */
function getChessPlayerDetails($id): PlayerDetailsInterface
{
  // Do some processing, consult some internet DB, Webservice or another resource
  $player = PLAYERS[$id];
  $name = $player[0];
  $ranking = $player[1];
  
  return new class($name, $ranking) implements PlayerDetailsInterface
  {
    private $name;
    private $ranking;
    
    public function __construct($name, $ranking) 
    {
      $this->name = $name;
      $this->ranking = $ranking;
    }
    
    public function getName() 
    {
      return $this->name;
    }
    
    public function getRanking() 
    {
      return $this->ranking;
    }
  };
}

foreach (array_keys(PLAYERS) as $id) {
  /**@var PlayerDetailsInterface $detail */
  $detail = getChessPlayerDetails($id);
  echo $detail->getName(), ': ', $detail->getRanking(), PHP_EOL;
}

/* Print
Garry Kasparov: 2851
Anatoly Karpov: 2780
Magnus Carlsen: 2882
Bobby Fischer: 2785
*/

You can see other uses cases at: anonymous classes

Group use declarations

Classes, functions and constants that are imported from the same namespace now can be grouped into a single use.

Grouping classes

Fork me on Github
<?php
// Before/Antes PHP 7
// use Symfony\Component\Form\Extension\Core\Type\TextType;
// use Symfony\Component\Form\Extension\Core\Type\DateType;
// use Symfony\Component\Form\Extension\Core\Type\SubmitType;

// PHP 7
use Symfony\Component\Form\Extension\Core\Type\{TextType,DateType,SubmitType};

Grouping functions

Fork me on Github
<?php
namespace Wordpress {

  function wp_is_writable( $path ) {
    if ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) )
        return win_is_writable( $path );
    else
        return @is_writable( $path );
  }

  function bool_from_yn( $yn ) {
    return ( strtolower( $yn ) == 'y' );
  }
}

// Global namespace
namespace {
  // Before/Antes PHP 7
  // use function Wordpress\wp_is_writable;
  // use function Wordpress\bool_from_yn;

  // PHP 7
  use function Wordpress\{wp_is_writable, bool_from_yn};

  if (wp_is_writable(__DIR__)) {
    echo __DIR__, ' is writeable', PHP_EOL;
  }

  echo bool_from_yn('y'), PHP_EOL;
}

Grouping constants

Fork me on Github
<?php
namespace Config {
  const DB_DRIVER = 'mysql';
  const DB_HOST   = 'localhost';
  const DB_PORT   = '3306';
  const DB_USER   = 'root';
}

// Global namespace
namespace {
  // Before/Antes PHP 7
  // use const Config\DB_DRIVER;
  // use const Config\DB_HOST;
  // use const Config\DB_PORT;
  // use const Config\DB_USER;

  // PHP 7
  use const Config\{DB_DRIVER, DB_HOST, DB_PORT, DB_USER};

  echo 'Connection details', PHP_EOL;
  echo 'Driver: ', DB_DRIVER, ', Host: ', DB_HOST, ', Port: ', DB_PORT, ', user: ', DB_USER, PHP_EOL;
}

Generators

The concept of generators was introduced from PHP 5.5. PHP 7 added 2 new features to generators

Return expressions

Generators now can return a final value using the sentence return (the return by reference is not allowed). This value can be obtained by using the new Generator:getReturn method, which can only be called after the generator has produced all previous values.

Fork me on Github
<?php

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

    return 3;
})();

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

echo $gen->getReturn(), PHP_EOL;

// Print
// 1
// 2
// 3

Generator delegation

Generators can now delegate to another generator, Traversable object or array using the yield from.

Fork me on Github
<?php
function gen()
{
    yield 1;
    yield 2;
    yield from gen2();
}

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

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

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

Unicode-escape syntax

It is now possible to escape unicode code in hexadecimal format and get the corresponding symbol. The following script checks if the runtime environment is cli and print the corresponding symbols in other hand assumes that the result will be displayed in the browser. Note that in case of display those symbols in the browser you can apply styles and use it as icons in buttons and others web components.

Fork me on Github
<?php
$is_cli = false !== strpos(PHP_SAPI, 'cli');
$nl = $is_cli ? PHP_EOL : nl2br(PHP_EOL);

if ($is_cli) {
    echo "\u{2665}", $nl;
    echo "\u{267B}", $nl;
    echo "\u{260E}", $nl;
} else {
    $style = '<a href="." style="font-size: 32px; color: %s; text-decoration: none;">%s</span>' . $nl;
    echo sprintf($style, 'green', "\u{2665}");
    echo sprintf($style, 'red', "\u{267B}");
    echo sprintf($style, 'blue', "\u{260E}");
    echo '<button type="button">', "\u{267B}", '</button>';
}

/**
 * Print
 *
 * ♥
 * ♻
 * ☎
 *
 */

Conclusion

PHP is not a wrapper of C language for the web because the community is adding new features that makes PHP a language with own features.

Further reading




Leave a Comment

Your email address will not be published. Required fields are marked *