👋 Dzień dobry.
Nowa wersja PHP v7.3 jest już tuż tuż. 13 grudnia zostanie udostępniona wersja produkcyjna. Zmian jest naprawdę dużo co widać po spisie treści 🙂. Zmiany nie są rewolucyjne, większość z nich to są drobnostki, ale na pewno każdy znajdzie coś dla siebie 👍.
📝 Spis treści:
- 💡 Elastyczna składnia HEREDOC / NOWDOC
- 💡 Przecinek po ostatnim argumencie w wywołaniach funkcji / metod
- 💡 Wsparcie referencji w
list()
- 💡
json_encode()
ijson_decode()
– rzucanie wyjątków - 💡 Nowa funkcja
is_countable()
- 💡 Nowe funkcje
array_key_first/last()
- 💡 Nowa funkcja do haszowania haseł – Argon2id
- 💡 Same site cookie
- 💡
hrtime()
– monotoniczny timer - 💡 Nowa metoda –
DateTime::createFromImmutable()
- 💡 Nowa funkcja –
fpm_get_status()
- 💡 Nowe funkcje w
GMP
- 💡
CompileError
– nowy typ błędu - 💡 Aktualizacja PCRE do PCRE2
- 💡
MBString
– wsparcie pełnegocase-mapping
icase-folding
- 🔧
compact()
rzuci Notice gdy napotka niezdefiniowaną zmienną - 🔧
instanceof
– literał jako pierwszy operand – brakFatal error
- 🔧 Przeprojektowane narzędzie
ext_skel
- 🔧 Wyjątki nie wypełnią stanu
error_get_last()
- 🔧
TypeError
zgłosi błędne typy jakoint
orazbool
- 🔧 Instrukcja
continue
rzuciWarning
wewnątrz switch - 🔧
ArrayAccess
nie zrzutuje$offset
typustring
naint
- 🔧 Naprawiono referencje w statycznych własnościach klas
- 🔧 Naprawiono odpakowywanie argumentów z kluczami nie
int
- 🔧
BCMath
użyje obsługi błędów PHP - ⚰️ Wsparcie dla BeOS porzucono
- ⚰️ Deprecated –
image2wbmp()
- ⚰️ Deprecated – Stałe case-insensitive
- ⚰️ Deprecated – funkcje
mb*
bez dokumentacji - ⚰️ Deprecated – funkcje szukania w string z argumentem nie
string
- ⚰️ Deprecated –
fgetss()
oraz filterstring.strip_tags
- ⚰️ Deprecated – definiowanie własnej funkcji
assert()
- ⚰️ Deprecated –
FILTER_FLAG_SCHEME_REQUIRED
orazFILTER_FLAG_HOST_REQUIRED
- ⚰️ Deprecated –
pdo_odbc.db2_instance_name
dyrektywaphp.ini
💡 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 lini:
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 line 3
⚠️ 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
💡 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
💡 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
💡 json_encode()
i json_decode()
– rzucanie wyjatkó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
💡 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:
💡 Nowe funkcje array_key_first/last()
Funkcje te zostały dodane by ułatwić uzyskiwanie kluczy 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; end($array); echo key($array); // 10
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_key_last($array); // 10
Więcej informacji:
💡 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
💡 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
💡 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
💡 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
💡 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
💡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
💡 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
🔧 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:
🔧 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 przezmb_strtolower
),MB_CASE_UPPER
(używane domyślnie przezmb_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: $str\n"; 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
🔧 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:
🔧 instanceof
– brak Fatal error
w przypadku literału
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, w przypadku gdy nie będzie to obiekt, 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
🔧 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
🔧 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
🔧 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
🔧 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.php
🔧 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 https://github.com/php/php-src/commit/30156d588c07e26d4e752ddb62344e96854d4773#diff-ff4e2dc4962dc25a1512353299992c8dL19
🔧 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
🔧 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:
// PHP Fatal error: Uncaught Error: Cannot unpack Traversable with non-integer keys
⚠️ Przy tej zmianie radzę zweryfikować czy stary kod działa poprawnie. Polecam Unit Testy do tego.
Więcej informacji:
🔧 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
⚰️ 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
⚰️ 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/image2wbmp
- http://git.php.net/?p=php-src.git;a=commit;h=3cbf594dfd0708dc36f1655c50e16fa963e61501
⚰️ 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 correct casing for this constant is "FOO"
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
⚰️ 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:
- https://wiki.php.net/rfc/deprecations_php_7_3
- https://github.com/php/php-src/pull/3366/commits/e6016ab20d6699cac9441686903051b3a815cbba
⚰️ 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 funkcję 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 lini. 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:
- https://wiki.php.net/rfc/deprecations_php_7_3
- https://github.com/php/php-src/pull/3366/commits/f4a9da389b728dcff2b499ff64fefd1c8e762c85
⚰️ 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:
- https://wiki.php.net/rfc/deprecations_php_7_3
- https://github.com/php/php-src/pull/3366/commits/4a5b2101e51b6be68e2bcb40f2ad6d846db1bdd8
⚰️ Deprecated – definiowanie własnej funkcji assert()
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 foo\assert()
. 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 1
Więcej:
- https://wiki.php.net/rfc/deprecations_php_7_3
- https://github.com/php/php-src/pull/3366/commits/5c4047b3396818e51bc95878cd45bcd3ff6d357b
⚰️ 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)
⚰️ 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:
- https://wiki.php.net/rfc/deprecations_php_7_3
- https://github.com/php/php-src/pull/3366/commits/fb0e8c65b9d196db44c7d92890e3540f863b6c8b
Podsumowanie.
Uff to chyba najdłuższy wpis na blogu. Poświęciłem mu dużo pracy. Gdy piszę to podsumowanie jest to 106 wersja tego wpisu 😊.
Czy warto zaktualizować do v7.3? Jasne, że tak. Nie ma aż tak dużo zmian które nie są kompatybilne wstecz, 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 🙈.
Dziękuję za poświęcony czas na przeczytanie tego potworka. Proszę udostępnij go, jestem pewny, że wiele osób będzie zainteresowanych tą tematyką 👌.
Fajnie wszystko wyjaśnione, dzięki za tekst! 🙂
Hey Marek, wielkie dzięki za opinie 🙂
Uszanowanko. Dałbyś radę zrobić powiadomionka na desktop? Bo nie wiem jak śledzić nowe posty.
rssem chocby 🙂
RSS powinien działać 🙂.
Planuje subskrypcje e-mail. Powiadomienia bardzo fajny pomysł 👍.
Niespodziewałem się takiego odzewu 🙈. Serdecznie dziekuje za wszystkie komentarze 😊
Cześć Papa Czarny.
Powiadomienia Desktop (push) są już dostępne. Kliknij proszę w ikonkę dzwonka w prawym dolnym rogu.
Pozdrawiam 🙂
x2 “używając” oraz na końcu kodu:
http://damian.dziaduch.pl/2018/11/15/php-7-3-co-nowego/#wsparcie-referencji-w-list
Dzięki za artykuł!
Ups, dzięki wielkie Sebastian, już naprawione.
Nie ma za co. Jak v7.4 będzie blisko to na pewno napiszę kolejny tekst 🙂
Czy są jakieś poprawy wydajnościowe ?
Widziałem jeden artykuł w internecie który twierdzi że tak:
https://www.phparch.com/2018/09/php-7-3-is-on-track/
Planuje w kolejnym poście sprawdzić to 👍
Uruchamiałeś kod w “instanceof – brak Fatal error w przypadku literału”? To nie rzuci fatal errora. Źle zrozumiałeś poprawkę. Prośba o poprawę artykułu.
Cześć, dzięki za opinię.
Tak, sprawdzałem czy jest Fatal i faktycznie jest:
➜ ~ php -v
PHP 7.1.23 (cli) (built: Oct 11 2018 16:19:26) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies
with Xdebug v2.6.0, Copyright (c) 2002-2018, by Derick Rethans
with Zend OPcache v7.1.23, Copyright (c) 1999-2018, by Zend Technologies
➜ ~ php -a
Interactive shell
php > var_dump(false instanceof \stdClass);
PHP Fatal error: instanceof expects an object instance, constant given in php shell code on line 1
Fatal error: instanceof expects an object instance, constant given in php shell code on line 1
php > var_dump([] instanceof \stdClass);
PHP Fatal error: instanceof expects an object instance, constant given in php shell code on line 1
Fatal error: instanceof expects an object instance, constant given in php shell code on line 1
php > var_dump('test' instanceof \stdClass);
PHP Fatal error: instanceof expects an object instance, constant given in php shell code on line 1
Fatal error: instanceof expects an object instance, constant given in php shell code on line 1
php > var_dump(1 instanceof \stdClass);
PHP Fatal error: instanceof expects an object instance, constant given in php shell code on line 1
Fatal error: instanceof expects an object instance, constant given in php shell code on line 1
Tego akurat dotyczy poprawka, więc dobrze, że rzuca błędem, choć nie bardzo rozumiem po co ktoś miałby robić takie porównania 🙂 Nie dokładnie napisałem swój pierwszy komentarz. Chodziło mi o kod z użyciem funkcji “array_filter”. Tam jest porównanie zmiennej, więc wszystko jest OK, a przykład może wprowadzać w błąd.
Witaj ponownie PHP.
Masz rację:
php > $a = null;
php > var_dump($a instanceof \stdClass);
bool(false)
php > var_dump(null instanceof \stdClass);
PHP Fatal error: instanceof expects an object instance, constant given in php shell code on line 1
Mój błąd 😉 Postaram się dzisiaj poprawić.
Dziękuję za wyłapanie tego 👍
Witaj ponownie PHP.
Poprawiłem już 🙂
Jeszcze raz dziękuję 🙃
Niestety po przejściu z 7.2 na 7.3 padło mi kodowanie polskich znaków. Stronę mam w ISO-8859-2 użycie ini_set(“default_charset”,”ISO-8859-2″); nie pomaga. Wróciłem do 7.2 🙁
Cześć Tomek.
Co Ci się wyświetla zamiast polskich znaków? Może czas rozważyć przejście na UTF-8?
Dzięki za komentarz 🙂
Pomogło dopisanie default_charset=”ISO-8859-2″ w pliku .user.ini mam polskie znaki w PHP 7.3.
Cześć Tomek.
Super wiadomość. Dziękuję za komentarz. Na pewno się komuś przyda 👍🏻