PHP 7.1: Огляд нових можливостей

imageНа Хабре вже був переклад з оглядом кілька місяців тому, але нещодавно вийшов перший реліз-кандидат PHP 7.1, а значить ніяких істотних змін більше не буде і можна сказати, які точно зміни будуть в релізі. Я вирішив трохи оживити сухий «changelog» своїм вільним перекладом змін, які принесе нам новий мінорна версія 7.х гілки.

Нова функціональність
Додано повертається тип «void» (RFC
Тепер функції та методи, які не мають нічого повертати, можна позначати її обчислене типом void:

function someNethod(): void {
// працює якщо return відсутній
// працює з return;
// не працює якщо return null;
// не працює якщо return 123;
}

Повернення якогось значення методу/функції, який позначений як void, буде генерувати виключення рівня Fatal Error. Зверніть увагу, що NULL значення не прирівнюється до void (відсутності значення), тобто повертати NULL не можна.

До речі, це не значить що $x = someNethod(); не поверне нічого. Як і раніше в $x NULL. Так само void можна використовувати як тип параметра.


function bar(void $foo) {}
// Викине: Fatal error: void cannot be used as a parameter type in....

Доданий новий псевдо-тип: «iterable» (RFC

function walkList(iterable $list): iterable {
foreach ($list as $value) {
yield $value['id'];
}
}

Цей тип, по суті, об'єднує примітивний тип array і інтерфейс Traversable (а значить і його похідні: Iterator, Generator, etc). Проблема виникла на ґрунті того, що наприклад, foreach може працювати з обома типами, але функція з типом array не прийме об'єкт з інтерфейсом Traversable і навпаки.

Так само в рамках цього RFC була додана нова функція is_iterable(), яка працює аналогічно іншим is_* функціям.

З'явилася можливість дозволяти null типізованих і повертаються параметрах (Nullable RFC

function callMethod(?Bar $bar): ?Bar {}
$this->callMethod($bar); // Працює
$this->callMethod(null); // Працює
$this->callMethod(); // НЕ працює

Зверніть увагу, що використання "?" та значення null за замовчуванням не одне і теж що


function callMethod(int $bar = null) {}
$this->callMethod(1); // Працює
$this->callMethod(null); // Працює
$this->callMethod(); // Теж працює

Причому додавання "?" залишає поведінка назад сумісним

function callMethod(?Bar $bar = null) {}
// Працює так само як і без "?"

Також важливий момент по спадкуванню:


interface Fooable {
function foo(int $i): ?Fooable;
}
interface StrictFooable extends Fooable {
function foo(?int $i): Fooable; // valid
}

В спадкоємця можна робити «суворіше» зворотний тип (тобто забороняти nullable), а параметр навпаки розширювати до nullable, АЛЕ не навпаки!

Додана можливість використовувати негативне значення для зміщення у рядках (RFC
echo $msg[-1]; // поверне останній символ
echo $msg{-3}; // Причому RFC явно рекомендує використовувати спосіб $str{} так як $str[] може збивати з пантелику І в майбутньому може бути оголошений як застарілим.

Від'ємні значення так само стали дозволені в деяких строкових функціях: strpos, stripos, substr_count, grapheme_strpos, grapheme_stripos, grapheme_extract, iconv_strpos, file_get_contents, mb_strimwidth, mb_ereg_search_setpos, mb_strpos, mb_stripos.

Скрізь це означає вважати зміщення з кінця рядка.

Дозволено використовувати рядкові ключі в конструкції list() (RFC
Так само був доданий короткий синтаксис для listRFC).


["test" => $a, "name" => $b] = ["name" => "Привіт", "test" => "World!"];
var_dump($a); // World!
var_dump($b); // Hello

Особливості:

  • не можна використовувати змішаний синтаксис (якщо вказуємо ключі — то вказуємо їх скрізь, якщо ні, то використовуються звичайні індекси 0, 1, 2… як зазвичай):

    
    // Parse error: syntax error, ...
    ["a" => $a, $b] = ["a" => 1, 2]
    

  • порожні елементи з ключами теж не можна:

    
    // Parse error: syntax error, ...
    list (,,,, "key" => $keyed) = $array;
    

  • якщо ключа у вихідному масиві немає, то буде викинуто попередження Notice: Undefined index: name, а в змінної буде NULL

  • при використанні вкладеної конструкції list способи можна комбінувати

    
    $points = [
    ["x" => 1, "y" => 2],
    ["x" => 2, "y" => 1]
    ];
    [["x" => $x1, "y" => $y1], ["x" => $x2, "y" => $y2]] = $points;
    
Конвертація callable виразів замикання (RFC
Closure::fromCallable(callable $calback);

Ось наочний приклад застосування:

class A {
public function getValidator(string $name = 'byDefault') {
return Closure::fromCallable([$this, $name]);
}
private function byDefault(...$options) {
echo "Private with default:".print_r($options, true);
}
public function __call ( string $name , array $args ) {
echo "Call $name with:".print_r($args, true);
}
}

$a = new A();
$a->getValidator("test")(1,2,3);
// Call test with: Array ( [0] = > 1 [1] = > 2 [2] => 3 )
$a->getValidator()('p1', 'p2');
// Private with default: Array ( [0] => 'p1', [1] => 'p2')
// Увагу Closure::fromCallable передає контекст ($this) в момент виклику всередину замикання, тим самим дозволяючи звертатися до приватних методів
// якщо залишити тільки return [$this, $name]; 
$a->getValidator()('p1', 'p2');
// поверне 
// Call byDefault with:Array ( [0] => p1 [1] => p2 )
// тобто викличе тільки публічний метод і не буде мати доступу до приватних методів об'єкта

Підтримка модифікаторів видимості для констант класу (RFC

class Token {
// Константа без модифікатора за замовчуванням "public"
const PUBLIC_CONST = 0;

// Константи з різною областю видимості
private const PRIVATE_CONST = 0;
protected const PROTECTED_CONST = 0;
public const PUBLIC_CONST_TWO = 0;

// Весь список має одну область видимості
private const FOO = 1, BAR = 2;
}

Ловити виключення можна объеденяя кілька типів виключень в один блок (RFC

try {
echo "OK";
} catch (Exception | DomainException $e) {
// ... обробка 2ух типів виключень відразу
} catch (TypeError $e) {
// ...
}

Викиди помилок рівня E_NOTICE and E_WARNING при арифметичні операції над рядками містять не валідні числа (RFC

$numberOfApples = "10 apples" + "5 pears";
// Викине
// Notice: A non well formed numeric string encountered in example.php on line 3
// Notice: A non well formed numeric string encountered in example.php on line 3
$numberOfPears = 5 * "orange";
// Warning: A non-numeric string encountered in example.php on line 3

Це досить важлива зміна, яка теоретично може зламати зворотну сумісність додатка якщо використовуються свої error handlers для перехоплення попереджень.

Причому є цікава особливість: пробіл на початку рядків «5» + «3» — не дасть помилок. А ось «5» + «3» — пробіл в кінці вже дасть видасть попередження.

Для обходу наслідків неявного перетворення і викиду попереджень можна явно вказувати «cast» в потрібний тип: (int)«5» + (int)«3» або придушувати всі примусово @(«5» + «3 „).

Інші зміни та зворотні несумісності
  • У зв'язку з новими типами, додані нові зарезервовані слова void, iterable, і код який містить класи, інтерфейси, трейты з такими іменами буде давати помилку в 7.1

  • Поміняли поведінку в php экстеншенах, які продовжували викидати Fatal Error замість генерації Error винятки (як поточне ядро 7.0), плюс помилки рівня E_ERROR або E_RECOVERABLE_ERROR теж стали викидати виключення там, де можливо (ясна річ, що при нестачі пам'яті раніше скрипт необоротно падає (RFC).

  • Змінилася поведінка при виклику функцій / методів без передачі обов'язкових аргументів. Тепер замість звичного Warning попередження, буде викинуто виняток ArgumentCountError (успадковує тип Error RFC):

    
    function foo($a) {
    var_dump($a); // тепер виконання сюди не дійде і в $a не дорівнює NULL
    }
    foo();
    // Fatal error: Uncaught ArgumentCountError: Too few arguments to function foo(), 0 passed in...
    

  • Наступні функції більше не можна викликати динамічно через: $func(), call_user_func(), array_map() і тд:

    1. extract()
    2. compact()
    3. get_defined_vars()
    4. func_get_args()
    5. func_get_arg()
    6. func_num_args()
    7. parse_str() з одним аргументом
    8. mb_parse_str() з одним аргументом
    9. assert() більше не можна використовувати рядок у якості агрумента
  • rand() srand() тепер просто псевдоніми (alias) до функцій mt_rand() mt_srand().
    Це в свою чергу торкнеться висновок таких функцій:

    1. rand()
    2. shuffle()
    3. str_shuffle()
    4. array_rand()
  • Додана функція session_gc(). Тепер можна чистити старі сесії прямо з скриптів.

  • Додана функція session_create_id(), яка дозволяє згенерувати валідний автоматичний id сесії без запуску нової сесії, який можна буде використовувати в session_id() для старту сесії зі згенерованим раніше ID.

  • Прискорили генерацію ID сесії в 2+ разу, прибравши хешування та використовуючи нову функцію з 7.0 php_random_bytes()

    Швидкість до: Requests per second: 899.36 [#/sec]
    Швидкість після: Requests per second: 2278.59 [#/sec]

  • Прибрали неконсистентное поведінка над змінної $this

    
    function foo($this) { // Fatal error: Cannot use $as this parameter
    }
    static $this; // Fatal error: Cannot use $this as static variable
    global $this; // Fatal error: Cannot use $this as global variable
    try {
    ...
    } catch (Exception $this) { // Fatal error: Cannot re-assign $this
    }
    foreach ($a as $this) { // Fatal error: Cannot re-assign $this
    }
    unset($this); // Fatal error: Cannot unset $this
    $a = "this";
    $$a = 42; // throw new Error("Cannot re-assign $this")
    // та інші кейси

  • Розширення mcrypt “ позначено як застаріле і все mcrypt_* функції будуть викидати E_DEPRECATED.

  • curl розширення додана підтримка для HTTP/2 Server Push, так само були додані новий функції curl_multi_errno(), curl_share_errno(), curl_share_strerror().

  • Опція 'e' для функцій mb_ereg_replace() mb_eregi_replace() об'явлена застарілою.
На цьому ми, мабуть, і зупинимося, хоча там ще повно дрібний змін в основному в розширеннях. А нам для холивара цілком вистачить і цього списку. )

Підсумок
Особисто моя думка про даний мінорний реліз: все дуже органічно вписалася, саме цього і не вистачало в більшості своїй в новому PHP 7.0 і дані зміни лише підкреслюють і посилюють особливості 7.х гілки.
Рекомендую дочекатися 7.1.1 і можна оновлюватися без страху щось зламати (якщо ви звичайно вже перейшли на 7.0).

Дана стаття не претендує на повне опис УСІХ змін і я міг пропустити щось важливе, рекомендую все одно ознайомитися з першоджерелами:
» https://wiki.php.net/rfc#php_71
» https://github.com/php/php-src/blob/php-7.1.0RC1/UPGRADING

P. S. Приклади можна випробувати самому в онлайн пісочниці — 3v4l.org/#version=7.1.0RC1

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.