Co nowego w PHP v7.3? Opisaliśmy wszystkie 34 zmiany
13 grudnia zostanie udostępniona wersja produkcyjna PHP v7.3. Zmian jest naprawdę dużo, choć nie są rewolucyjne. Większość z nich to drobnostki, ale na pewno każdy znajdzie coś dla siebie. Wszystkie 34 aktualizacje postanowiłem opisać poniżej.
Damian Dziaduch. Absolwent Uniwersytetu Gdańskiego. Zawodowy programista z 7 letnim stażem. Aktualnie lider zespołu oraz specjalista od wydajności w GForces Polska. Po godzinach bloger (damian.dziaduch.pl) i freelancer. Lubi czysty kod, testowanie, wydajność, a także produktywność.
Spis treści
1. Elastyczna składnia HEREDOC / NOWDOC
Znacznik zamykający – nowa linia
Do tej pory, po znaku zamykającym wyrażenie, wymagana była nowa linia:
sql_quote(<<<STR select * from table where 1=1; STR ); $data = [<<<DATA a b c DATA , 'd e f'];
W nowej wersji nie ma już tego wymogu:
sql_quote(<<<STR select * from table where 1=1; STR); $data = [<<<DATA a b c DATA, 'd e f'];
Wcięcie znacznika zamykającego
W aktualnej wersji PHP znacznik zamykający musi być na początku linii:
class Foo { private const SQL = <<<SQL select * from table where 1=1; SQL; }
W nowej wersji całe wyrażenie HEREDOC / NOWDOC może być wcięte:
class Foo { private const SQL = <<<SQL select * from table where 1=1; SQL; }
Co ważne, wcięcie znacznika zamykającego decyduje o ilości usuniętych białych znaków z łańcucha znaków:
// bez wcięcia znacznika zamykającego echo <<<END a b c END; /* a b c */ // wcięcie na 4 spacje echo <<<END a b c END; /* a b c */
Ja jestem bardzo zadowolony z tych zmian, często używam HEREDOC / NOWDOC np. do budowania zapytań SQL.
Uwaga na składnie nie kompatybilne z nową wersją
Jeśli w starym kodzie masz znacznik zamykający wewnątrz łańcucha znaków, może to spowodować błąd lub łańcuch zostanie źle zinterpretowany. W poniższym kodzie zostanie wyrzucony ParseError
:
$str = <<<FOO abcdefg FOO FOO; var_dump($str); // Parse error: Invalid body indentation level (expecting an indentation level of at least 4) in test.php on l
Przy tej zmianie radzę zweryfikować czy stary kod działa poprawnie. Polecam do tego Unit Testy.
Więcej w RFC: https://wiki.php.net/rfc/flexible_heredoc_nowdoc_syntaxes.
2. Przecinek po ostatnim argumencie w wywołaniach funkcji / metod
Aktualnie możemy zostawić przecinek po ostatnim elemencie w tablicy oraz w grupowanych namespace. W nowej wersji możemy przecinek zostawić po ostatnim argumencie w wywołaniach:
unset( $foo, $bar, $baz, ); echo $this->render( 'index.html', compact( 'title', 'body', 'comments', ) ); $newArray = array_merge( $arrayOne, $arrayTwo, [ 'foo', 'bar' ], ); $closure = function () {}; $closure( 1, false, );
Co ważne, dotyczy to tylko wywołań a nie deklaracji!
Jeśli dbasz o ładną historię w systemie kontroli wersji, to na pewno będziesz zadowolony.
Więcej informacji:
- https://wiki.php.net/rfc/trailing-comma-function-calls
- https://github.com/php/php-src/commit/b591c329ee3129adbdc35141bb1542d119f7a2a1
3. Wsparcie referencji w list()
Używając operatora list() nie mogłeś użyć referencji. Teraz jest to możliwe:
$array = [1, 2]; list($c, &$d) = $array; [$a, &$b] = $array;<br>
Kod jest poprawny i działa jak należy w v7.3.
Więcej informacji:
- https://wiki.php.net/rfc/list_reference_assignment
- https://github.com/php/php-src/commit/6d4de4cf0582cf33848826ab78aae58077dc2dea
4. json_encode()
i json_decode()
– rzucanie wyjątków
Gdy wywołasz json_decode()
z niepoprawnym JSON funkcja zwróci null. Co ciekawe, gdy wywołasz ją z poprawnym JSON, który zawiera tylko null, funkcja również zwróci null. Jedyną metodą, by sprawdzić czy wystąpił błąd podczas dekodowania jest odwołanie się do funkcji json_last_error()
lub json_last_error_msg()
.
W v7.3 to się zmieni. Ekipa PHP wprowadziła flagę JSON_THROW_ON_ERROR
, dzięki której funkcja json_encode()
oraz json_decode()
będą rzucać wyjątek JsonException, gdy coś pójdzie nie tak. Co ważne, kod wyjątku będzie zgodny z kodem zwracanym przez funkcję json_last_error()
, a wiadomość zgodna z wiadomością zwracaną przez json_last_error_msg()
.
try { $decoded = json_decode("{ 'invalid' : 'json' }", false, 512, JSON_THROW_ON_ERROR); } catch (JsonException $e) { echo $e->getCode(); // 4 echo $e->getMessage(); // Syntax error }
Więcej informacji:
- https://wiki.php.net/rfc/json_throw_on_error
- https://github.com/php/php-src/commit/e823770515bd0530bd3c09ea273c720b4df33734
5. Nowa funkcja is_countable()
W wersji v7.2 dodano Warning które jest wyrzucany podczas próby wywołania count()
na czymś co nie jest policzalne, np. na instancji stdClass. By temu zapobiec należy zweryfikować czy rzecz jest policzalna, jednak nie było do tego odpowiedniej funkcji, więc warunek jest złożony:
if (is_array($foo) || $foo instanceof Countable) { echo count($foo); }
W v7.3 wystarczy użyć is_countable()
:
if (is_countable($foo)) { echo count($foo); }
Więcej informacji:
6. Nowe funkcje array_key/value_first/last()
Funkcje te zostały dodane by ułatwić uzyskiwanie kluczy i wartości ostatniego i pierwszego elementu z tablicy. W v7.2 trzeba modyfikować wewnętrzny wskaźnik tablicy używając np. end()
/ current()
:
$array = [ 1 => 'a', 5 => 'm', 10 => 'z' ]; reset($array); echo key($array); // 1; echo current($array); // a end($array); echo key($array); // 10 echo current($array); // z
W v7.3 możesz użyć dedykowanych funkcji do tego, co ważne, nie zmieniają wskaźnika wewnątrz tablicy:
$array = [ 1 => 'a', 5 => 'm', 10 => 'z' ]; echo array_key_first($array); // 1 echo array_value_first($array); // a echo array_key_last($array); // 10 echo array_value_last($array); // z
Więcej informacji:
7. Nowa funkcja do haszowania haseł – Argon2id
Algorytm Argon2 ma trzy odmiany:
- Argon2i,
- Argon2d,
- Argon2id.
Ta ostatnia jest teraz zalecaną odmianą w wersji roboczej specyfikacji IEFT. W nowej wersji zostaje wsparcie poprzez funkcje password_* oraz stałą PASSWORD_ARGON2ID
:
password_hash('password', PASSWORD_ARGON2ID, ['memory_cost' => 1<<17, 'time_cost' => 4, 'threads' => 2]); var_dump(password_get_info('$argon2id$v=19$m=1024,t=2,p=2$ZUhOUVczSHpZRDBDU2ZBRA$k/vI1wKP4s0ecJIpUybRfgBeo3as1PhIV1Od6PvOEFA')); /* array(3) { ["algo"]=> int(3) ["algoName"]=> string(8) "argon2id" ["options"]=> array(3) { ["memory_cost"]=> int(1024) ["time_cost"]=> int(2) ["threads"]=> int(2) } } */
Więcej informacji:
- https://wiki.php.net/rfc/argon2_password_hash_enhancements
- https://github.com/php/php-src/commit/55277a668409b9d62ac42695934aca64e354869f
- https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03
13 grudnia zostanie udostępniona wersja produkcyjna PHP v7.3. Zmian jest naprawdę dużo, choć nie są rewolucyjne. Większość z nich to drobnostki, ale na pewno każdy znajdzie coś dla siebie. Wszystkie 34 aktualizacje postanowiłem opisać poniżej.
8. Same site cookie
Jest to póki co proponowany standard sieci web, ale widać chęć jego adopcji w przeglądarkach, językach programowania czy frameworkach. Zapobiega atakom CSRF. Metoda polega na ustawieniu odpowiedniej flagi w ciastku HTTP. Ciastka są ustawiane przez serwer za pomocą nagłówka Set-Cookie, nagłówek ten może mieć kilka flag:
Set-Cookie: foo=bar; path=/; domain=example.com; HttpOnly
Nowa flaga to SameSite która może przyjąć dwie wartości, Strict oraz Lax:
Set-Cookie: foo=bar; path=/; domain=example.com; HttpOnly; SameSite=Lax
Zarówno Lax
jak i Strict
kompletnie blokują ataki CSRF. Prosto i skuteczne. Czym się różnią oba parametry? Strict
jak sama nazwa mówi jest bardziej restrykcyjny. Załóżmy, że jesteś zalogowany do serwisu https://www.allegro.pl. Gdyby ten serwis miał ustawioną flagę na Strict, to po kliknięciu w link, który umieściłem w tym zdaniu zostałbyś przekierowany do strony, lecz byłbyś wylogowany! W trybie Lax jest mniej restrykcyjny. Pozwala on na wczytanie zawartości pliku cookie pod warunkiem, że żądanie jest wykonane przy użyciu bezpiecznej metody, tzn. GET
, HEAD
, OPTIONS
, TRACE
. Ataki CSRF
są wykonywane zazwyczaj przy użyciu metod POST
, PUT
, DELETE
. Gdyby jednak twoja strona akceptowała żądania niebezpieczne metodą GET
, np:
GET
http://www.example.com/api/deleteUser?id=1
to zdecydowanie zalecam użycia trybu Strict
.
Ekipa PHP zmodyfikowała cztery funkcje:
- setcookie,
- setrawcookie,
- session_set_cookie_params,
- session_get_cookie_params
Pierwsze trzy funkcje mają bardzo zbliżoną sygnaturę. Sygnatura funkcji setcookie w wersji v7.2 wygląda tak:
bool setcookie(string $name [, string $value = "" [, int $expire = 0 [, string $path = "" [, string $domain = "" [ , bool $secure = FALSE [ , bool $httponly = FALSE ]]]]]] )
Straszny bajzel z tym parametrami! Więc ludzie odpowiedzialni za PHP postanowili, że nie będą wprowadzać kolejnego argumentu. Zaimplementowali alternatywną wersją funkcji:
bool setcookie ( array $options )
Opcje jakie możesz teraz przekazać są następujące:
- name,
- value,
- expire,
- path,
- domain,
- secure,
- httponly,
- samesite.
Pozostałe sygnatury po zmianach:
bool setrawcookie ( array $options ) void session_set_cookie_params ( array $options )
Sygnatura funkcji session_get_cookie_params
się nie zmieniła. Dodany został nowy klucz w tablicy zwracanej:
array( "lifetime" => 0, "path" => "/", "domain" => "example.org", "secure" => true, "httponly" => true, "samesite" => "Strict" // nowy klucz )
Więcej informacji:
- https://sekurak.pl/czym-jest-podatnosc-csrf-cross-site-request-forgery/
- https://wiki.php.net/rfc/same-site-cookie
- http://git.php.net/?p=php-src.git;a=commit;h=2b58ab2
- https://tools.ietf.org/html/draft-west-first-party-cookies-07
- https://caniuse.com/#search=samesite
9. hrtime() – monotoniczny timer
Czy wiesz, że funkcja microtime()
ma pewne ograniczenia? Dość często wylicza się czas wykonania kodu właśnie przy pomocy microtime()
. Na początku kodu deklarujesz zmienną, która zawiera czas uruchomienia skryptu, a na końcu deklarujesz kolejną zmienną, tym razem czas zakończenia. Wystarczy, że obliczysz różnicę i otrzymasz czas wykonania. Kod:
$startTime = microtime(); /** * wykonaj jakiś skomplikowany kod */ $endTime = microtime(); $elapsed = $endTime - $startTime; var_dump($elapsed);
Problem polega na tym, że jeśli czas systemowy zmieni się podczas wykonywania skryptu to czas wykonywania skryptu będzie niepoprawny. Dlatego zaimplementowano funkcję hrtime()
. Dzięki niej czas wykonywania skryptu będzie super dokładny. Użycie:
$startTime = hrtime(); /** * wykonaj jakiś skomplikowany kod */ $endTime = hrtime(); $elapsed = $endTime - $startTime; var_dump($elapsed);
Więcej informacji:
- https://github.com/php/php-src/commit/83497327e747703300d35831b4aa94adf1120165
- https://github.com/php/php-src/pull/2976
- https://twitter.com/symfony_en/status/950302683671465984
10. Nowa metoda – DateTime::createFromImmutable()
Metoda ta jest lustrzana do metody DateTimeImmutable::createFromMutable()
. Więcej informacji:
- https://github.com/php/php-src/commit/d9d13aba5889ecc88c2a7638382c5ab2fcf0f984
- http://php.net/manual/en/datetimeimmutable.createfrommutable.php
11. Nowa funkcja – fpm_get_status()
Zwraca ona aktualny status menadżera procesów fastcgi
. Więcej informacji: https://github.com/php/php-src/commit/140def4ac712acac6e2561f65e1b72f8efad8717
12. Nowe funkcje w GMP
gmp_binomial()
Wylicza Symbol Newtona. Więcej informacji: https://pl.wikipedia.org/wiki/Symbol_Newtona. Commit: https://github.com/php/php-src/commit/7fea79675cfb064726f3fc7845e2c2bb1f247ea5.
gmp_lcm()
Wylicza najmniejsza wspólną wielokrotność. Więcej informacji: https://pl.wikipedia.org/wiki/Najmniejsza_wspólna_wielokrotność. Commit: https://github.com/php/php-src/commit/a1d36a1157bd88afd64119be059812dd46c4fb2d.
gpm_perfect_power()
Sprawdza czy dana liczba jest perfect power. Więcej info: https://en.wikipedia.org/wiki/Perfect_power. Commit: https://github.com/php/php-src/commit/10a336d3d062fdfa0133b22fb4f79852ec939ea5.
gmp_kronecker()
Wylicza Symbol Kroneckera. Więcej informacji: https://pl.wikipedia.org/wiki/Symbol_Kroneckera. Commit: https://github.com/php/php-src/commit/fc80114a481e1e7fc8c7be775a2cce683db9c107.
13. CompileError – nowy typ błędu
Ekipa PHP dodała nowy błąd do aktualnie dostępnych. Dziedziczy po nim ParseError
. Póki co bardzo niewielka ilość błędów kompilacji rzuci CompileError
zamiast generować błąd fatal. Dotyczy to tylko funkcji token_get_all()
w trybie TOKEN_PARSE
oraz eval()
. W przyszłości więcej błędów będzie rzucać CompileError
.
try { token_get_all('<?php declare(encoding=[]); ?>', TOKEN_PARSE); } catch (CompileError $error) { echo $error->getMessage(); // Encoding must be a literal }
Więcej informacji https://github.com/php/php-src/commit/d04917c7b361fd07e098fe29ae931fb6fac1d9e0.
14. Aktualizacja PCRE do PCRE2
PHP aktualnie bazuje na PCRE w wersji pierwszej, które jest dość stare. Dlatego postanowiono o aktualizacji do najnowszej wersji PCRE2, która została wydana w 2015. Całość zmian jest „pod maską”, ale kilka rzeczy się zmieni:
- modyfikator S nie ma wpływu na zachowanie, wszystkie wyrażenia regularne są teraz dokładniej analizowane, więc ta flaga jest zawsze włączona,
- modyfikator X również nie ma wpływu na zachowanie, PCRE2 domyślnie korzysta z tej flagi więc zespół PHP ustawił ją zawsze włączoną,
- PCRE2 korzysta z nowszego Unicode (v10 w PCRE2 vs v7 w PCRE) więc niektóre wyrażenia mogą się inaczej zachowywać,
- PCRE2 jest bardziej dokładne, więc niektóra wyrażenie które były zgodne z poprzednią implementacją, teraz mogą nie działać jak należy.
Więcej informacji:
https://wiki.php.net/rfc/pcre2-migration
https://github.com/php/php-src/pull/2857
15. MBString – wsparcie pełnego case-mapping i case-folding
W stosunku do do prostego case-mapping, jego pełna odmiana może zmienić długość łańcucha znaków. Przykład:
mb_strtoupper("Straße") // "STRAßE" - PHP v7.2 // "STRASSE" - PHP v7.3
Różne typy case-mapping oraz case-folding są dostępne przez mb_convert_case():
MB_CASE_LOWER
(używane domyślnie przez mb_strtolower),MB_CASE_UPPER
(używane domyślnie przez mb_strtoupper),MB_CASE_TITLE
,MB_CASE_FOLD
,MB_CASE_LOWER_SIMPLE
,MB_CASE_UPPER_SIMPLE
,MB_CASE_TITLE_SIMPLE
,MB_CASE_FOLD_SIMPLE
(używane domyślnie przez operacje case-insensitive).
function toCases($str) { echo "String: $strn"; echo "Lower: ", mb_convert_case($str, MB_CASE_LOWER), "n"; echo "Lower Simple: ", mb_convert_case($str, MB_CASE_LOWER_SIMPLE), "n"; echo "Upper: ", mb_convert_case($str, MB_CASE_UPPER), "n"; echo "Upper Simple: ", mb_convert_case($str, MB_CASE_UPPER_SIMPLE), "n"; echo "Title: ", mb_convert_case($str, MB_CASE_TITLE), "n"; echo "Title Simple: ", mb_convert_case($str, MB_CASE_TITLE_SIMPLE), "n"; echo "Fold: ", mb_convert_case($str, MB_CASE_FOLD), "n"; echo "Fold Simple: ", mb_convert_case($str, MB_CASE_FOLD_SIMPLE), "n"; echo "n"; } toCases("ß"); toCases("ff"); toCases("İ"); /* String: ß Lower: ß Lower Simple: ß Upper: SS Upper Simple: ß Title: Ss Title Simple: ß Fold: ss Fold Simple: ß String: ff Lower: ff Lower Simple: ff Upper: FF Upper Simple: ff Title: Ff Title Simple: ff Fold: ff Fold Simple: ff String: İ Lower: i̇ Lower Simple: i Upper: İ Upper Simple: İ Title: İ Title Simple: İ Fold: i̇ Fold Simple: İ */
Przy tej zmianie radzę zweryfikować czy stary kod działa poprawnie. Polecam Unit Testy do tego.
Więcej informacji:
- https://github.com/php/php-src/commit/582a65b06f3de125887cab02d5c561168fcf94bc
- https://github.com/php/php-src/blob/PHP-7.3/UPGRADING#L187
16. compact()
rzuci Notice gdy napotka niezdefiniowaną zmienną
Przed v7.3 funkcja compact()
w żaden sposób nie informowała o fakcie przekazania nazwy niezadeklarowanej zmiennej. W przypadku literówki jest to bardzo przydatna rzecz. Spójrz na ten przykład, który wyrzuci Notice w v7.3:
$foo = 'bar'; $baz = compact('foz'); // Notice: compact(): Undefined variable: foz
Przy tej zmianie radzę zweryfikować czy stary kod działa poprawnie. Polecam Unit Testy do tego.
Więcej informacji:
17. instanceof
– brak Fatal error w przypadku literału
Załóżmy, że masz tablicę z nieznaną Ci zawartością i chcesz z wyciągnąć tylko obiekty danej instancji. Teoretycznie wystarczy przefiltrować tablicę za pomocą instanceof
:
$dateTimeObjects = array_filter($data, function ($data) { return $data instanceof DateTime; });
Jedna w v7.2 spowoduje tp Fatal error
:
// Fatal error: instanceof expects an object instance, constant given in php shell code on line 1
Do wersji v7.2 operator instanceof działał tylko z obiektami, w v7.3 naprawili to i teraz możesz wykonać instanceof na dowolnym typie zmiennej, w przypadku gdy nie będzie ona obiektem, zwróci false:
php > var_dump(false instanceof stdClass); bool(false) php > var_dump([] instanceof stdClass); bool(false) php > var_dump('test' instanceof stdClass); bool(false) php > var_dump(1 instanceof stdClass); bool(false)
Więcej informacji:
https://github.com/php/php-src/commit/b6131364f95af68159b1c17ab48f9523e439cb65
https://github.com/php/php-src/blob/PHP-7.3/UPGRADING#L173
18. Przeprojektowane narzędzie ext_skel
Narzędzie to z pewnością przyda Ci się, jeśli planujesz stworzyć własne rozszerzenie do PHP. Każde rozszerzenie składa się z mnóstwa plików, wiele z nich wygląda bardzo podobnie i mają zbliżoną treść, wręcz duplikacje w niektórych miejscach. By ułatwić pracę przy tych plikach powstało narzędzie ext_skel. We wcześniejszych wersjach PHP narzędzie to było napisane w bash z kilkoma zależnościami. W v7.3 całość została przepisane w PHP + zero zależności. By tworzyło się rozszerzenia łatwiej. Więcej info:
- https://github.com/php/php-src/blob/php-7.3.0RC3/UPGRADING#L23
- https://github.com/php/php-src/blob/php-7.3.0RC3/ext/ext_skel.php
- https://github.com/php/php-src/commit/f35f45906eac34498c7720326fb9da9fde960871
19. Wyjątki nie wypełnią stanu error_get_last()
Wyjątki wyrzucane przez automatyczną konwersję Warning w trybie EH_THROW
już nie wypełniają stanu error_get_last()
. Teraz działają w ten sam sposób co wyjątki rzucane manualnie.
try { new DateTime('lalala'); } catch (Throwable $exception) { var_dump(error_get_last()); /* array(4) { ["type"]=> int(2) ["message"]=> string(128) "DateTime::__construct(): Failed to parse time string (lalala) at position 0 (l): The timezone could not be found in the database" ["file"]=> string(53) "/Users/damiandziaduch/Downloads/php-7.3.0RC4/test.php" ["line"]=> int(3) } */ }
try { new DateTime('lalala'); } catch (Throwable $exception) { var_dump(error_get_last()); // NULL }
Więcej:
- https://github.com/php/php-src/blob/php-7.3.0RC3/UPGRADING#L27
- https://github.com/php/php-src/commit/688b9136abcbfe28ca5c0fe6aae6143c4da58f73
20. `TypeError` zgłosi błędne typy jako int
oraz bool
Gdy w pliku PHP masz zadeklarowane strict_types
to musisz sam zadbać o typy zmiennych. Załóżmy że masz metodę która przyjmuję argument typu int
. Gdy przekażesz argument type string
, język wyrzuci TypeError. W v7.2 w przypadku gdy twój kod oczekuje int albo bool, wyjątek zgłosi że oczekuje integer
lub boolean
:
// TypeError: Argument 1 passed to Foo::__construct() must be of the type integer, string given
W v7.3 poprawiono to i teraz zgłasza int
lub bool
:
// TypeError: Argument 1 passed to Foo::__construct() must be of the type int, string given
Więcej:
- https://github.com/php/php-src/blob/php-7.3.0RC3/UPGRADING#L31
- https://github.com/php/php-src/commit/ce1d69a1f6dcf15d43029301059c25e5bc09a577
https://github.com/php/php-src/commit/fef879a2d63899ed25f39b4581c16682afdd0a8f
21. Instrukcja continue
rzuci Warning wewnątrz switch
Jeśli w kodzie masz pętlę np. while
, w której jest struktura switch
, a do tego wewnątrz switch
użyjesz continue
, by przejść do kolejnej iteracji w pętli, to twój kod był poprawny w v7.2, ale działał nie prawidłowo. continue
wewnątrz switch
działa tak samo break, przerywa dalsze przetwarzanie switch i nie przechodzi do kolejnej iteracji w pętli zawierającej switch
. Wynika to z faktu iż switch
jest uważany przez język za pętlę. Więc by przejść do kolejnej iteracji należy użyć continue 2
. Zespół PHP wyszedł temu na przeciw i wyrzuci Warning w tym przypadku:
while ($foo) { switch ($bar) { case "baz": continue; // Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? } }
Przy tej zmianie radzę zweryfikować czy stary kod działa poprawnie. Polecam Unit Testy do tego.
Więcej:
https://github.com/php/php-src/commit/04e3523b7d095341f65ed5e71a3cac82fca690e4
http://php.net/manual/en/control-structures.continue.php13 grudnia zostanie udostępniona wersja produkcyjna PHP v7.3. Zmian jest naprawdę dużo, choć nie są rewolucyjne. Większość z nich to drobnostki, ale na pewno każdy znajdzie coś dla siebie. Wszystkie 34 aktualizacje postanowiłem opisać poniżej.
22. `ArrayAccess` nie zrzutuje $offset
typu string na int
Gdy obiekt implementuje interfejs ArrayAccess i odwołujesz się do niego poprzez $foo['123']
to zostanie na nim wywołana metoda ->offsetGet(123)
w przypadku v7.2. Zwróć uwagę, że string 123 został rzucony na int. W v7.3 zostało to poprawione więc kod prawidłowo wywoła ->offsetGet('123')
; Przykład kodu:
class Foo implements ArrayAccess { public function offsetGet($offset) { var_dump($offset); // '123' w v7.2, 123 w v7.3 } public function offsetExists($offset) { } public function offsetSet($offset, $value) { } public function offsetUnset($offset) { } } $foo = new Foo(); $foo['123']; $foo[123];
Przy tej zmianie radzę zweryfikować czy stary kod działa poprawnie. Polecam Unit Testy do tego.
Więcej informacji:
23. Naprawiono referencje w statycznych własnościach klas
Statyczne własności są współdzielone między instancjami klasy oraz między instancjami klasami potomnymi. Wyjątkiem jest klasa potomna, która bezpośrednio nadpisze własność. Co ciekawe, w v7.2 jest dość ciekawy błąd, który pozwala zepsuć współdzielenie dzięki użyciu referencji:
class Foo { public static $x = 0; } class Bar extends Foo { } $y = 1; Bar::$x = &$y; var_dump(Foo::$x); // int(0) var_dump(Bar::$x); // int(1)
W v7.3 zostało to naprawione, obie klasy będę miały wartość int(1)
.
Przy tej zmianie radzę zweryfikować czy stary kod działa poprawnie. Polecam Unit Testy do tego.
Więcej informacji:
- https://github.com/php/php-src/commit/2543e61aed67add7522e0b4cdf9a13cf3e441f6f
- https://bugs.php.net/bug.php?id=76509
24. Naprawiono odpakowywanie argumentów z kluczami nie int
Okazuje się, że w PHP v7 – v7.2 był błąd, dzięki któremu ten kod:
function foo(...$args) { var_dump($args); } function gen() { yield 1.23 => 123; } foo(...gen()); // array(1) { [0] => int(123) }
w pełni działał. Zostało to naprawione i rzucany jest teraz wyjątek:
Przy tej zmianie radzę zweryfikować czy stary kod działa poprawnie. Polecam Unit Testy do tego.
Więcej informacji:
https://github.com/php/php-src/commit/3a8f26060c44b86145f332e87c53668cac58a6d0
25. BCMath użyje obsługi błędów PHP
Wcześniej niektóre ostrzeżenia rzucane przez funkcje BCMath były wypisane bezpośrednio do stderr. Przykład:
var_dump(bcpowmod(1, 1.2, 1, 1)); /* v7.2: bc math warning: non-zero scale in exponent v7.3: PHP Warning: bcpowmod(): non-zero scale in exponent */
Więcej informacji:
- https://github.com/php/php-src/commit/18adc6f0fc974a6958f719db996a99a7e49ca9ef
- https://bugs.php.net/bug.php?id=75169
26. Wsparcie dla BeOS porzucono
System ten został wykupiony przez Palm w 2001. Od tego czasu nie było nowej wersji systemu. Więcej:
- https://github.com/php/php-src/blob/php-7.3.0RC3/UPGRADING#L26
- https://github.com/php/php-src/commit/d3bc8beb4ff9e00c3742ada32788239c3f7988e0
- https://pl.wikipedia.org/wiki/BeOS
27. Deprecated – image2wbmp()
W rozszerzeniu gd do PHP są dwie bardzo zbliżone do siebie funkcje, image2wbmp()
(http://php.net/manual/en/function.image2wbmp.php) oraz imagewbmp()
(http://php.net/manual/en/function.imagewbmp.php). Robią one praktycznie to samo, więc ekipa PHP postanowiła zostawić tylko jedną. W 7.3 image2wmbp
zostaje oznaczona jako deprecated, w kolejnych wersjach zostanie usunięta.
Więcej informacji:
- https://wiki.php.net/rfc/image2wbm
- phttp://git.php.net/?p=php-src.git;a=commit;h=3cbf594dfd0708dc36f1655c50e16fa963e61501
28. Deprecated – Stałe case-insensitive
PHP aktualnie wspiera zmienne case-sensitive oraz case-insensitive. Te drugie można zadeklarować używając funkcji define()
z trzecim parametrem true
(http://php.net/manual/en/function.define.php). By ujednolicić oraz pozbyć się dziwnych problemów typu:
define('foo', 42, true); var_dump(FOO); // int(42); define('FOO', 24); var_dump(FOO); // int(24)
więc zespół PHP oznaczył stałe case-insensitive jako deprecated. Więc teraz język będzie wyrzucał takie oto wiadomości:
define('FOO', 42, true); // Deprecated: define(): Declaration of case-insensitive constants is deprecated var_dump(FOO); // Ok! var_dump(foo); // Deprecated: Case-insensitive constants are deprecated. The corre
W kolejnych wersjach stałe te zostaną usunięte.
Więcej informacji:
- https://wiki.php.net/rfc/case_insensitive_constant_deprecation
- https://github.com/php/php-src/pull/3321
29. Deprecated – funkcje mb*
bez dokumentacji
Lista funkcji:
- mbregex_encoding,
- mbereg,
- mberegi,
- mbereg_replace,
- mberegi_replace,
- mbsplit,
- mbereg_match,
- mbereg_search,
- mbereg_search_pos,
- mbereg_search_regs,
- mbereg_search_init,
- mbereg_search_getregs,
- mbereg_search_getpos,
- mbereg_search_setpos.
Wszystkie te funkcje to aliasy do funkcji z prefiksem mb_
, np. mberegi
to alias do mb_eregi
.
Więcej informacji:
- https://wiki.php.net/rfc/deprecations_php_7_3,
- https://github.com/php/php-src/pull/3366/commits/e6016ab20d6699cac9441686903051b3a815cbba
30. Deprecated – funkcje szukania w string
z argumentem nie string int
Dotyczy to funkcji:
- strpos,
- strrpos,
- stripos,
- strripos,
- strstr,
- strchr,
- strrchr,
- stristr.
Załóżmy, że w string Ala ma 10 kotów chcesz znaleźć 10, ale przekazujesz 10 jako int. Niestety to nie zadziała, ponieważ powyższe funkcje rzutują na `int` każdy argument, który nie jest typu `string`. Następnie dla wynikowej wartości pobierają znak z tablicy ASCII.10
w ASCII to znak nowej linii. Sam tego nie wiedziałem, dopóki nie zacząłem pisać tego wpis. Kod w 7.2:
$str = "Ala ma 10 kotów"; var_dump(strpos($str, "10")); // int(7) var_dump(strpos($str, 10)); // bool(false)
Kod w 7.3:
$str = "Ala ma 10 kotów"; var_dump(strpos($str, "10")); // int(7) var_dump(strpos($str, 10)); // Deprecated: strpos(): Non-string needles will be interpreted as strings in the future. Use an explicit chr() call to preserve the current behavior in php shell code on line 1 // bool(false)
Więcej informacji:
- https://wiki.php.net/rfc/deprecations_php_7_3
- https://github.com/php/php-src/pull/3366/commits/f4a9da389b728dcff2b499ff64fefd1c8e762c85
31. Deprecated – fgetss()
oraz filter string.strip_tags
Obie konstrukcje umożliwiają użycie funkcjonalności z strip_tags()
podczas streamingu. Jak wiadomo strip_tags()
działa niezbyt dokładnie, dlatego zespół PHP postanowił oznaczyć następujące twory jako deprecated:
- funkcja
fgetss()
- funkcja
gzgetss()
- metoda
SplFileObject::fgetss()
- filter streamu
string.strip_tags()
Więcej informacji:
- https://wiki.php.net/rfc/deprecations_php_7_3
- https://github.com/php/php-src/pull/3366/commits/4a5b2101e51b6be68e2bcb40f2ad6d846db1bdd8
W PHP jest wbudowana funkcja assert()
. Jest ona specjalnie traktowana przez kompilator, tzn gdy ustawisz w zend.assertions=-1
to kompilator tą funkcję po prostu pominie. Przydatne gdy korzystasz z tej funkcji, a kod wrzucasz na produkcję. Co ciekawe, możesz sobie zdefiniować własną funkcję `assert()` w nieglobalnym namespace np. fooassert()
. A w samym PHP jest błąd, po wyłączeniu wbudowanej funkcji assert()
, wszystkie zdefiniowane przez użytkownika również zostaną wyłączone. Dlatego w 7.3 podczas definiowania własnej funkcji assert()
zostaniemy poinformowani, że jest to Deprecated:
namespace Foo { function assert() { echo 'foobar'; } } // Deprecated: Defining a custom assert() function is deprecated, as the function has special semantics in php shell code on line
Więcej informacji:
- https://wiki.php.net/rfc/deprecations_php_7_3
- https://github.com/php/php-src/pull/3366/commits/5c4047b3396818e51bc95878cd45bcd3ff6d357b
32. Deprecated – FILTER_FLAG_SCHEME_REQUIRED
oraz FILTER_FLAG_HOST_REQUIRED
Od wersji 5.2.1 FILTER_VALIDATE_URL
implikuje oba filtry, więc są one zbędne. Dodatkowo tworzą wrażenie, że walidacja schematu / host może zostać pominięta, co jest nieprawdą. Od 7.3 podczas próby ich użycia otrzymasz ostrzeżenie Deprecated
:
var_dump(filter_var('//example.com/', FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED)); // Deprecated: filter_var(): explicit use of FILTER_FLAG_SCHEME_REQUIRED and FILTER_FLAG_HOST_REQUIRED is deprecated in php shell code on line 1 bool(false)
var_dump(filter_var('http://', FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED)); // Deprecated: filter_var(): explicit use of FILTER_FLAG_SCHEME_REQUIRED and FILTER_FLAG_HOST_REQUIRED is deprecated in php shell code on line 1 bool(false)
33. Deprecated – pdo_odbc.db2_instance_name
dyrektywa php.ini
Od wersji 5.1.1 ta dyrektywa została oznaczona jako Deprecated w manualu. Okazuje się jednak że w kodzie nie była oznaczona jako Deprecated
(a to psikus), więc zostało to „ujednolicone”. Dyrektywa ta modyfikuje zmienną środowiskową DB2INSTANCE
dla systemów nie-Windows. Jak wiadomo zmiana czegoś globalnego to nie jest dobry pomysł…
Więcej informacji:
- https://wiki.php.net/rfc/deprecations_php_7_3
- https://github.com/php/php-src/pull/3366/commits/fb0e8c65b9d196db44c7d92890e3540f863b6c8b
Podsumowanie
Czy warto zaktualizować do v7.3? Jasne, że tak. Nie ma aż tak dużo zmian, które nie są kompatybilne, większość starego kodu powinna bez problemu działać. Przy zmianach, które mogą robić problemy dodałem ostrzeżenia. PhpStorm już jest gotowy na zmiany: https://www.jetbrains.com/phpstorm/nextversion/#php-73-support.
Mam nadzieję że spodobał Ci się ten post. Starałem się, by wszystkie zmiany wytłumaczyć oraz dodać stosowane źródła. Nie chciałem by ten wpis był tylko tłumaczeniem changeloga.
Artykuł został pierwotnie opublikowany na damian.dziaduch.pl. Zdjęcie główne artykułu pochodzi z pexels.com.