Už PHP 7? Kam zmizelo PHP 6?

Jako PHP 6 byla označována nakonec nikdy nevydaná verze PHP, která měla přinést podporu Unicode. Aby se předešlo zmatkům s již vydanými články a knihami o PHP 6 (schválně si ve svém oblíbeném online knihkupectví zkuste vyhledat PHP 6), bylo rozhodnuto novou verzi označit jako PHP 7.

Jak si vyzkoušet novou funkcionalitu?

Nejjednodušším způsobem je použít online nástroj 3v4l.org, který spustí zadaný PHP kód na všech možných verzích (takže je možné rovnou porovnat, jak by se daný kód choval ve starší verzi PHP). Druhou variantou je rozjet si ho u sebe. Je možné využít připravený Vagrant image, Docker containerzkompilovat si ho ze zdrojáků nebo si stáhnout hotový nightly build, což je podle mě je nejlepší varianta. Detailněji tu rozeberu postup pro Windows.

  1. Otevřete si stránku s buildy.
  2. Postupně odspodu (od nejnovějších) otevírejte jednotlivé složky.
  3. Hledejte soubor php-master-ts-windows-vc11-x86-XXXXXXX.zip, který se velikostí bude blížit 20MB. Pokud má 10MB, tak je to asi nějaký rozbitý build (případně si rovnou stáhněte php-master-ts-windows-vc11-x86-r869f662.zip, který mi fungoval OK).
  4. Stáhněte ho a rozbalte.
  5. Zkopírujte php.ini-development do php.ini a případně v něm povolte potřebná rozšíření.

Pokud si v adresáři spustíte php -v, tak by se vám měl ukázat následující výstup (datum může být jiné – podle toho, jaký si vyberete build):

PHP 7.0.0-dev (cli) (built: Jun  5 2015 04:05:24)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v3.0.0-dev, Copyright (c) 1998-2015 Zend Technologies

Tím pádem máte PHP 7 nainstalované, wohoo! Ještě si ho případně můžete přidat do systémové cesty, ať jde spouštět odkudkoliv (jen ho pak nezapomeňte zas odebrat). Když už ho máte rozjeté lokálně, tak na něm můžete spustit lint (kontrolu syntaxe), který mimo jiné ověří, že nikde nepoužíváte nová rezervovaná slova. Případně jde využít pro spuštění PHPUnit testů vašeho projektu.

Třešničkou na dortu může být rozjetí přímo v Apache – v xamppu kupodivu stačilo jen prohodit 5 za 7 vC:\xampp\apache\conf\extra\httpd-xampp.conf. Docela fajn také je, že můj oblíbený PHPStorm 9 EAP už některé věci podporuje (a správně pro ně zvýrazňuje syntaxi).

A co je nového? PHP 7 je mnohem rychlejší!

Díky velkému refaktoringu a přepracování datových struktur označovaného jako phpng bude PHP 7 výrazně rychlejší a méně paměťově náročné (detailnější povídání o změnách v implementaci doporučuji fajnšmekrům – přiznám se, že na mě to je moc low-level).

Raději přidám nějaká čísla – Magento by mělo zvládat 3× tolik požadavků/s než na PHP 5.6 (2× rychlejší requesty, o 30 % menší spotřeba paměti). A úvodní stránka WordPressu potřebuje 4× méně CPU instrukcí.

V následujících kapitolách projdeme z mého pohledu nejdůležitější změny. Kompletní přehled implementovaných RFC si můžete pročíst na wiki.

Typová kontrola pro skalární datové typy

Kromě vyššího výkonu je typová kontrola asi nejvíce medializovanou novinkou PHP 7. Dlouhou dobu se řešilo, jak skalární typy do PHP přidat a přijato bylo až několikáté RFC, které je navrhovalo. Konečně tedy můžeme napsat:

<?php
function add(int $a, int $b) {
    return $a + $b;
}
add(1, 3);

A v případě předání neplatných datových typů PHP vypíše chybu Argument 2 passed to add() must be of the type integer, string given, called in ….

Kontrola datových typů není aktivní automaticky, ale je nutné na začátku souboru uvést:

<?php
declare(strict_types = 1);

Po zapnutí jsou datové typy kontrolovány i při volání standardních funkcí z PHP:

<?php
declare(strict_types = 1);

substr(123, 2); //substr() expects parameter 1 to be string, integer given

V případě rozšiřování z int na float probíhá automatická konverze (nevyhodí chybu, opačný směr z float naint ano).

<?php
declare(strict_types = 1);
 
function add(float $a, float $b): float {
    return $a + $b;
}
 
add(1, 2); // float(3)

Typy návratových hodnot

V PHP 7 bude možné zapsat návratovou hodnotu funkce/metody přímo do její definice (takže nebude nutné to psát do dokumentačních komentářů).

<?php
function foo(): array {
    return [];
}

Zajímavostí je, že RFC přidávající podporu návratových typů řešilo jen ty typy, které bylo možné již dříve použít v definici metody a int/string/float/boolean nepovolovalo. Jejich podpora byla v přidána až v RFC zmíněném dříve (které bylo ve skutečnosti řešeno později než návratové typy).

Pokud funkce vrátí jiný typ, než by měla, tak PHP vyhodí chybu (opět je to závislé na declare(strict_types = 1);):

<?php
declare(strict_types = 1);

function add(int $a, int $b): int
{
    return $a + $b * 0.2;
}

add(1, 2); // Return value of add() must be of the type integer, float returned

Výjimky místo fatal errorů

Spoustu věcí z jádra PHP bylo upraveno, aby místo Fatal Erroru vyhodilo výjimku. Jak jsem výše psal, že PHP v případě nekompatibilních typů vypíše chybu, tak ono ve skutečnosti nevypíše chybu, ale dokonce vyhodí výjimku:Uncaught TypeException: Return value of add() must be of the type integer, float returned.

Pokud by TypeException dědila od Exception, tak by to mohlo vést k těžko odhalitelným chybám (byly by odchyceny pomocí catch (Exception $e)). Proto byl vytvořen nový typ výjimek – EngineException a nový společný předek pro všechny výjimky, jak je naznačeno níže:

BaseException (abstract)
 +- EngineException
 +- ParseException
 +- Exception
     +- ErrorException
     +- RuntimeException
         +- ...
     +- ...

Přehodnocení E_STRICT errorů

Když jsme u těch chyb, tak jste si určitě všimli, že v PHP existoval takový divný typ chyby – E_STRICT. V PHP 7 byly jeho jednotlivé výskyty přehodnoceny a nahrazeny buď E_DEPRECATED, E_NOTICE nebo E_WARNING. Případně byl vypisování některých chyb zrušeno.

Null Coalesce Operator ??

Předpokládám, že všichni znáte krátký ternární operátor zavedený v 5.3, takže jen příklad pro připomenutí:

<?php
$a = 'value';
var_dump($a ?: 'no'); // string(5) "value"

Nicméně, problém nastane, pokud jako operand vlevo budeme mít neexistující klíč v poli – takové situace je nutné ošetřit pomocí isset():

<?php
var_dump($_GET['user'] ?: 'nobody'); // string(6) "nobody"
// Notice: Undefined index: user

var_dump(isset($_GET['user']) ? $_GET['user'] : 'nobody'); // string(6) "nobody"

Takový zápis je zbytečně upovídaný, a vývojář s vypnutými notices si chyby ani nevšimne. Právě to řeší Null Coalesce Operator, který otestuje první operand a pokud existuje a není null, tak ho vrátí, jinak vrátí druhý. Takže i v případě přístupu k neexistujícímu prvku pole nebude vyhozena notice.

<?php
var_dump($_GET['user'] ?? 'nobody'); // string(6) "nobody"

Nový porovnávací operátor – Spaceship

Nově byl přidaný „trojcestný“ porovnávací operátor (tzv. spaceship operator), který pro shodné hodnoty vrátí 0, pro první menší -1 a pro první větší 1.

<?php // http://3v4l.org/VDBof
var_dump(1 <=> 1); // 0
var_dump(1 <=> 2); // -1
var_dump(2 <=> 1); // 1

var_dump(('a' <=> 'a')); // 0
var_dump(('a' <=> 'b')); // -1
var_dump(('b' <=> 'a')); // 1

Když jsme dříve chtěli něco seřadit vlastní funkcí, tak to mohlo vypadat takhle:

<?php // http://3v4l.org/hNavV
$data = array(
    array('id' => 1, 'price' => 50),
    array('id' => 7, 'price' => 40),
    array('id' => 5, 'price' => 130),
);
uasort($data, function ($a, $b) {
    return ($a['price'] < $b['price']) ? -1 : (($a['price'] > $b['price']) ? 1 : 0);
});

A pokud použijeme raketu, tak se porovnávací funkce zjednoduší na:

<?php // http://3v4l.org/ing7c
uasort($data, function ($a, $b) {
    return $a['price'] <=> $b['price'];
});

Generátor kryptograficky bezpečných náhodných čísel (CSPRNG)

Vygenerovat dostatečně náhodné (=nepredikovatelné) číslo není jednoduché. V PHP to bylo možné řešit pomocíopenssl_random_pseudo_bytes() nebo mcrypt_create_iv(), nicméně oboje je závislé na aktivovaném rozšíření. Proto v PHP 7 přibyly dvě funkce pro generování náhodných bajtů a náhodných čísel:

random_bytes(int length);
random_int(int min, int max);

Anonymní třídy

PHP 7 bude podporovat anonymní třídy, takže bude fungovat následující kód:

<?php // http://3v4l.org/I7Kbo
$instance = new class('foo') {
    public $i;
    public function __construct($i) {
        $this->i = $i;
    }
};
var_dump($instance); // object(class@anonymous)#1 (1) {   ["i"]=>   string(3) "foo" }

Nejsem si jistý, jestli tohle bylo nezbytné přidávat. Na jednu stranu si to své usecase najde – třeba pro mockování v testech nebo na nějaké drobnosti, ale na druhou stranu to umožní vznik neznovupoužitelných kusů kódu, které se budou špatně testovat. Příkladem, kdy to (trochu) dává smysl, může být jednoduchý observer:

<?php
$subject->attach(new class implements SplObserver {
    function update(SplSubject $s) {
        printf("Got update from: %s\n" $subject);
    }
);

Drobnosti

Leave a Reply