Přejít k navigační liště

Zdroják » PHP » Jaké novinky přinese PHP 7.1

Jaké novinky přinese PHP 7.1

Články PHP

PHP 7.0.0 vyšlo teprve před necelým rokem a už se pomalu můžeme těšit na PHP 7.1. Před pár dny byla vydána verze RC3, takže už je čas začít testovat kompatibilitu aplikací.

Finální verzi můžeme očekávat v listopadu nebo začátkem prosince. Stejně jako u PHP 7.0 není stanovené přesné datum vydání, ale finální verze vyjde, až bude dostatečně stabilní.

S testováním můžete začít snadno – už jsou k dispozici i buildy pro Windows a podpora ve stabilní verzi PHPStormu (stačí přepnout verzi v Settings – Languages & Frameworks – PHP). Pokud chcete porovnat chování se staršími verzemi PHP, tak doporučuji 3v4l.org (neboli php.wtf).

Prošel jsem pro vás novinky implementované v 7.1, prostudoval jsem RFC, vybral ty nejzajímavější a shrnul je i s příklady. Spoustu změn se nese v duchu zvyšování konzistence jazyka a omezování možností střelit se do nohy, u některých zrušených věcí jsem naštěstí ani nevěděl, že to je možné. Z provedených změn je vidět snahu o silnou zpětnou kompatibilitu a případná nekompatibilita je opodstatněná. Upgrade z 7.0 by tedy měl být relativně snadný.

V manuálu už přibyla sekce o upgrade na 7.1, takže si o potřebných změnách můžete přečíst i tam.

Nullable types

V PHP 7.0 přibyla možnost definovat typy parametrů funkcí a jejich návratových hodnot. Nicméně nebylo možné místo parametru s určeným typem předat null. Obdobně nešlo vrátit null z funkce s definovaným návratovým typem:

<?php
function foo(string $foo): string
{
    return $foo;
}

foo('a'); // OK
foo(null); // TypeError: Argument 1 passed to foo() must be of the type string, null given

Obejít to šlo odebráním definice typů, případně u parametrů pomocí definice: string $foo = null – nicméně v tu chvíli nejde jen o nullable parametr, ale zároveň je $foo také nepovinný.

V PHP 7.1 přibyla možnost pomocí ? označit nullable parametry a návratové hodnoty, takže můžeme bezpečně odlišit nepovinný a nullable parametr:

<?php // https://3v4l.org/mO6vd
function bar(?string $bar): ?string
{
    return $bar;
}

var_dump(bar("a")); // string(1) "a"
var_dump(bar(null)); // NULL
var_dump(bar()); // Uncaught ArgumentCountError: Too few arguments to function bar()

Void return type

Od PHP 7.0 bylo možné definovat návratový typ funkce, ale nebylo možné vyžadovat nevrácení žádné hodnoty (což může být užitečné pro prevenci chyb). Teď je to možné pomocí void:

<?php
function shouldReturnNothing(): void
{
    return 1; // Fatal error: A void function must not return a value
}

V takové funkci musíme return vynechat úplně nebo ho zavolat bez hodnoty: return;. Vrácení null pomocí return null; také není platné.

Chybějící parametr uživatelsky definované funkce vyhodí Error

Pokud jste dříve zavolali funkci s chybějícími parametry, tak se provedla bez nich a zahlásila warning. Nově chybějící parametr funkce vyhodí ArgumentCountError a funkce se vůbec neprovede.
Zdůrazňují, že se to netýká přímo funkcí v PHP, ale jen těch uživatelsky definovaných – substr(); způsobí stále jen warning.

<?php // https://3v4l.org/F9thD
function test($param)
{
    var_dump($param);
}

test(); // Uncaught ArgumentCountError: Too few arguments to function test(), 0 passed and exactly 1 expected

Aritmetické operace s neplatnými stringy vrací warning či notice

V PHP <7.1 bylo možné sčítat jablka s hruškami bez warningů nebo notice:

<?php
var_dump('1' + '1'); // int(2)
var_dump('10 apples' + '5 pears'); // int(15)
var_dump('two apples' + '5 pears'); // int(5)

Pokud v PHP 7.1 použijeme aritmetickou operaci na string, který nelze převést na číslo (např. 'two apples') dostaneme Warning: A non-numeric value encountered in %s on line %d. Pro stringy, ze kterých jde získat číslo ('10 apples') je připravena Notice: A non well formed numeric value encountered in %s on line %d. Řetězec obsahující číslo ('10') se bere jako platný a notice nezpůsobí.

Definice viditelnosti konstant

Nově je možné konstanty označit jako private nebo protected. To přijde vhod, pokud chcete konstantu použít jen uvnitř třídy a nechcete, aby na ní někdo závisel v kódu mimo třídu.

<?php
class Token
{
    const PUBLIC_CONST = 0; // = public const
 
    public const PUBLIC_CONST_TWO = 0; // = const
    protected const PROTECTED_CONST = 0;
    private const PRIVATE_CONST = 0;
}

Chytání více typů výjimek v jednom catch

Pokud jsme v PHP <7.1 chtěli dva typy výjimek ošetřit stejným způsobem, bylo potřeba kód zkopírovat nebo vytvořit společnou metodu:

<?php
try {
   // Some code...
} catch (ExceptionType1 $e) {
   // Code to handle the exception
} catch (ExceptionType2 $e) {
   // Same code to handle the exception
} catch (Exception $e) {
   // ...
}

Nově je možné chytit více typů najednou:

<?php
try {
   // Some code...
} catch (ExceptionType1 | ExceptionType2 $e) {
   // Code to handle the exception
} catch (\Exception $e) {
   // ...
}

Nový typ iterable

Při definici typehintu pro kolekci jste se museli rozhodnout, jestli použijete array a nebude možné tam předat Traversable nebo naopak. Obejít to šlo vynecháním typehintu a kontrolou až v kódu.

Teď už to není potřeba a můžete použít typ iterable:

<?php
function foo(iterable $iterable)
{
    foreach ($iterable as $value) {
        // ...
    }
}

Zároveň přibyla funkce (bool) is_iterable().

Negativní offsety pro stringové funkce

Některé funkce pro práci se stringy (například substr()) umožňovaly používat negativní offsety:

<?php

substr("abcdef", -1); // "f"

Od 7.1 je možné negativní offsety používat také u funkcí: strpos(), stripos(), substr_count(), iconv_strpos(), mb_strpos() (a několik dalších, viz RFC)

$this se vždy bude chovat očekávaným způsobem

Tipuji, že jste nevěděli, a snad díky tomu ani nepoužívali možnost přiřadit do $this jinou hodnotu (netuším, proč by to někdo dělal). V rámci zvyšování konzistence a odebírání historických haluzí už to naštěstí v 7.1 nepůjde.

<?php // https://3v4l.org/ZcMVY

$a = 'this';
$$a = 42;

var_dump($this); // na < 7.1 vypíše 42, na 7.1+ Fatal Error
<?php // https://3v4l.org/cAJGi

class C
{
    function foo()
    {
        $a =& $this;
        $a = 42;
        var_dump($this); // na < 7.1 vypíše 42, na 7.1+ očekávaný dump $this
    }
}

$x = new C();
$x->foo();

Session ID už není hashované

Bezpečně vygenerované ID (pomocí random_bytes() přidaného v 7.0) už není potřeba hashovat, takže byly odebrány související volby v php.ini. Naopak přibyla možnost nastavení délky session id pomocí session.sid_length. V php.ini distribuovaném s PHP je nastaveno na 26 kvůli zpětné kompatibilitě, ale výchozí hodnota přímo v PHP je 32.

Doporučuji tedy ověřit, že sloupec (resp. místo, kam ukládáte session ID) zvládne 32 znaků, případně do php.ini přidat session.sid_length = 26

Zkrácená syntaxe pro array destructuring

Je možné použít kratší zápis pro přiřazení pole do jednotlivých proměnných:

<?php

list($a, $b, $c) = array(1, 2, 3); // původně

[$a, $b, $c] = [1, 2, 3]; // nově také takto

Trošku odbočím, troufám si tvrdit, že použití list() signalizuje code-smell a ve většině případů by mělo jít nahradit pomocí result objectu (tzn. metoda nebude vracet asociativní pole, ale immutable objekt). Pokud list() používáte, zkuste se zamyslet, jestli místo přechodu na novou syntaxi nebude vhodnější upravit kód a rovnou vracet objekt. Pokud máte příklad, kde použití list() dává smysl a neznepřehledňuje kód, tak budu rád, pokud se o něj podělíte v komentářích.

Drobnosti, které nepotřebují vlastní kapitolu

Závěr

Jak je vidět, tak oproti 7.0 jsou změny drobnější a zaměřené na vyladění stávající funkcionality. Upgrade by tedy neměl být složitý, ale stejně doporučuji počkat s produkčním nasazením alespoň na 7.1.1 ;-)

Pokud se vám ještě nepodařil upgrade na PHP 7.0, tak budu rád, pokud se v komentářích rozepíšete o tom, na čem jste se zasekli a co vás drží zpět.

Komentáře

Subscribe
Upozornit na
guest
15 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
v6ak

Takovou věc nepoužívám často, ale pokud chci parsovat tabulku třeba v CSV, je to na to jak dělané. Většinou to nedělám v PHP, ale to už je detail, i jiné jazyky mají podobné (a někdy i mocnější) konstrukce.

Další věc, kde se to může hodit, je parkování pomocí regulárních výrazů.

dimov-cz

list() pouziju malokdy, ale u nejakych jednodussich specifickych skriptu se mi obcas hodi u vysledku internich php funkci. Napr:

  1. list($id, $date, $time) = explode(‚-‚, $line);
  2. preg_match_all(…,$matches);
    list($full, $id, $date, $time) = $matches;

Mozna existuji lepsi cesty, necham si poradit

tomasfejfar

To první je náchylné na chyby kdy line bude ‚one-two‘. Tipuju, že to neošetřuješ, narozdíl od if (count($explodedLine) !== 3) { //exception }

Jakub Vrána

list používám na dvě věci:

  1. Přiřazení matchnutých částí regulárního výrazu do proměnných. Toho se dá zbavit používáním (?P<name>).
  2. Přiřazení parametrů příkazového řádku do proměnných. Toho se dá zbavit používáním pojmenovaných parametrů místo pozičních a knihovnou getopt.
Jakub Vrána

Odstranění session.hash_function škoda není. Nikdy jsem ale nepochopil, proč PHP na serveru neukládá jen hash session ID. Pokud někdo získá read-only přístup k úložišti session ze serveru (může to být třeba jen mirror), tak se rázem může vydávat za jakéhokoliv uživatele. Když by se na serveru ukládal jen hash session ID, tak by se to stát nemohlo. Dohledání dat podle daného session ID by ale pořád bylo možné. Viz též tip 883 v mé knize 1001 tipů a triků pro PHP, který to řeší.

tacoberu

Já to používám u návratových hodnot funkcí:

/**
 * @return [array, array]
 */
function fetch() {}
list($blocked, $other) = fetch()

Samozřejmě by to šlo i tím, že vrátím strukturu, ale ve výsledku je to zbytečně ukecané a rozdíl nulovej.

Samozřejmě musím mět zaručeno, že mi ta funkce bude vracet to co slibuje. Což je imho opět žádoucí, ať laskavě dělá svou práci pořádně, nebudu to za ní kontrolovat.

tomasfejfar

No a myšlenka je, že protože to nechceš kontrolovat, tak to uvedeš v kontraktu – co chceš, aby vrátila. A protože v PHP není jednoúčelový struct, tak musíš udělat celou třídu, případně interface+anonymní třídu.

tacoberu

Ano, to chápu. Jen se rozcházíme v užitku toho. Mě se zdá, že ve výsledku snaha o něco takového povede k mnohem většímu code-smell, než kterému se chceš vyvarovat.

tacoberu

Já jsem nepsal, že problém je zbytečná práce navíc.

tacoberu

Je zajímavé, že třeba v Haskellu se to používá hodně, páč pattern-matching, a je to hodně pohodlené. Chyby si tam hlídá typový systém.

V Lue, tam zase případ, když bych udělal něco jako list($a, $b, $c) = [1, 2]; tak v $c bude prostě Null. U ní jde zase o to, že Lua má velice čistá pravidla jak se pracuje s Null (respektive dle její terminologie Nil) hodnotama.

Já bych to za code-smell určitě nepovažoval.

Plysak

Co jen si pocneme bez $$a ??? :D

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.