Jaké novinky přinese PHP 7

Za pár dnů (11. 6.) vyjde první alfa verze PHP 7 (a finální verze v půlce listopadu). Je tedy již je nejvyšší čas podívat se na vylepšení a novinky, které nás čekají.
Nálepky:
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 container, zkompilovat 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.
- Otevřete si stránku s buildy.
- Postupně odspodu (od nejnovějších) otevírejte jednotlivé složky.
- 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). - Stáhněte ho a rozbalte.
- Zkopírujte
php.ini-development
dophp.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 v C:\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
na int
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
- Bylo implementováno nové rozšíření pro JSON – původní mělo nesvobodnou licenci (známé „The Software shall be used for Good, not Evil.“)
- Přibyla možnost vypnutí errorů/zapnutí výjimek pro assert()
- Nové rezervované typy (pro názvy třídy, rozhraní, traitů):
int
,float
,bool
,string
,true
,false
,null
- A další rezervace:
resource
,object
,mixed
,numeric
(aby případná nová funkcionalita v PHP 7.1 nebyla BC break)
Odebrané věci
Z PHP 7 byly odebrány některé věci, který byly již dříve označené jako deprecated – nejzajímavější:
- Extension
ext/ereg
– nahrazenaext/pcre
- Extension
ext/mysql
– nahrazenaext/mysqli
aext/pdo_mysql
- Přiřazení
new
referencí:$a = & new Foo()
set_magic_quotes_runtime
,magic_quotes_runtime
- Možnost použití eval v
preg_replace
– nahrazenopreg_replace_callback
- Komentáře začínající
#
vphp.ini
(správně je;
) - Warning při nenastavené časové zóně – důležitější volby mají „sane defaults“, tak proč ne tohle
- PHP 4 konstruktory (metoda stejného jména jako je název třídy) – zatím jen deprecated warning, odebrány budou v PHP 8
- Alternativní otevírací tagy
<%
a<script language=php>
(krátké otevírací<?
zůstávají)
Závěrem
PHP 7 přinese výrazné zrychlení a spoustu nových věcí. I když je vydání finální verze ještě daleko, a nasazování do produkce ještě dál, tak určitě dává smysl si PHP 7 vyzkoušet už dnes a ověřit, že jsou na něj vaše aplikace připravené.
Tesim sa na
??
a samozrejme vykon. Tiez uvazujem o typovej kontrole… Je to v podstate ciastocny unit test. :)Asi je najvyssi cas nastudovat
Exceptions
… Furt neviem naco je to dobre, ale asi sa tomu zachvilu nevyhnem.Ked tak uvazujem o typovej kontrole, vacsina funkcii tak ci tak vracia pole… Takze nebude velmi co typovat.
Těžko říct, co děláte, ale výjimky se hodí když píšete třeba vlastní mini-wrapper na nějakou HTTP API. :-)
Tak trebars mam svoj mini framework… Stara sa o MySQL spojenie, error handling a par podobnych figlov. Stale mi vsak nezaplo, co ziskam s
Exceptions
oproti tomuto. Nemam dovodExceptions
nepouzivat, len nevidim ziadnu vyhodu.Tak třeba když mám background worker, který zpracovává nějaký joby, tak výjimky využiju proto, abych v hlavní
perform
metodě odchytnul jeden typ chyb, pro které má smysl job opakovat (třeba network timeout error) a zbytek zalogoval s tím, že opakovat to nemá smysl (not found, SQL error, …).Je to jen jedna z několika možností, jak něco zastavit, když nemá smysl v tom pokračovat a zároveň dát možnost někomu s tím nějak naložit. Když bych místo toho vyhodit fatal error, můj worker by to asi nerozdejchal. :-)
Typová kontrola a návratové typy jsou opravdu užitečné. Jediné co my ještě chybí je něco na způsob šablon aby když mi funkce bude vracet pole objektů abych hned věděl o jaké typy se jedná. Prostě něco co má Hack :-)
PS: docela mi zde chybí zmínka o AST, protože to umožní konečně PHP vcelku normálně rozšiřovat
AST je implementační detail. Na koncové uživatele to nemá zatím vůbec žádný vliv. Teprve až bude API pro práci s AST dostupné v userlandu, tak to začne být zajímavé.
Tak tak, pokud jsem to správně vyčetl, tak se na ten AST stejně zaatím nedá sáhnout, takže to nějakému CodeSnifferu stejně nepomůže.
Tak na ten výkon se těším asi nejvíc. Vypadá to, že by to mohlo serveru celkem ušetřit.
Takže chápu to dobře, že by v zásadě každý dobře psaný kód měl od počátku fungovat a neměli bychom se dočkat nějakých problémů? Tj. mohu vzít třeba nějaký svůj WP web resp. jeho hosting a upgradovat na PHP7 (tedy až bude ostrá verze)?
Nespoléhal bych na to. Z PHP 7 budou smazány věci, které se staly deprecated klidně až ve verzi 5.5 (např. starý způsob uploadování přes curl).
https://wiki.php.net/rfc/remove_deprecated_functionality_in_php7
Na jednu stranu je tam spoustu odebraných věcí, na druhou stranu nebylo tolik práce fixnout testy ZF1 (ano, toho starého, běžícího i na PHP 5.2), aby procházely i na PHP 7 (teď padá jen jeden podezřelý test pro FirePHP, který nějak magicky parsuje
debug_backtrace()
).Zkoušel jsem spustit Shopio (které už taky vyvíjíme dost dlouho, původně běželo na PHP 5.2) a běželo bez problémů.
Změny zní super, klobouk dolů. Docela by mě zajímalo, jak rychle se firmy aklimatizují, přece jen není švanda upgradnout všechna jejich řešení. :-)
Tak to sem posledně vyřešil tak že sem napsal hostingu at mi vrati starsi verzi php, protoze za to, ze oni se rozmislely a po peti letech updatly verzi php, mi ten, komu ten web patri, za upravu nic nezaplati. proslo :)
Jo, s hostingy bude problém, určitě nemůžou upgradnout PHP na serverech všem klientům. Třeba http://www.tojeono.cz/ má různé servery – pro 5.2/5.3 a pro 5.5/5.6.
Ten uvedený příklad je takový nešikovný, protože už v PHP 5 bylo možné to zapsat jako
což je ještě kratší než zápis s
<=>
operátorem. Rozdíl je v tom, že operátor<=>
je jednak čitelnější a především funguje nejen pro čísla, ale i pro řetězce (dříve bylo potřeba použítstrcmp
) a pro pole (které dříve nebylo možné jednoduše porovnat vůbec). Ty pole jsou obzvlášť důležité, protože umožní porovnat podle více parametrů zároveň, např:Díky za doplnění toho porovnávání řetězců a polí.
Ta finta s odečítáním funguje jen pokud bude porovnávaná věc
int
, jinak to může dávat nesprávné výsledky (tím, že výsledek je automaticky přetypovaný naint
) – viz http://3v4l.org/YTYP8 (v prvním případě to seřadí špatně)To neni pravda. Timto se jen vynuti striktni kontrola datovych typu. Pokud se tato direktiva neuvede nebo hodnota bude nula, tak se pouzije weak type-checking mode.
Po dlouhé době krok správným směrem. Líbí se mi výkon, přepracování Exceptions, nové operátory ?? <=> i kontrola datových typů, kterou jsem povětšinou stejně musel řešit vyhozením vyjímky třeba \InvalidArgumentException … hned se pustím do študýrování a testování :-) Fajn článek, díky!
pridame repozitar
sudo add-apt-repository ppa:ondrej/php-7.0
aktualizujeme repozitare
sudo apt-get update
a nainstalujeme balicek php7
sudo apt-get install php7
vice zde https://launchpad.net/~ondrej/+archive/ubuntu/php-7.0
plati poucka nikdy takove veci neinstalovat na produkcnim serveru, to je myslim jasne .-)
oprava:
a nainstalujeme balicek php7.0
sudo apt-get install php7.0
V RFC bylo nakonec BaseException nahrazeno za Throwable, takže pokud chcete odchytit úplně všechno včetně fatalů, tak catch(Throwable $e).