A PHP 8 újdonságai és változásai

botond küldte be 2021. 02. 02., k – 18:20 időpontban

Tartalom

 

Bevezető

PHP 2020. november 26-án adta ki legújabb főverzióját a PHP 8-at (8.0.0), valamint azóta 2021. január 7-én pedig a PHP 8.0.1-et is, ezért már itt az ideje, hogy itt az oldalon is foglalkozzunk vele. Ebben a rövidebb cikkben átnézzük a PHP 8 fontosabb újításait és változtatásait.

 

 

A PHP 8 újdonságai

A PHP 8 sok újdonsággal bővült, illetve sok mindent változtattak benne.

RFC-k

Az RFC (az angol Request For Comments rövidítése, magyarul: kéretik megkritizálni) egy olyan dokumentum, mely egy új Internet-szabvány beiktatásakor adnak közre. Az új szabvány első tervezete saját számmal kerül a nyilvánosság elé, egy adott időtartamon belül bárki hozzászólhat. Ezeket a hozzászólásokat rendszerezik, majd többszöri módosítás után a szabványtervezetet elfogadják, vagy eldobják. Forrás

A PHP esetében az RFC-k segítségével gyűjtik össze azokat a terveket, amik idővel a PHP nyelv szabványává válnak. Ezen az oldalon láthatjuk a jelenlegi RFC-ket, amik még szavazati fázisban, vagy megvitatás alatt, vagy piszkozatban, vagy elfogadott állapotban, vagy pedig megvalósított állapotban vannak.

Itt pedig érdekességképpen élőben követhetjük, hogy melyik RFC hány szavazattal rendelkezik, stb: php-rfc-watch.beberlei.de

A következő részben a teljesség igénye nélkül átnézünk néhány fontosabbat ezek közül.

Union types (RFC)

A union types (unió típusok) segítségével változóknak megadhatunk két, vagy többféle típust is, mint elfogadható típusok. Példák:

// Példa #1
class Number {
    private int|float $number;
 
    public function setNumber(int|float $number): void {
        $this->number = $number;
    }
 
    public function getNumber(): int|float {
        return $this->number;
    }
}

// Példa #2
// phpdoc jelöléssel:
class Number {
    /**
     * @var int|float $number
     */
    private $number;
 
    /**
     * @param int|float $number
     */
    public function setNumber($number) {
        $this->number = $number;
    }
 
    /**
     * @return int|float
     */
    public function getNumber() {
        return $this->number;
    }
}

Named arguments (RFC)

A named arguments  (magyarosan: nevezett paraméterek) lehetővé teszik, hogy egy függvénynek a paramétereket a neveik alapján adhassuk meg, ne a pozícióik alapján. Ez a paramétereket sorrendfüggetlenné teszi, valamint lehetővé teszi az alapértelmezett/opcionális értékek kihagyhatóságát is. Példák:

// Példa #1:
// Hagyományos paraméterek használata:
array_fill(0, 100, 50);
 
// Named arguments használata:
array_fill(start_index: 0, num: 100, value: 50);

// A nevezett argumentumok átadásának sorrendje nem számít.
// A fenti példa ugyanabban a sorrendben adja át őket, mint amelyet a függvény aláírásában deklaráltunk, 
// de bármilyen más sorrend is lehetséges:
array_fill(value: 50, num: 100, start_index: 0);


// Példa #2:
function foo(string $a, string $b, ?string $c = null, ?string $d = null) 
{ /* … */ }

foo(
    b: 'value b', 
    a: 'value a', 
    d: 'value d',
);

JIT (RFC)

A JIT (Just In Time compilation) egy futásidejű fordítási módszer, amely a bájtkód-fordított rendszerek teljesítményének növelésére szolgáló technika. A bájtkódot futási időben fordítja natív kódra. A PHP 8 esetében akár háromszoros teljesítménynövekedést is eredményezhet a tisztán matematikai számításokat végző részek futtatásakor, azonban más webes alkalmazásoknál hasonló teljesítményt produkál, mint elődje, a PHP 7.4.

 

 

Attributes (RFC)

Az attributes (attribútumok), más nyelvekben közismertebb nevén kommentárok, lehetőséget kínálnak a metaadatok osztályokhoz adására anélkül, hogy fel kellene dolgozni a docblock-okat. Az attribútumok lehetővé teszik a kód deklarációjába közvetlenül beágyazott konfigurációs irányelvek meghatározását.

Hasonló megoldások léteznek más programozási nyelvekben is: Annotation-ok a Java-ban, Attribute-ok a C #-ban, C ++-ban, Rust-ban, Hack-ban és Decorator-ok a Python-ban és Javascript-ben. PHP példa:

// PHP 7-ben:
class PostsController
{
    /**
     * @Route("/api/posts/{id}", methods={"GET"})
     */
    public function get($id) { /* ... */ }
}

// PHP 8-ban:
class PostsController
{
    #[Route("/api/posts/{id}", methods: ["GET"])]
    public function get($id) { /* ... */ }
}

A PHPDoc kommentárok helyett mostantól strukturált metaadatokat használhatunk a PHP natív szintaxisával. Forrás

Constructor Property Promotion (RFC)

Constructor Property Promotion, másnéven egyszerűsített konstruktor, leegyszerűsíti az objektumok létrehozásának menetét. Ahelyett, hogy külön meg kellene adni az osztálytulajdonságokat és a konstruktort, a PHP 8 már egybe kombinálja ezeket. Példák:

// Példa #1:
// Korábbi PHP-k esetén:
class Point {
  public float $x;
  public float $y;
  public float $z;

  public function __construct(
    float $x = 0.0,
    float $y = 0.0,
    float $z = 0.0
  ) {
    $this->x = $x;
    $this->y = $y;
    $this->z = $z;
  }
}

// PHP 8-ban ugyanez:
class Point {
  public function __construct(
    public float $x = 0.0,
    public float $y = 0.0,
    public float $z = 0.0,
  ) {}
}



// Példa #2:
// Korábbi PHP-k esetén:
class Money 
{
    public Currency $currency;
 
    public int $amount;
 
    public function __construct(
        Currency $currency,
        int $amount,
    ) {
        $this->currency = $currency;
        $this->amount = $amount;
    }
}

// PHP 8-ban:
class Money 
{
    public function __construct(
        public Currency $currency,
        public int $amount,
    ) {}
}

Match (RFC)

 

 

A match kifejezés tulajdonképpen a switch kifejezés okosabb változata: A match szerkezetnek lehet visszatérési értéke, nem igényel break kifejezéseket, egyesítheti a feltételeket, szigorú típus-összehasonlításokat alkalmaz, és nem végez semmilyen típus kényszerítést. Példák:

// Példa #1:
// PHP 7-ben:
switch (1) {
    case 0:
        $result = 'Foo';
        break;
    case 1:
        $result = 'Bar';
        break;
    case 2:
        $result = 'Baz';
        break;
}
 
echo $result;
//> Bar


// PHP 8-ban
echo match (1) {
    0 => 'Foo',
    1 => 'Bar',
    2 => 'Baz',
};
//> Bar


// Példa #2:
// PHP 7-ben:
switch ('foo') {
    case 0:
      $result = "Oh no!\n";
      break;
    case 'foo':
      $result = "This is what I expected\n";
      break;
}
echo $result;
//> Oh no!

// PHP 8-ban:
echo match ('foo') {
    0 => "Oh no!\n",
    'foo' => "This is what I expected\n",
};
//> This is what I expected

Ésszerűbb karakterlánc-szám összehasonlítás (RFC)

Az ésszerűbb karakterlánc-szám összehasonlítás (Saner string to number comparisons) kijavít egy régi PHP furcsaságot: a PHP korábbi verzióiban amikor a parancsfeldolgozó karakterláncokat és számokat hasonlított össze a "==" és más nem szigorú összehasonlító operátorok használatával, az úgy történt, hogy a karakterláncot először számmá alakította, és ezután egész számokként, vagy lebegőpontos számokként hasonlította össze. Ez sok esetben nem várt eredményt idézett elő, mint például ebben az esetben is: 0 == "foobar" , aminek az értéke igaz volt. Ez az RFC azt írja elő, hogy a nem szigorú összehasonlításokat hasznosabbá és kevésbé hibára hajlamossá tegye, azáltal, hogy számként történő összehasonlítást csak akkor végezzen, ha az összehasonlítandó karakterlánc valójában numerikus szám. Ellenkező esetben a számok kerüljenek karakterlánccá alakítva, majd egy karakterlánc összehasonlítást hajtson végre rajtuk. Példa:

// PHP korábbi változataiban:
0 == 'foobar' // true

// PHP 8-ban:
0 == 'foobar' // false



Összehasonlítás	| Korábban	| PHP 8-ban
----------------------------------------
 0 == "0"		| true		| true
 0 == "0.0"		| true		| true
 0 == "foo"		| true		| false
 0 == ""		| true		| false
42 == "   42"	| true		| true
42 == "42foo"	| true		| false

A nullsafe operátor (RFC)

A nullsafe operátor segítségével a null ellenőrzési feltételek helyett most már használható a híváslánc az új nullsafe operátorral. Ha a lánc egyik elemének értékelése sikertelen, akkor a teljes lánc végrehajtása megszakad, és a teljes lánc nullára értékelődik. Példa:

// PHP korábbi verzióiban:
$country =  null;

if ($session !== null) {
  $user = $session->user;

  if ($user !== null) {
    $address = $user->getAddress();
 
    if ($address !== null) {
      $country = $address->country;
    }
  }
}


// PHP 8-ban:
$country = $session?->user?->getAddress()?->country;

Throw kifejezés (RFC)

 

 

Ez az RFC megváltoztatja a korábbi throw utasítást (statement), amelyet kifejezéssé (expression) alakít, ami lehetővé teszi a kivételek új helyeken történő dobását, ami rugalmasabbá teszi a használatát. Példa:

// Korábbi PHP változatokban használható throw minta:
// függvény létrehozáűsa egy kivétellel
function checkNum($number) {
  if($number>1) {
    throw new Exception("Az értéknek 1-nek vagy akatta kell lennie");
  }
  return true;
}


// PHP 8-ban használható minták, amik ezelőtt nem voltak lehetségesek:
// Ez korábban nem volt lehetséges, mivel a (PHP 7.4-ben bevezetett) nyíl függvények  csak egyetlen kifejezést fogadtak el, míg a throw korábban  egy utasítás volt.
$callable = fn() => throw new Exception();
 
// $value is non-nullable.
$value = $nullableValue ?? throw new InvalidArgumentException();
 
// $value is truthy.
$value = $falsableValue ?: throw new InvalidArgumentException();
 
// $value is only set if the array is not empty.
$value = !empty($array)
    ? reset($array)
    : throw new InvalidArgumentException();


$triggerError = fn () => throw new MyError();

Új str_starts_with() és str_ends_with() karakterlánc kereső függvények (RFC)

A karakterláncok kezdetének és végének ellenőrzése nagyon gyakori feladat, amelynek egyszerűnek kell lennie. Ennek a feladatnak a teljesítése nem könnyű, ezért sok keretrendszer választotta ennek beépítését. Több magas szintű programozási nyelvben is megvalósították ezt a funkciót, mint például a JavaScript, a Java, a Haskell és a Matlab. A karakterlánc kezdetének és végének ellenőrzése nem lehet olyan feladat, amely megköveteli egy PHP keretrendszer behúzását vagy egy potenciálisan nem optimális (vagy rosszabb esetben hibás) funkció kifejlesztését a programozási nyelvben.

Az str_starts_with() ellenőrzi, hogy egy karakterlánc egy másik karaktersorozattal kezdődik-e, és visszaadja a megfelelő logikai értéket (igaz/hamis)
Az str_ends_with() ellenőrzi, hogy egy karakterlánc egy másik karaktersorozattal végződik-e, és visszaadja a megfelelő logikai értéket (igaz/hamis)

str_starts_with('abcdefgh', 'abc'); // igaz
str_ends_with('abcdefgh', 'fgh'); // igaz

 

Elavult dolgok (Deprecated)

Itt kerülnek felsorolásra azok a dolgok, amik a PHP korábbi verzióiban még mindenféle hiba nélkül működtek, de a PHP 8-ban elavulttá lettek nyilvánítva. Az elavult (deprecated) dolgok általában még nem kerülnek kivezetésre az adott verzióból, tehát még nem dobnak végzetes hibákat (Fatal error) de már "Deprecated" típusú PHP notice-eket dobálnak, és majd egy jövőbeni PHP verzióban kerülnek végleges kivezetésre.

Reflection API ReflectionMethod osztály egyes metódusai

A PHP Reflection API ReflectionMethod osztályának néhány metódusa elavulttá vált (deprecated), ezért amik az alábbi metódusokat használják, notice-okat kapnak:

  • ReflectionParameter::getClass()
  • ReflectionParameter::isArray()
  • ReflectionParameter::isCallable()

Forrás: https://php.watch/versions/8.0/deprecated-reflectionparameter-methods

Ezt azért emeltem itt ki, mert például a phpMyAdmin 4.9.x verziója is ilyen notice-okat dobál PHP 8-on futtatva, tehát ez az ág még nem 100%-osan kompatibilis a PHP 8-al.

A phpMyAdmin által megjelenített hibaüzenet:

Deprecation Notice in ./libraries/classes/Di/ReflectorItem.php#82
 Method ReflectionParameter::getClass() is deprecated

 

Egyéb változások

Itt kerülnek felsorolásra a fenti csoportokba nem sorolt változások, újdonságok.

Új alapértelmezett Error reporting szint

A PHP 8-ban megváltozott az alapértelmezett Error reporting szint. A korábban érvényben lévő alapértelmezett beállítás (error_reporting = E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED) helyett a PHP 8-ban az error_reporting = E_ALL beállítás lépett érvénybe. Forrás

// PHP 8 előtti verzióban az alapértelmezett error_reporting érték:
// php.ini beállítás:
// error_reporting = E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
// PHP script beállítás:
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);

// A PHP 8-ban pedig:
// php.ini beállítás:
// error_reporting = E_ALL
// PHP script beállítás:
error_reporting(E_ALL);

Mindez azt eredményezi, hogy a PHP 8 alapértelmezetten minden olyan hibát megjelenít, amiket a PHP korábbi verziói figyelmen kívül hagytak. Ezért éles használat esetén célszerű kézzel beállítani a megfelelő értéket, amennyiben nem szeretnénk, ha a weboldalainkon minden apró hiba megjelenjen.

Végzetes hibák felfedése

A forráskódban az utasítások elé helyezett '@' operátorok a PHP 8-ban már nem nyomják el a végzetes hibákat, így azok felfedésre kerülnek, amik korábban nem voltak láthatók. Éles szerverkörnyezetek esetén gondoskodjunk róla, hogy a "display_errors=Off" beállítás legyen érvényben, hogy az elrejteni kívánt végzetes hibák továbbra is rejtve maradjanak.

 

PHP 8 telepítése és beállítása

A PHP 8 telepítéséről és beállításáról egy másik leírásban tájékozódhatunk:

 

 

Konklúzió

Ezek lennének a PHP 8 fontosabb változásai. Természetesen ezeknél még sokkal több újdonságot tartalmaz, ezeknek részletesebb megismeréséhez látogassunk el a lenti linkekre. Apránként bővítem még ezt a cikket, ahogy belebotlok újabb PHP 8-as érdekességekbe.