Co nowego w PHP7?

Zastanawialiście się co nowego pojawiło się wraz z nową wersją? Jeśli nie, to powinniście (zaparzyć kawę i ewentualnie) czytać dalej!

Szybkość

Jako że nie tak łatwo jest przygotować sensowne testy sprawdźmy co mówi na ten temat Internet (a podobno jest znacznie lepiej):

Zwóćcie uwagę, że wraz z wyższą wersją HHVM spada przewaga PHP 7. Według testów PHP 7 jest nawet 2 razy szybsza od poprzednich wersji – robi wrażenie. Jeśli chcecie jak sami sprawdzić jak sprawuje się wasz serwer sprawdźcie:

Nowości

Wskazanie typów skalarnych & zwrócenie wartości o określonym typie

Od PHP 5 możemy wskazać jakiego obiektu oczekujemy w parametrze:

<?php
function method1(DateTime $myDateTimeObj)
{
  // do sth
}

W PHP 7 (wreszcie) się pojawiają wraz możliwością wymuszenia zwrócenia wartości określonego typu:

<?php
function add(int $a, int $b) : int
{
  return $a + $b;
}

var_dump(add(1000, 1)); // int(1001)
var_dump(add('1000', '1')); // int(1001)

Ale zaraz! Przecież w drugim wywałoaniu pojawiają się napisy! Oszukali nas?! Otóż PHP dość liberalnie podchodzi do sprawdzania typu 10, 10e2, '100', '100meEither' (wszystkie zostaną potraktowane jako int (ostatni przypadek wyrzuci przy okazji ostrzeżenie)).

Jeśli chcecie by PHP gorliwie sprawdzał typy, trzeba posłużyć się taką oto dyrektywą:

<?php
declare(strict_types=1);

Co ważne musi być ona dołączona w pierwszej linii i działa tylko w obrębie wykonywanego pliku. Co to znaczy?

// my_mul.php
<?php
declare(strict_types=1);

function multiply(int $a, int $b) : int
{
  return $a * $b;
}

// use_mul.php
<?php

include('my_mul.php');
var_dump(multiply('10', 10)) // int(100)

ale

// use_mul_v2.php
<?php
declare(strict_types=1);

include('my_mul.php');
var_dump(multiply('10', 10)) // Uncaught TypeError: Argument 1 passed to multiply() must be of the type integer, string given

Więcej informacji: https://wiki.php.net/rfc/scalar_type_hints_v5 (RFC)

“Kosmiczny” operator <=>

Jak to działa?

echo 1 <=> 1; // 0 - dla równych wartości
echo 1 <=> 0; // 1 - jeśli prawa wartość jest większa od wartości po lewej
echo 0 <=> 1; // -1 - jeśli prawa wartość jest mniejsza od wartości po lewej

działa także dla napisów

echo "x" <=> "x"; // 0
echo "y" <=> "x"; // 1
echo "y" <=> "z"; // -1

jak również tablic i obiektów:

echo [] <=> []; // 0
echo [1, 2, 3] <=> [1, 2, 3]; // 0
echo [1, 2, 3] <=> [1, 2]; // 1
echo [1, 2, 4] <=> [1, 2, 3]; // 1
echo [1, 2] <=> [1, 2, 3]; // -1
echo [1, 2, 3] <=> [1, 2, 4]; // -1

$a = (object) ["field" => "abc"]; 
$b = (object) ["field" => "abc"]; 
echo $a <=> $b; // 0

$a = (object) ["other_field" => "cba"]; 
$b = (object) ["other_field" => "abc"]; 
echo $a <=> $b; // 1


$a = (object) ["one_another" => "abc"]; 
$b = (object) ["one_another" => "cba"]; 
echo $a <=> $b; // -1

Więcej informacji: https://wiki.php.net/rfc/combined-comparison-operator

Null Coalesce Operator?

Proste zastosowanie

$username = $user->getName() ?? 'nobody';

Odpowiednik

$username = ($user->getId() === NULL) ? 'nobody' : $user->getName()

$width = $imageData['width'] ?? 100;

Bardziej złożony przykład

Po kolei próbujemy pozyskać konfigurację ze zmiennej $_GET['config'] jeśli nie ma żadnej wartości próbujemy pozyskać informację z pola $this->config. Jeśli i tu nie znajdziemy żadnej wartości wykorzystujemy wartość static::$defaultConfig

$defaultConfig = $_GET['config'] ?? $this->config ?? static::$defaultConfig;

Klasy anonimowe

Gdy nie zawsze chcemy (chce -opłaca się nam) tworzyć niezwykle mało użyteczną klasę możemy posłużyć się klasą anonimową. Przykład z Symfony wzięty:

use Symfony\Component\Process\Process;

$process = new class extends Process {
  public function start()
{
            // do sth
  }
};

zamiast

namespace My\Namespace\Process;

use Symfony\Component\Process\Process as Base;

class Process extends Base {
  public function start() {
            // do sth
  }
}

$process = new \My\Namespace\Process\Process;

Czy to na pewno jest ładnie? A to nie jest!?

array_walk($arr, function($v, $k) {
  echo 'Key: '.$k.' value: '.$k;
});

Więcej informacji: https://wiki.php.net/rfc/anonymous_classes

Stała tablicowa via define()

Od wersji 5.6 tablice mogą być także stałymi:

<?php
const MY_ARRAY = [
  'hey',
  'it's',
  'an',
  'array!',
];

ale dopiero wersja 7 wprowadza możliwość definiowania takiej stałej przy pomocy funkcji define()

define()
<?php
define('MY_ARRAY', [
  'I'm',
  'an array',
  'too!',
]);

Grupowanie deklaracji

do PHP 5.6

<?php
use Framework\Component\ClassA;
use Framework\Component\ClassB as ClassC;
use Framework\Component\OtherComponent\ClassD

od PHP 7

<?php
use Framework\Component\{
    ClassA,
    ClassB as ClassC,
    OtherComponent\ClassD
};

można używać też do funkcji i stałych

<?php
use Framework\Component\{
    ClassA,
    function OtherComponent\someFunction,
    const OtherComponent\SOME_CONSTANT
};

 Łap błędy!

W PHP 5.x błędy [RECOVERABLE] FATAL ERROR powodowały zatrzymanie skryptu, a dlaczego by ich nie złapać? Od teraz można. Nowa hierarchii wyjątków:

interface Throwable
  |- Exception implements Throwable
        |- ...
  |- Error implements Throwable
        |- TypeError extends Error
        |- ParseError extends Error
        |- ArithmeticError extends Error
            |- DivisionByZeroError extends ArithmeticError
        |- AssertionError extends Error

<?php
$foo = new class() {
  public function bar()
  {
            echo 'works';
  }
};

try {
  $foo->bar(); // works
  $foo->bar2(); // bar2 caused an error!
} catch (Error $e) {
  echo 'bar2 caused an error!';
}

Wyrażenia po nowemu

Szybkość nowej wersji wzięła się m. in. z wprowadzenia AST, co wpłynęło również na sposób interpretowania zapisanych wyrażeń:

Wyrażenie Działanie w PHP 5 Działanie w PHP 7
$$foo[‚bar’][‚baz’] ${$foo[‚bar’][‚baz’]} ($$foo)[‚bar’][‚baz’]
$foo->$bar[‚baz’] $foo->{$bar[‚baz’]} ($foo->$bar)[‚baz’]
$foo->$bar[‚baz’]() $foo->{$bar[‚baz’]}() ($foo->$bar)[‚baz’]()
Foo::$bar[‚baz’]() Foo::{$bar[‚baz’]}() (Foo::$bar)[‚baz’]()

Więcej informacji: https://wiki.php.net/rfc/abstract_syntax_tree

A co żegnamy?

  • <% // php code %>
  • <script language='php'> // php code </script>
  • Wszystkie funkcje ereg_ (zdeprecjonowane już od wersji 5.3.0); zastąpione przez funkcje preg_
  • Wszystkie funkcnie mysql_ (zdeprecjonowane w wersji 5.5.0); zastąpione prze funkcje mysqli_ lub PDO
  • Funkcja split() (zdeprecjonowana od wersji 5.3.0); możliwe alternatywy preg_split(), explode(), str_split()
  • Wielokrotne użycie domyślnego wariantu (default) w wyrażeniu switch

Statyczne odwołania do niestatycznych metod będą usunięte w przyszłości (co jest komunikowane odpowiednim ostrzeżeniem)

class foo()
{
  public function bar()
  {
        echo 'regular method';
  }
}

foo::bar(); // Deprecated: Non-static method foo::bar() should not be called statically
            // regular method

Konstruktory znane z PHP 4 również są oznaczone jako przestarzałe

class foo2()
{
  public function foo2()
  {
        echo 'the constructor';
  }
} // Deprecated:  Methods with the same name as their class will not be constructors in a future version of PHP;

Więcej informacji: http://php.net/manual/en/migration70.deprecated.php

To oczywiście nie wszystkie zmiany (starałem się wybrać raczej te najciekawsze i te które moim zdaniem są przydatne). Jeśli jesteście ciekawi wszystkich zmian w stosunku do wersji 5.6 zajrzyjcie na http://php.net/manual/en/migration70.php.

FacebookTwitterGoogle+LinkedIn