Hlavní navigace

PHP 8: trojnásobný výkon díky JIT a užitečné novinky pro programátory

2. 12. 2020
Doba čtení: 5 minut

Sdílet

 Autor: Depositphotos
Po téměř pěti letech přichází nová major verze populárního jazyka pro tvorbu webů. Stejně jako tomu bylo s příchodem řady 7.x, přináší i tato nová řada spoustu zajímavých a pro programátory jistě vítaných novinek.

Co je nového

Stejně jako minulé řady, přichází s PHP 8 lepší výkon, tentokrát v podobě JIT (just-in-time) kompilace. Některé benchmarky ukazují až trojnásobný výkon oproti PHP 7.4, u některých dlouho běžících aplikací je výkon až dvojnásobný.

Z novinek můžeme zmínit například:

  • nové interní funkce, které známe z mnoha frameworků – str_contains(), str_starts_with() a str_ends_with() či interface  Stringable,
  • striktnější kontrolu typů při bitových a aritmetichých operacích (viz RFC),
  • validaci abstraktních metod u traitů (viz RFC),
  • změny v chybách,
  • změny v dědičnosti privátních metod (viz RFC),
  • operátor @ již nemá vliv na fatální chyby,
  • mixed typ,
  • static jako návratový typ (viz RFC).

Na některé novinky se nyní podíváme trochu blíže.

Pojmenované agrumenty

Pojmenované argumenty (named aguments) známe například z Pythonu. Moderní IDE sice argumenty funkcí napovídají, ale nový zápis je jistě přehlednější.

<?php

// PHP 7
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);

// PHP 8
htmlspecialchars($string, double_encode: false);

Atributy

Místo anotací podle standardu PHPDoc, můžeme nyní použít nativní atributy. Výhodou může být například jejich podpora v reflection API.

<?php

// PHP 7
class PostsController {
    /**
     * @Route("/api/posts/{id}", methods={"GET"})
     */
    public function get($id) { /* ... */ }
}

// PHP 8
class PostsController {
    #[Route("/api/posts/{id}", methods: ["GET"])]
    public function get($id) { /* ... */ }
}
<?php

class PostsController {
    #[Route("/api/posts/{id}", methods: ["GET"])]
    public function get($id) { /* ... */ }
}

$reflectionClass = new ReflectionMethod(PostsController::class, 'get');
$attributes = $reflectionClass->getAttributes();

var_dump($attributes[0]->getName());
var_dump($attributes[0]->getArguments());
string(5) "Route"
array(2) {
  [0]=>
  string(15) "/api/posts/{id}"
  ["methods"]=>
  array(1) {
    [0]=>
    string(3) "GET"
  }
}

Definice atributů třídy v konstruktoru

Díky constructor property promotion také napíšete o kousek kódu méně.

<?php

// PHP 7
class Point {
    public float $x;
    public float $y;
    public float $z;

    public function __construct(
        float $x = 0.0,
        float $y = 0.0,
        float $z = 0.0,
    ) {
        $this->x = $x;
        $this->y = $y;
        $this->z = $z;
    }
}

// PHP 8
class Point {
    public function __construct(
        public float $x = 0.0,
        public float $y = 0.0,
        public float $z = 0.0,
    ) {}
}

Union Types

Namísto současného použití PHPDoc anotací pro případ, že proměnná můžeme různých datových typů, můžeme typy kombinovat při deklaraci např. atributu třídy. Tyto deklarace jsou pak při běhu skriptu validovány.

<?php

// PHP 7
class Number {
    /** @var int|float */
    private $number;

    /**
     * @param float|int $number
     */
    public function __construct($number) {
        $this->number = $number;
    }
}

new Number('NaN'); // Toto je v pořádku

// PHP 8
class Number {
    public function __construct(
        private int|float $number
    ) {}
}

new Number('NaN'); // Vyvolá výjimku TypeError

Výrazy s match

S PHP 8 přichází nové klíčové slovo match , které má jistou podobnost se switch blokem. Můžeme jej charakterizovat takto:

  • match je výraz, čili jeho výsledek můžeme uložit do proměnné nebo jej může vracet funkce,
  • match pracuje pouze s jednořádkovými výrazy, nemá žádnou obdobu pro  break,
  • match porovnává striktně.
<?php

// PHP 7
switch (8.0) {
    case '8.0':
        $result = "Oh no!";
        break;
    case 8.0:
        $result = "This is what I expected";
        break;
}
echo $result; // Oh no!

// PHP 8
echo match (8.0) {
    '8.0' => "Oh no!",
    8.0 => "This is what I expected",
}; // This is what I expected

Nullsafe operátor

Kontrola, zda je proměnná null, zvláště pokud se několikrát opakuje, dělá často kód hůře čitelný. Nullsafe operátor zde jistě mnohé zpřehlední.

<?php

// PHP 7
$country =  null;

if ($session !== null) {
    $user = $session->user;

    if ($user !== null) {
        $address = $user->getAddress();

        if ($address !== null) {
            $country = $address->country;
        }
    }
}

// PHP 8
$country = $session?->user?->getAddress()?->country;

Změny v interních funkcích

Dosud volání interní funkce s chybným typem argumentu vyvolalo pouze chybu typu Warning, nyní je ve většině případů dochází k vyvolání příslušných výjimek, které korespondují s typem chyby.

<?php

// PHP 7
strlen([]); // Warning: strlen() expects parameter 1 to be string, array given

array_chunk([], -1); // Warning: array_chunk(): Size parameter expected to be greater than 0

// PHP 8
strlen([]); // TypeError: strlen(): Argument #1 ($str) must be of type string, array given

array_chunk([], -1); // ValueError: array_chunk(): Argument #2 ($length) must be greater than 0

Zpětná kompatibilita

  • změna při porovnávání řetězců a čísel
Výraz Dříve Nyní
0 == "0" true true
0 == "0.0" true true
0 == "foo" true false
0 == "" true false
42 == " 42" true true
42 == "42foo" true false
  • match je klíčové slovo,
  • metoda, která má stejný název jako třída již není konstruktor, je třeba používat výhradně  __construct(),
  • byla odstraněna možnost volat staticky metody, které nejsou statické, což je zohledněno i v chování funkce  is_callable(),
  • assert() nyní vyvolává výjimku, toto lze změnit pomocí ini direktivy  assert.exception=0,
  • bylo odstraněno přetypování (real) a (unset),
  • byla odstraněna ini direktiva track_errors,
  • již nelze definovat case-insensitive konstanty,
  • funkci __autoload() již nelze použít k autoloadingu tříd, náhrada je  spl_autoload_register(),
  • z funkcí pro obsluhu chyb byl odstraněn argument errcontext,
  • byly odstraněny funkce create_function() a each(),
  • funkci array_key_exists() již nelze použít pro objekty,
  • výchozí úroveň pro error_reporting je nyní E_ALL,
  • ini direktiva display_startup_errors je ve výchozím stavu povolená,
  • použití parent ve třídě, která není potomkem žádné třídy nyní vyvolá fatální chybu,
  • #[ již není bráno jako začátek komentáře,
  • desetinná čísla se budou vypisovat nezávisle na locales
<?php

// PHP 7
setlocale(LC_ALL, "cs_CZ");
$f = 3.14;
echo $f; // 3,14

// PHP 8
setlocale(LC_ALL, "cs_CZ");
$f = 3.14;
echo $f; // 3.14
  • byla odstraněna možnost přistupovat k prvkům pole pomocí složených závorek.

Frameworky a redakční systémy

Podíváme-li se do .travis.yml nebo composer.json velkých PHP frameworků jako jsou Symfony, Laravel, Yii nebo naše české Nette, všechny už s novým PHP počítají.

WordPressu a Drupalu to také vypadá slibně, i poslední vydání Joomly říká, že je na PHP 8 připravené.

DT2021 tip

Instalace

Pokud chcete nové PHP začít používat, cesta je celkem snadná. Chcete-li si novinky jen vyzkoušet, jsou pro vás k dispozici třeba aktuální image pro Docker. Samozřejmě můžete zvolit i klasické balíčky, pokud používáte Debian či Ubuntu, Ondřej Surý spravuje repozitáře pro DebianPPA pro Ubuntu, v nichž je již nové PHP k dispozici. Pro CentOS můžete použít Remi's RMP Repository, kde je PHP 8 již také k dispozici.

Novinky nejen pro programátory

Všechny novinky v PHP 8 najdete samozřejmě na stránkách PHP. Lepší výkon určitě potěší všechny a novinky v jazyku udělají jistě radost programátorům. Je třeba říci, že v době psaní článku není ještě kompletní oficiální dokumentace, např. chybí novinky v reflection API, proto je zde odkazováno především na RFC. Na závěr nezbývá než popřát co nejméně problémů při aktualizaci.

Autor článku

Jakub Vokoun ztělesňuje typické IT klišé a přeměňuje kofein na kód, převážně v jazycích Python, Go a PHP. Od doby Debianu Woody (2002) je také zapáleným linuxákem.