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ówNowa funkcja
is_countable()
Nowe funkcje
array_key_first/last()
Nowa funkcja do haszowania haseł – Argon2id
Same site cookie
hrtime()
– monotoniczny timerNowa metoda –
DateTime::createFromImmutable()
Nowa funkcja –
fpm_get_status()
Nowe funkcje w
GMP
CompileError
– nowy typ błęduAktualizacja 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 switchArrayAccess
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 PHPWsparcie dla BeOS porzucono
Deprecated –
image2wbmp()
Deprecated – Stałe case-insensitive
Deprecated – funkcje
mb*
bez dokumentacjiDeprecated – 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ę:
Postaram się dzisiaj poprawić.
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
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