Jeszcze kilka miesięcy temu (ale po wypuszczeniu PHP7) często spotykana wśród mało doświadczonych programistów i PM’ów była niechęć „zaryzykowania” uruchomienia tworzonej aplikacji na PHP7. Niechęć ta jest zupełnie przesadzona, chociaż ma swoje podstawowe uzasadnienie w „możliwości” niepowodzenia. Myślę, że jest to tożsame z wyborem systemu operacyjnego i decyzją, że nie wybieramy „cieplutkiej” nowej wersji, by dokonać tego po roku. Tak stajemy się „ciepłą kluchą” programowania – opóźnienie nie ma większych konsekwencji dla laików i zwykłych użytkowników Internetu, ale dla web developerów stanowi przepaść technologiczną. Należy korzystać z nowinek. Po roku oczekiwania zapewne twórcy pracują już nad nowszą wersją, a nierzadko wprowadzają na rynek nową wersję.
W przypadku PHP już na listopad 2016 planowany jest release wersji 7.1. Tempo stosowne, a dla niektórych nawet za szybkie, wyprzedzające podjęcie decyzji o przejściu z PHP5 na PHP7.
Oczywiście mogą wystąpić małe problemy przy zmianie wersji PHP, ale albo są one łatwe do naprawienia (kwestia jakichś modułów), albo problemy powiązane są z deprecated features lub backward incompatible changes. Informacje o wszelkich zmianach są łatwo dostępne na stronie migracji do PHP7.
O tym co nam daje najnowszy PHP w tym artykule.
Wydajność
Jakichkolwiek źródeł nt. PHP7 byście nie czytali, wydajność będzie w nich wiodącym tematem. Wniosek nasuwa się jeden – jest tak dobrze względem poprzednika, że naprawdę zaleta jest bezdyskusyjna. Zawdzięczamy ten sukces trzem panom: Dmitry Stogov, Xinchen Hui i Nikita Popov. Pracowali oni na PHPNG (PHP next generation), z którego to wywodzi się PHP7. PHPNG posiada zmieniony Zend Engine, pozostając w 100% kompatybilnym. Pozornie mało, ale efekt jest piorunujący! Wzrost prędkości + bardzo widoczne ograniczenie zużycia pamięci = niższe koszty infrastruktury.
Poniżej zaczerpnięte wykresy z http://www.zend.com/en/resources/php7_infographic :
Tryb strict
Umożliwia świadome przyspieszenie działania aplikacji już na poziomie programowania.
Wymagane do tego jest użycie deklaracji:
declare
(strict_types=1);
Musi ona zostać dodana w pierwszej linii kodu PHP. Odtąd nadzorujemy na etapie budowania aplikacji jakiego typu parametry przekazujemy do funkcji (i jakiego typu otrzymujemy wynik). Lista typów dostępna pod adresem: http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration.types. W przypadku błędnie przekazanego obiektu otrzymamy:
Catchable fatal error
Przykład:
function sum(int $a, int $b) : int { return $a + $b; }
Spaceship operator
Nowy operator ułatwiający porównywanie obiektów. Na pewno zwiększa czytelność kodu i ta zaleta jest wiodącą przy nieznacznym przyspieszeniu operacji. Wystarczy wstawić ten operator <=> pomiędzy dwie zmienne, a w wyniku otrzymamy -1, 0 lub 1, w zależności czy pierwsza zmienna jest mniejsza od drugiej, równa lub większa.
Null coalescing operator
Kolejne udogodnienie. Często zdarza się bowiem, że sprawdzamy zmienną, czy aby nie jest NULLem, by wówczas zwrócić coś innego niż ją samą.
Przykład z php.net:
// Fetches the value of $_GET['user'] and returns 'nobody' // if it does not exist. $username = $_GET['user'] ?? 'nobody'; // This is equivalent to: $username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
Stałe tablice poprzez define()
Dotychczas (przed PHP7) możliwe było definiowanie stałych o wskazanych typach: integer, float, string, boolean, bądź wartości NULL. Teraz można definiować stałą tablicę.
Przykład z php.net:
// Works as of PHP 7 define('ANIMALS', array( 'dog', 'cat', 'bird' )); echo ANIMALS[1]; // outputs "cat"
Klasy anonimowe
Możecie się domyślić, że chodzi tutaj o stworzenie klasy w miejscu i przekazanie do funkcji. Jest to usprawnienie pozwalające na ograniczenie deklaracji niepotrzebnych obiektów w kodzie.
Przykład:
interface Comparator { public function compare($a, $b); } // PHP7 $system->setComparator( new class implements Comparator { public function compare($a, $b) { return $a === $b; } } );
Uproszczona składnia zmiennych
Pozwala to na użycie operatorów: ->, [], (), {} i :: z dowolnymi poprawnymi wyrażeniami po lewej.
Do głównych zarzutów przeciwko językowi PHP stawiano brak konsekwencji w jego składni. Nie było intuicyjne, czy dana operacja jest dozwolona, mimo iż podobna wykonała się bez problemów składniowych. Np. od wersji 5.4 umożliwiono natychmiastową dereferencję tablicy zwracanej z metody, jednak dalej było niemożliwe natychmiastowe wywołanie zwracanej funkcji. W PHP7 położono kres tym problemom. Dodano wiele operacji, które sprawiają, że składnia staje się bardziej spójna i jednolita, pozwoli to między innymi na pełniejsze wykorzystanie programowania funkcyjnego. Poniżej kilka przykładów działających na PHP7 i jednocześnie niedziałających na PHP<7:
// zagnieżdżenie wywoływania funkcji - funkcje wyższego rzędu $apply = function ($f) { return function ($a) use ($f) { return $f($a); }; }; $multiply = function ($x) { return function ($y) use ($x) { return $x * $y; }; }; echo $apply($multiply)(2)(3);
echo (function ($a) { return $a * 2; })(2);
($object->closureProperty)() class foo { static $bar = 'bas'; } class bas { static $bat = '1'; } bas::$bat = function () { echo "2"; }; $foo = 'foo'; ($foo::$bar::$bat)();
Pozostaje jeszcze zobrazować jedną zmianę, która może wprowadzić trochę zamieszania, jeśli przenosimy aplikację z wersji PHP <7 na „siódemkę”.
$obj->$properties['name']; // w PHP 5.6 $obj->{$properties['name']} // w PHP 7 {$obj->$properties}['name']
W wersji PHP7 wywołanie $obj->$properties[„key”] odwoła się do atrybutu klasy (zapisanym w zmiennej $properties) z podanym kluczem. W wersji wcześniejszej, ten sam kod odwoływał się do atrybutu klasy, którego nazwa była dostępna pod kluczem „key”‚ w tablicy $properties.
Grupowanie deklaracji use
Pozwoli to na import grupowy zależności – poprawi czytelność i ułatwi wpisywanie (tutaj można liczyć na udogodnienia naszego IDE programistycznego).
use Framework\Component\{ ClassA, ClassB as ClassC, OtherComponent\ClassD, function OtherComponent\someFunction, const OtherComponent\SOME_CONSTANT }; use function some\namespace\{fn_a, fn_b, fn_c}; use const some\namespace\{ConstA, ConstB, ConstC};
Obsługa błędów
Zmieniono podejście do błędów krytycznych pojawiających się podczas działania aplikacji. Od teraz błędy te są wyjątkami silnika EngineException i można je w łatwy sposób przechwytywać, co ułatwia procedurę debugowania.
Wszystkie wyjątki dziedziczą po interfejsie \Throwable.
Więcej na temat – Engine Exceptions.
Nowe zarezerwowane typy
W PHP7 pojawiają się nowe typy danych, które są zarezerwowane. Oznacza to, że nie możesz nazywać klas, interfejsów, czy traitów tymi słowami. Te nowe typy to: Int, Float, Bool, String, True, False, Null.
Ta zmiana może spowodować błędy w aplikacjach, które przenosimy na PHP7, ale prawdopodobieństwo ich wystąpienia jest tak małe, że nie warto nawet o tym pamiętać – chyba że jesteśmy Januszami programowania.
Wsparcie dla 64-bitowego Windowsa
Do tej pory buildy 64-bitowe dla Windowsa uznawane były za eksperymentalne. Wraz z najnowszą wersją wprowadzono konsekwentne wsparcie dla platform 64-bitowych – koniec problemów z dużymi liczbami całkowitymi i dużymi plikami 🙂
intdiv()
Pomocnicza funkcja pozwalająca zwrócić część całkowitą z dzielenia bazowej liczby całkowitej (dzielna) przez inną wartość całkowitą (dzielnik)
int intdiv ( int $dividend , int $divisor )
Aktualizacja funkcji session_start()
W PHP7 dodano parametr $options do dotychczas bezparametrowej funkcji session_start(). Moża zdefiniować w ten sposób dyrektywy konfiguracyjne sesji.
session_start([ 'cache_limiter' => 'private', 'read_and_close' => true, ]);
Zmiany w unserialize()
Umożliwiono deserializację kontrolowaną, czyli zabezpieczono przed iniekcją kodu. Programista może określić białą listę klas, które będą podlegać deserializacji.
Generator return expression
Generatory w PHP7 zostają rozbudowane o dodatkowe opcje. Jedną z nich jest możliwość konstrukcji return, czyli generator jest w stanie zwracać jakąś wartość/wyrażenie. Z generatora nadal jednak nie można zwrócić referencji. Zwracana wartość może być pobrana z generatora za pomocą metody getReturn() wykonanej na generatorze.
$gen = (function() { yield 1; yield 2; return 3; })(); foreach ($gen as $val) { echo $val, PHP_EOL; } echo $gen->getReturn(), PHP_EOL;
W wyniku otrzymamy:
1 2 3
Pobieranie wartości zwracanej poprzez metodę publiczną generatora jest dość dziwne na pierwszy rzut oka, ale w tym jest głębszy sens. Gdyby return generatora działał, jak w zwykłych funkcjach problemem byłaby identyfikacja, czy to co otrzymaliśmy pochodzi z yield czy return. Tym samym sami decydujemy, czy chcemy pobrać wartość zwracaną poprzez return.
Generator delegation
Umożliwiono delegowanie przez generator iteracji do innego generatora, tablicy lub obiektu implementującego interfejs Traversable. Wszystko z poziomu instrukcji yield.
Przykład z php.net:
function gen() { yield 1; yield 2; yield from gen2(); } function gen2() { yield 3; yield 4; } foreach (gen() as $val) { echo $val, PHP_EOL; }
W wyniku mamy:
1 2 3 4
Podsumowanie
Warto, warto, jeszcze raz warto. Mnie samego przekonuje tak fenomenalna poprawa wydajności, a wszelkie dobrodziejstwa, które opisałem oprócz tego pozwalają na przyjemniejsze tworzenie aplikacji w PHP, poczucie większej kontroli nad kodem, a poprzez ujednolicenie składni także na zaoszczędzenie czasu przy rozmyślaniu o poprawności. Za chwilę (4 miesiące) wersja 7.1 – tam mają się pojawić między innymi:
- typy skalarne opcjonalnie puste
- typ zwracany void
- modyfikatory dostępu dla stałych klasy