Thursday, July 20, 2017

New Features In PHP 7 That You Should Have A Look At

Now a days PHP is the most preferred programming language. But is PHP 7 the most exciting releases of them all? Yes. The whole PHP community and the people linked to PHP, are all excited to welcome the biggest release for PHP in decades. It surely adds more versatility to the already versatile language.

New Features In PHP 7

But you must be wondering why PHP named its latest release PHP 7 and not PHP 6. Reason behind it is that, many of the PHP 6 releases were already implemented in PHP 5.3 and later, there was not really a proper reason just to change the name. What I am trying to say here is that we haven't missed anything. Just to avoid the confusion with a dead project, PHP's latest release was named to PHP 7.

Let's take a deeper dive. Let's check out what new features PHP 7 has to offer. And what improvements those features will bring forth.

  • Performance (Speed Improvement)
  • Scalar Type Declarations
  • Return Type Declarations
  • Combined Comparison Operator
  • Null Coalesce Operator
  • Anonymous Classes
  • Unicode Codepoint Escape Syntax
  • Closure call() Method
  • Filtered unserialize()
  • IntlChar Class
  • Expectations
  • Group use Declarations
  • Generator Return Expressions
  • Generator Delegation
  • Integer Division with intdiv()
  • session_start() Options
  • preg_replace_callback_array() Function
  • CSPRNG Functions
  • Support for Array Constants in define()
  • Reflection Additions

Let's take a look on every feature in details:

Performance (Speed Improvement)


The developers for the PHP 7 has done a pretty commendable job here. Now your PHP codebase uses less memory and gives you even more performance than before. This is a result of refactoring the Zend Engine to use more compact data structures and less heap allocations/deallocations. The performance gains on real world applications will vary, though many applications seem to receive a ~100% performance boost - with lower memory consumption too! After it's release, internet was overloaded with benchmarks which were really promising. It is almost a 2x increase in server response times with PHP 7. For further details on benchmarks 


Scalar Type Declarations


PHP 7 has now added Scalar type declaration for int, float, string, and boolean. Adding scalar type declaration and enabling strict requirements ensures that more correct and well-documented PHP programs can be written. It also helps you in gaining more control over your code and make the code easier to read.
By default, on PHP 7, these declarations are non-strict. Which means that the type forging is possible. As if you pass a string starting with a number into a float type function, it grabs the number from in the start and skips everything else. Passing a float into a function that requires an int, that float will become int.

Non-Strict Example

[code]
function getSum(float $a, float $b) {
   return $a + $b;
}
getSum(6, "7 week");
//Here int(6) changed to float(6.0) and string "7 week" changed to float(7.0)
//with a "Notice: A non well formed numeric value encountered"
//returns float(13)
getSum(1.1, "2.2");
//Here string "2.2" is changed to float(2.2) without any notice
//returns float(3.3)
getSum(3.1, 2);
// changes int(2) to float(2.0)
// returns int(5.1)
[/code]

Here the getSum function receives 2 floats and adds them together returning the sum. When you use a non-strict type declaration in PHP 7. It will reforge these arguments to match the type specified in the function. Which means whatever the argument we pass, PHP will convert it to float.

Strict Example

PHP 7 additionally gives us the opportunity to strict the declaration type. It is achieved by adding "strict_types=1" on the very first line of the file. This ensures that any calls made to the functions specified must strictly adhere to the specified types. Strict is determined in the file in which the call to a function is made and not the file in which the function is defined.
While using a strict type-declaration, if a mismatch occurs, a "Fatal Error" occurs and we know that something is not functioning as desired. This helps in not causing random and confusing diagnose issues. Let's just cut the talk and take a look at an example with strict types turned on.

declare(strict_types=1);
function getSum(float $a, float $b) {
    return $a + $b;
}

getSum(3, "2 week");
// Fatal error: Uncaught TypeError: Argument 2 passed to getSum() must be of the type float, string given

getSum(1.8,  "4.5");
// Fatal error: Uncaught TypeError: Argument 2 passed to getSum() must be of the type float, string given

getSum(3.1, 2);
// int(2) change to float(2.0)
//returns float(5.1)

Setting "declare strict_type" to "1", the first two calls that pass a string produces a Fatal error: "Uncaught TypeError: Argument 2 passed to getSum() must be of the type float, string given". With only the exception in the third call, in which if you pass an integer for an argument instead of a float value, PHP will perform “widening”, which includes adding .0 at the end of the integer value. This returns (5.1).

Return Type Declaration


The third type of declaration that PHP 7 supports are a Return Type Declaration. It supports all similar type arguments as a return. Take look at the example of how to specify a return type declaration.

function getSum(float $a, float $b) : float {

}

Adding a return type ensures that only an expected value type returns. For the previous two examples if we set the return type float it will work the same. As the values being returned are already float. So we will be doing an example for int. return types.

Non-Strict Integer Example

Without the strict type declaration on, if we specify the return type as int for the previous examples, it will work the same. With just the difference being, that return will be forged to an integer. Which means it will truncate the float value and only returns the integer.

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

getSum(6, "7 week");
// changes int(6) to float(6.0) & string("7 week") to float(7.0)
// returns int(13);

getSum(1.1, "2.2");
// changes string "2.2" to float(2.2)
// returns int(3.3)

getSum(3.1, 2);
// changes int(2) to float(2.0)
// returns int(5.1)


Strict Integer Example

If we turn strict types on, we’ll get a Fatal error: Uncaught TypeError: Return value of getSum() must be of the type integer, float returned. For this case we’ll be casting our return value as an int. which then returns the truncated value.

declare(strict_types=1);
function getSum(float $a, float $b) : int {
    // return $a + $b;
    // The above statement shows Fatal error: Uncaught TypeError: Return value of getSum() must be of the type integer, float returned
    return (int)($a + $b); // truncate float like non-strict
}
getSum(3.1, 2); // changes int(2) to float(2.0) and returns int(5.1)

Benefits
These new implementations of Type Declaration certainly help in making the code easier to read. With PHP 7 you get a versatile type declaration methods which makes your life easier. You can even see at the start of the function, what is required and what will be returned.

Combined Comparison Operator


The combined comparison operator (or spaceship operator) is a shorthand notation for performing three-way comparisons from two operands. It has an integer return value that can be either:


  • a positive integer (if the left-hand operand is greater than the right-hand operand)
  • 0 (if both operands are equal)
  • a negative integer (if the right-hand operand is greater than the left-hand operand)


The operator has the same precedence as the equality operators (==, !=, ===, !==) and has the exact same behaviour as the other loose comparison operators (<, >=, etc). It is also non-associative like them too, so chaining of the operands (like 1 <=> 2 <=> 3) is not allowed.

// compares strings lexically
var_dump('PHP' <=> 'Node'); // int(1)

// compares numbers by size
var_dump(123 <=> 456); // int(-1)

// compares corresponding array elements with one-another
var_dump(['a', 'b'] <=> ['a', 'b']); // int(0)
Objects are not comparable, and so using them as operands with this operator will result in undefined behaviour.

Null Coalesce Operator


The null coalesce operator (or isset ternary operator) is a shorthand notation for performing isset() checks in the ternary operator. This is a common thing to do in applications, and so a new syntax has been introduced for this exact purpose.

// Pre PHP 7 code
$route = isset($_GET['route']) ? $_GET['route'] : 'index';

// PHP 7+ code
$route = $_GET['route'] ?? 'index';


// compares corresponding array elements with one-another
var_dump(['a', 'b'] <=> ['a', 'b']); // int(0)
Objects are not comparable, and so using them as operands with this operator will result in undefined behaviour.

Null Coalesce Operator

The null coalesce operator (or isset ternary operator) is a shorthand notation for performing isset() checks in the ternary operator. This is a common thing to do in applications, and so a new syntax has been introduced for this exact purpose.

// Pre PHP 7 code
$route = isset($_GET['route']) ? $_GET['route'] : 'index';

// PHP 7+ code
$route = $_GET['route'] ?? 'index';

Anonymous Classes


Anonymous classes are useful when simple, one-off objects need to be created.

// Pre PHP 7 code
class Logger
{
    public function log($msg)
    {
        echo $msg;
    }
}

$util->setLogger(new Logger());

// PHP 7+ code
$util->setLogger(new class {
    public function log($msg)
    {
        echo $msg;
    }
});

They can pass arguments through to their constructors, extend other classes, implement interfaces, and use traits just like a normal class can:

class SomeClass {}
interface SomeInterface {}
trait SomeTrait {}

var_dump(new class(10) extends SomeClass implements SomeInterface {
    private $num;

    public function __construct($num)
    {
        $this->num = $num;
    }

    use SomeTrait;
});

/** Output:
object(class@anonymous)#1 (1) {
  ["Command line code0x104c5b612":"class@anonymous":private]=>
  int(10)
}
*/
Nesting an anonymous class within another class does not give it access to any private or protected methods or properties of that outer class. In order to use the outer class' protected properties or methods, the anonymous class can extend the outer class. To use the private or protected properties of the outer class in the anonymous class, they must be passed through its constructor:

<?php

class Outer
{
    private $prop = 1;
    protected $prop2 = 2;

    protected function func1()
    {
        return 3;
    }

    public function func2()
    {
        return new class($this->prop) extends Outer {
            private $prop3;

            public function __construct($prop)
            {
                $this->prop3 = $prop;
            }

            public function func3()
            {
                return $this->prop2 + $this->prop3 + $this->func1();
            }
        };
    }
}

echo (new Outer)->func2()->func3(); // 6

Unicode Codepoint Escape Syntax


This enables a UTF-8 encoded unicode codepoint to be output in either a double-quoted string or a heredoc. Any valid codepoint is accepted, with leading 0's being optional.

echo "\u{aa}"; // ª
echo "\u{0000aa}"; // ª (same as before but with optional leading 0's)
echo "\u{9999}"; // 香

Closure call() Method


The new call() method for closures is used as a shorthand way of invoking a closure whilst binding an object scope to it. This creates more perfomant and compact code by removing the need to create an intermediate closure before invoking it.

class A {private $x = 1;}

// Pre PHP 7 code
$getXCB = function() {return $this->x;};
$getX = $getXCB->bindTo(new A, 'A'); // intermediate closure
echo $getX(); // 1

// PHP 7+ code
$getX = function() {return $this->x;};
echo $getX->call(new A); // 1

Filtered unserialize()


This feature seeks to provide better security when unserializing objects on untrusted data. It prevents possible code injections by enabling the developer to whitelist classes that can be unserialized.

// 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]);

IntlChar Class


The new IntlChar class seeks to expose additional ICU functionality. The class itself defines a number of static methods and constants that can be used to manipulate unicode characters.

printf('%x', IntlChar::CODEPOINT_MAX); // 10ffff
echo IntlChar::charName('@'); // COMMERCIAL AT
var_dump(IntlChar::ispunct('!')); // bool(true)
In order to use this class, the Intl extension must be installed.

BC Breaks

Classes in the global namespace must not be called IntlChar.

Expectations


Expectations are backwards compatible enhancement to the older assert() function. They enable for zero-cost assertions in production code, and provide the ability to throw custom exceptions on error.

The assert() function's prototype is as follows:

void assert (mixed $expression [, mixed $message]);
As with the old API, if $expression is a string, then it will be evaluated. If the first argument is falsy, then the assertion fails. The second argument can either be a plain string (causing an AssertionError to be triggered), or a custom exception object containing an error message.

ini_set('assert.exception', 1);

class CustomError extends AssertionError {}

assert(false, new CustomError('Some error message'));
With this feature comes two PHP.ini settings (along with their default values):


  • zend.assertions = 1
  • assert.exception = 0

zend.assertions has three values:


  • 1 = generate and execute code (development mode)
  • 0 = generate code and jump around at it at runtime
  • -1 = don't generate any code (zero-cost, production mode)

assert.exception means that an exception is thrown when an assertion fails. This is switched off by default to remain compatible with the old assert() function.

Group use Declarations


This gives the ability to group multiple use declarations according to the parent namespace. This seeks to remove code verbosity when importing multiple classes, functions, or constants that come under the same namespace.

// 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};

Generator Return Expressions


This feature builds upon the generator functionality introduced into PHP 5.5. It enables for a return statement to be used within a generator to enable 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 finishing yielding values.

// IIFE syntax now possible - see the Uniform Variable Syntax subsection in the Changes section
$gen = (function() {
    yield 1;
    yield 2;

    return 3;
})();

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

echo $gen->getReturn(), PHP_EOL;

// output:
// 1
// 2
// 3
Being able to explicitly return a final value from a generator 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 specifically handled by the client code executing the generator. This is far simpler than forcing the client code to firstly check whether the final value has been yielded, and then if so, to handle that value specifically.

Generator Delegation


Generator delegation builds upon the ability of being able to return expressions from generators. It does this by using an new syntax of yield from <expr>, where can be any Traversable object or array. This will be advanced until no longer valid, and then execution will continue in the calling generator. This feature enables yield statements to be broken down into smaller operations, thereby promoting cleaner code that has greater reusability.

function gen()
{
    yield 1;
    yield 2;

    return yield from gen2();
}

function gen2()
{
    yield 3;

    return 4;
}

$gen = gen();

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

echo $gen->getReturn();

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

Integer Division with intdiv()


The intdiv() function has been introduced to handle division where an integer is to be returned.

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

session_start() Options


This feature gives the ability to pass in an array of options to the session_start() function. This is used to set session-based php.ini options:

session_start(['cache_limiter' => 'private']); // sets the session.cache_limiter option to private
This feature also introduces a new php.ini setting (session.lazy_write) that is, by default, set to true and means that session data is only rewritten if it changes.

preg_replace_callback_array() Function


This new 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 (second parameter of preg_replace_callback()) to be polluted with lots of branching (a hacky method at best).

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.

Function Signature:

string preg_replace_callback_array(array $regexesAndCallbacks, string $input);
$tokenStream = []; // [tokenName, lexeme] pairs

$input = <<<'end'
$a = 3; // variable initialisation
end;

// Pre PHP 7 code
preg_replace_callback(
    [
        '~\$[a-z_][a-z\d_]*~i',
        '~=~',
        '~[\d]+~',
        '~;~',
        '~//.*~'
    ],
    function ($match) use (&$tokenStream) {
        if (strpos($match[0], '$') === 0) {
            $tokenStream[] = ['T_VARIABLE', $match[0]];
        } elseif (strpos($match[0], '=') === 0) {
            $tokenStream[] = ['T_ASSIGN', $match[0]];
        } elseif (ctype_digit($match[0])) {
            $tokenStream[] = ['T_NUM', $match[0]];
        } elseif (strpos($match[0], ';') === 0) {
            $tokenStream[] = ['T_TERMINATE_STMT', $match[0]];
        } elseif (strpos($match[0], '//') === 0) {
            $tokenStream[] = ['T_COMMENT', $match[0]];
        }
    },
    $input
);

// PHP 7+ code
preg_replace_callback_array(
    [
        '~\$[a-z_][a-z\d_]*~i' => function ($match) use (&$tokenStream) {
            $tokenStream[] = ['T_VARIABLE', $match[0]];
        },
        '~=~' => function ($match) use (&$tokenStream) {
            $tokenStream[] = ['T_ASSIGN', $match[0]];
        },
        '~[\d]+~' => function ($match) use (&$tokenStream) {
            $tokenStream[] = ['T_NUM', $match[0]];
        },
        '~;~' => function ($match) use (&$tokenStream) {
            $tokenStream[] = ['T_TERMINATE_STMT', $match[0]];
        },
        '~//.*~' => function ($match) use (&$tokenStream) {
            $tokenStream[] = ['T_COMMENT', $match[0]];
        }
    ],
    $input
);

CSPRNG Functions


This feature introduces two new functions for generating cryptographically secure integers and strings. They expose simple APIs and are platform-independent.

Function signatures:

string random_bytes(int length);
int random_int(int min, int max);
Both functions will emit an Error exception if a source of sufficient randomness cannot be found.

Support for Array Constants in define()


The ability to define array constants was introduced in PHP 5.6 using the const keyword. This ability has now been applied to the define() function too:

define('ALLOWED_IMAGE_EXTENSIONS', ['jpg', 'jpeg', 'gif', 'png']);

Reflection Additions


Two new reflection classes have been introduced in PHP 7. The first is ReflectionGenerator, which is used for introspection on generators:

class ReflectionGenerator
{
    public __construct(Generator $gen)
    public array getTrace($options = DEBUG_BACKTRACE_PROVIDE_OBJECT)
    public int getExecutingLine(void)
    public string getExecutingFile(void)
    public ReflectionFunctionAbstract getFunction(void)
    public Object getThis(void)
    public Generator getExecutingGenerator(void)
}
The second is ReflectionType to better support the scalar and return type declaration features:

class ReflectionType
{
    public bool allowsNull(void)
    public bool isBuiltin(void)
    public string __toString(void)
}
Also, two new methods have been introduced into ReflectionParameter:

class ReflectionParameter
{
    // ...
    public bool hasType(void)
    public ReflectionType getType(void)
}
As well as two new methods in ReflectionFunctionAbstract:

class ReflectionFunctionAbstract
{
    // ...
    public bool hasReturnType(void)
    public ReflectionType getReturnType(void)

}

These were the new features in PHP 7.

2 comments:

  1. Nice one! OnceBuilder runs on PHP 5.3 and above.

    ReplyDelete
  2. It might seem like you’re required to go out and beg rich people for money, but all you need is a WordPress donation plugin and a few other elements.

    ReplyDelete