PHP 7 new features

Scalar type declarations

Скаларни, защото в PHP 5 е можело типът на входните параметри да е или клас или array. Тоест, скаларни типове не е имало. Ако пробваш нещо от рода на:

function patapan(string $str){...}

ще мисли, че string е име на клас, а не скаларен тип и ще гръмне с нещо от рода на „Argument 1 must be instance of class string, string given“.

Strict types

С директивата declare(strict_types=1); може да се зададе на PHP стриктно да съблюдава типът на желаните входни параметри и на изходният резултат. Тоест, PHP по дефолт е сервилен език откъм типове, той ще направи каквото може за да замаже type mismatch грешки. Ако например функция очаква int, а ти и подадеш ‘123ivan’, PHP ще опита да замаже ситуацията като вземе само 123 и отреже ‘ivan’.
Хубаво, но ако declare(strict_types=1); тогава изрично казваш на PHP да не „замазва“ такива случаи, а да гърми.
Without strict types turned on, PHP attempts to cast or change these arguments to match the type specified in the function.

Същото важи и за връщане на резултата.

Error handling

За фатални грешки вече няма да спира изпълнението на скрипта, а ще се хвърли exception от клас Error.
Отделно, механизмът за хвърляне на ексепшъни пак си остава същият – подобен на ексепшън от клас Exception.
Tънкият момент е, че и Error и Exception имплементират един интерфейс Throwable, което значи, че на практика работят по един и същ начин в смисъл, че всеки от тях го имплементира този интерфейс по свой начин.

class Error implements Throwable { ... }
class Exception implements Throwable { ... }

Йерархията е следната:
Exception implements Throwable
….
Error implements Throwable
TypeError extends Error
ParseError extends Error
ArithmeticError extends Error
DivisionByZeroError entends Error
AssertionError extends Error

А самият Throwable интерфейс, добавен в PHP 7 е следният:

interface Throwable {
     abstract public getMessage ( void ) : string
     abstract public getCode ( void ) : int
     abstract public getFile ( void ) : string
     abstract public getLine ( void ) : int
     abstract public getTrace ( void ) : array
     abstract public getTraceAsString ( void ) : string
     abstract public getPrevious ( void ) : Throwable
     abstract public __toString ( void ) : string
 }

Spaceship operator, a.k.a. Combined Comparison Operator

$compare = $a <=> $b;
$compare ще е -1 ако $а < $b
$compare ще е  0 ако $а = $b 
$compare ще е  1 ако $а > $b 

Null Coalesce Operator

$name = $firstName ?? ‘Anonimous’;
Ще върне левият операнд ако не е NULL, иначе – десният

Constant arrays

define('ANIMALS', [
    'dog',
    'cat',
    'bird'
]);

echo ANIMALS[1];    // outputs "cat"

Anonymous classes

https://www.php.net/manual/en/language.oop5.anonymous.php

Има случаи, в които е по-практично да предадеш като аргумент на дадена функция обект, от който нямаме клас. Тогава можем да създадем т.н. „anonimous class“, който да подадем като аргумент. Без име, просто като аргумент, това е напълно пълнокръвен клас, може да наследява друг клас, да имплементира интерфейси, да „юзва“ трейтове…

Unicode codepoint escape syntax

Unicode codepoint-ове могат да се използват, например:
echo „\u{aa}“;
echo „\u{0000aa}“;
echo „\u{9999}“;

Closure::call()

Имаме един прост клас:

<?php
class Value
{
    public function __construct(private int $value)
    {
    }
}

Създаваме два обекта от него:

$three = new Value(3);
$four = new Value(4);

Нека отделно имаме и един клоужър:

$closure = function ($delta) {
    return $this->value + $delta;
};

Aко напишем това:

var_dump($closure->call($three, 4));
var_dump($closure->call($four, 14));

това значи, че викаме клуожъра два пъти, като всеки път му подаваме отделните обекти. Вътре в извиканият клоужър, можем да използваме даденият обект $this както и частните му пропъртите и методи. Следователно, ше получим:

int(7)
int(18)

Къде е особеността?

Всичко това можем да го постигнем и с една обикновена функция, на която пак просто ще подадем някой от двата обекта, но няма да можем да имаме достъп до частните му пропъртита и методи. This is because it binds the object scope with a closure at runtime without inheritance, composition, and so on.

Toва по принцип би било полезно ако в отделни моменти от изпълнението, искаме да правим различни неща с текущите състояния на обекта.

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.

Демек, можеш да зададеш какво да „прескочи“ когато прави unserialize()

// default behaviour that accepts all classes
// second argument can be ommited.
// if allowed_classes is passed as false, unserialize converts all objects into __PHP_Incomplete_Class object
$data = unserialize($serializedObj1 , ["allowed_classes" => true]);
// converts all objects into __PHP_Incomplete_Class object except those of MyClass1 and MyClass2
$data2 = unserialize($serializedObj2 , ["allowed_classes" => ["MyClass1", "MyClass2"]]);

IntlChar

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.

In PHP7, a new IntlChar class is added, which seeks to expose additional ICU functionality. This class defines a number of static methods and constants, which can be used to manipulate unicode characters. You need to have Intl extension installed prior to using this class.

Expectations

???

Grouping use declarations

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

Integer division with intdiv()

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

Session options

session_start() now accepts an array of options that override the session configuration directives normally set in php.ini

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

Generator Return Expressions

This 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).

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

    return 3;
})();

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

echo $gen->getReturn(), PHP_EOL;

The above example will output:

1
2
3

Идеята е да можем както и да използваме генратора по стандартният начин, така и накрая на функцията, да може да върнем обикновена стойност.

blog.eduonix.com/web-programming-tutorials/learn-use-generator-return-expressions-delegation/

Generator delegation

Generators can now delegate to another generator, Traversable object or array automatically, without needing to write boilerplate in the outermost 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;
}

The above example will output:

1
2
3
4

blog.eduonix.com/web-programming-tutorials/learn-use-generator-return-expressions-delegation/

Nullable types

function testReturn(): ?string
{
    return 'elePHPant';
}
function test(?string $name)
{
    var_dump($name);
}

Void functions

function swap(): void
{
    if ($left === $right) {
        return;
    }
    $tmp = $left;
    $left = $right;
    $right = $tmp;
}

Symmetric array destructuring

Различни, елегантни начини за работа с масиви като: пренареждане на елементите, филтриране на нужните, изтриване на ненужните и т.н…

Class constant visibility

class ConstDemo
{
    const PUBLIC_CONST_A = 1;
    public const PUBLIC_CONST_B = 2;
    protected const PROTECTED_CONST = 3;
    private const PRIVATE_CONST = 4;
}

iterable pseudo-type

Нов тип на параметър или на връщан тип, указващ, че параметърът трябва да е „array or object implementing the Traversable interface“, тоест, например масив или обект, който може да бъде изциклян с foreach например.

Multi catch exception handling

try {
    // some code
} catch (FirstException | SecondException $e) {
    // handle first and second exceptions
}

Support for negative string offsets

In such cases, a negative offset is interpreted as being an offset from the end of the string.

New object type

function test(object $obj): object
{
    return new SplQueue();
}

test(new StdClass()); 

Като подаденият обект $obj може да е обект от който и да е клас. Просто да е обект.

Abstract method overriding

Abstract methods can now be overridden when an abstract class extends another abstract class.

abstract class A
{
    abstract function test(string $s);
}

abstract class B extends A
{
    // overridden - still maintaining contravariance 
    // for parameters and covariance for return
    abstract function test($s): int;
}

Parameter type widening (after 7.2)

Parameter types from overridden methods and from interface implementations may now be omitted. This is still in compliance with LSP, since parameters types are contravariant.

Идеята е да се премахне задължението оверрайдващите методи да бъдат заставяни да имат същите типове параметри (не връщан резултат, само за параметрите говорим) като съответните родителски методи.

Важно уточнение 1: тук declare(strict_types = 1); не играе роля!
Важно уточнение 2: не важи за типът на връщаният резултат!
Важно уточнение 3: важи само когато пропускаме типа на параметъра, ако напишем друг тип – тогава имаме грешка. Тоест, ако имаме:

interface A
{
    public function Test(array $input);
}

class B implements A
{
    public function Test($input)    // type omitted for $input
    {
        var_dump($input);
    }
}

(new B)->Test(123);

До 7.1.* горният пример ще гръмне с „Declaration of B::Test($input) must be compatible with A::Test(array $input)“

Но след 7.2.* вече всичко е наред.

https://php.budgegeria.de/blog/how-the-parameter-type-widening-feature-of-php-72-can-influence-development

Allow a trailing comma for grouped namespaces

A trailing comma can now be added to the group-use syntax introduced in PHP 7.0.

use Foo\Bar\{
    Foo,
    Bar,
    Baz,
};

Typed properties

Class properties now support type declarations.

class User
{
    public int $id;
    public string $name;
}

Arrow functions

Arrow functions provide a shorthand syntax for defining functions with implicit by-value scope binding.

$factor = 10;
$nums = array_map(fn(int $n) => $n * $factor, [1, 2, 3, 4]);
//$nums = array(10, 20, 30, 40);

Limited return type covariance and argument type contravariance

Full variance support is only available if autoloading is used. Inside a single file only non-cyclic type references are possible, because all classes need to be available before they are referenced.

class A {}
class B extends A {}

class Producer
{
    public function method(): A { }
}

class ChildProducer extends Producer
{
    public function method(): B { }
} 

В този пример имаме ковариантност за връщаният тип, защото method() на наследника, връща по-конкретният тип B, a не по-общият – родителят А.

Null coalescing assignment operator

$array['key'] ??= computeDefault();

// is roughly equivalent to
if (!isset($array['key'])) {
    $array['key'] = computeDefault();
}

Unpacking inside arrays

$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
// ['banana', 'orange', 'apple', 'pear', 'watermelon'];

Numeric literal separator

Numeric literals can contain underscores between digits.

6.674_083e-11; // float
299_792_458;   // decimal
0xCAFE_F00D;   // hexadecimal
0b0101_1111;   // binary

Каква е разликата между null, когато е…

Каква е разликата между:
когато подава null като дефолт стойност на параметър
и когато задаваш, че параметърът може и да е null

Демек:
function registerServices(int $id = null){…}
function registerServices(?int $id){…}

Съвсем различни са:

$id = null показва, че може и ДА НЯМА подаден параметър, в който случай се приема, че е подаден null или каквато и да е там дефолтна стойност.

?$id показва, че ТРЯБВА ЗАДЪЛЖИТЕЛНО да има подадена стойност за този парам, но МОЖЕ И ДА Е NULL

Демек:
1. може и да няма нищо
2. не може да няма нещо, ако ще и да е null