После того, как версия PHP 7.3 была выпущена, я решил подробно изучить PHP development: что разрабатывается и как лучше
понять его новый потенциал и оптимизацию.

После просмотра быстрого списка возможностей, которые PHP реализовал во время разработки PHP v7.x, я решил составить список самостоятельно.

Мы начнем с PHP 5.6 в качестве основы и рассмотрим, что было добавлено, изменено. Я также добавил ссылки на официальную документацию для каждой из упомянутых вещей, так что, если вы заинтересованы в глубоком чтении — не стесняйтесь.

PHP 7.0

Поддержка анонимных классов

Анонимный класс может использоваться над именованным классом:

  • Когда класс не нужно документировать
  • Когда класс используется только один раз во время выполнения
new class($i) {
    public function __construct($i) {
        $this->i = $i;
    }
}

Целочисленная функция деления

Целочисленная функция деления — безопасный способ деления (даже на 0)
Возвращает целочисленное деление первого операнда на второй. Если делитель (второй операнд) равен нулю, он выдает E_WARNING и возвращает FALSE.

intdiv(int $numerator, int $divisor)

Добавлен новый нулевой оператор объединения

$x = NULL;
$y = NULL;
$z = 3;
var_dump($x ?? $y ?? $z); // int(3)
 
$x = ["c" => "Лазизбек"];
var_dump($x["a"] ?? $x["b"] ?? $x["c"]); 
// string(16) "Лазизбек"

Скалярные объявления типов

Это только первый шаг к достижению более сильных преимуществ типизированного языка программирования в PHP — v0.5.

function add(float $a, float $b): float {
    return $a + $b;
}
 
add(1, 2); // float(3)

Декларации возвращаемого типа

Добавлена возможность возвращать типы вне скалярных классов, включая наследование. Хех, до сих пор как-то совершенно не хватало возможности сделать его необязательным (введено в v7.1 :))

interface A {
    static function make(): A;
}
class B implements A {
    static function make(): A {
        return new B();
    }
}

Объявления группового использования




use FooLibrary\Bar\Baz\ClassA;
use FooLibrary\Bar\Baz\ClassB;
use FooLibrary\Bar\Baz\ClassC;

// Сгруппированный синтаксис `use`:
use FooLibrary\Bar\Baz\ClassD as Fizbo;
 
use FooLibrary\Bar\Baz\{ ClassA, ClassB, ClassC, ClassD as Fizbo };

Генерация делегирования

В функций генератора разрешен следующий новый синтаксис:

yield from <expr>

Улучшенная производительность

PHP 7 в два раза быстрее, чем PHP 5.6.

PHP запрос в секунду. Диаграмма
PHP запрос в секунду. Диаграмма

Значительно уменьшено использование памяти

Пиковое использование памяти в КиБ (страница с доступом к базе данных)
Пиковое использование памяти в КиБ (страница с доступом к базе данных)

Как видно из графиков, PHP 7.0 — это огромное улучшение с точки зрения производительности и использования памяти. Для страницы с запросами к базе данных версия 7.0.0 более чем в 3 раза быстрее, чем 5.6 с включенным opcache, и в 2.7 раза быстрее без opcache! С точки зрения использования памяти, разница также значительна!

Throwable interface

Реструктурированные «классы исключений» имеют схему именования, которая не является интуитивно понятной и приведет к меньшей путанице, особенно для новых пользователей.
Ошибки и исключения теперь реализуют Throwable.
Вот иерархия Throwable:

interface Throwable
  |- Error implements Throwable
      |- ArithmeticError extends Error
          |- DivisionByZeroError extends ArithmeticError
      |- AssertionError extends Error
      |- ParseError extends Error
      |- TypeError extends Error
          |- ArgumentCountError extends TypeError
  |- Exception implements Throwable
      |- ClosedGeneratorException extends Exception
      |- DOMException extends Exception
      |- ErrorException extends Exception
      |- IntlException extends Exception
      |- LogicException extends Exception
          |- BadFunctionCallException extends LogicException
              |- BadMethodCallException extends BadFunctionCallException
          |- DomainException extends LogicException
          |- InvalidArgumentException extends LogicException
          |- LengthException extends LogicException
          |- OutOfRangeException extends LogicException
      |- PharException extends Exception
      |- ReflectionException extends Exception
      |- RuntimeException extends Exception
          |- OutOfBoundsException extends RuntimeException
          |- OverflowException extends RuntimeException
          |- PDOException extends RuntimeException
          |- RangeException extends RuntimeException
          |- UnderflowException extends RuntimeException
          |- UnexpectedValueException extends RuntimeException

Синтаксис выхода из кодовой точки Unicode — «\ u {xxxxx}»

echo "\u{202E}Reversed text"; // outputs ‮Reversed text
echo "mañana"; // "ma\u{00F1}ana"
echo "mañana"; // "man\u{0303}ana" "n" with combining ~ character (U+0303)

Контекстно-зависимый лексер

Благодаря этому глобально зарезервированные слова стали полурезервированными:

callable  class  trait  extends  implements  static  abstract  final  public  protected  private  const
enddeclare  endfor  endforeach  endif  endwhile  and  global  goto  instanceof  insteadof  interface
namespace  new  or  xor  try  use  var  exit  list  clone  include  include_once  throw  array
print  echo  require  require_once  return  else  elseif  default  break  continue  switch  yield
function  if  endswitch  finally  for  foreach  declare  case  do  while  as  catch  die  self parent

по-прежнему запрещено называть класс как класс из-за ::class

PHP 7.1

Обнуляемые типы

function answer(): ?int  {
    return null; //ok
}

function answer(): ?int  {
    return 42; // ok
}

function answer(): ?int {
    return new stdclass(); // error
}
function say(?string $msg) {
    if ($msg) {
        echo $msg;
    }
}

say('hello'); // ok -- будет печатать hello
say(null); // ok -- не будет печатать print
say(); // error -- пропущенный параметр
say(new stdclass); //error -- bad type

Void Returns

function should_return_nothing(): void {
    return 1; // Fatal error: A void function must not return a value
}

В отличие от других возвращаемых типов, которые применяются при вызове функции, этот тип проверяется во время компиляции, что означает, что возникает ошибка без необходимости вызова функции.

Функция с возвращаемым типом «void» или «void function» может либо возвращаться implicitly, либо иметь оператор return без значения:

function lacks_return(): void {
    // ok
}

Итерируемый псевдотип

Обычно функции принимают или возвращают либо массив, либо объект, реализующий traversable для использования с foreach. Однако, поскольку массив является примитивным типом, а Traversable является интерфейсом, в настоящее время нет способа использовать объявление типа для параметра или возвращаемого типа, чтобы указать, что значение является итеративным.

function foo(iterable $iterable) {
    foreach ($iterable as $value) {
        // ...
    }
}

Итерируемое также можно использовать в качестве возвращаемого типа, чтобы указать, что функция возвратит итеративное значение. Если возвращаемое значение не является массивом или экземпляром Traversable, выдается ошибка TypeError.

function bar(): iterable {
    return [1, 2, 3];
}

Параметры, объявленные как итеративные, могут использовать ‘null‘ или массив в качестве значения по умолчанию.

function foo(iterable $iterable = []) {
    // ...
}

«Closure» от «callable»

class Closure {
    ...
    public static function fromCallable(callable $callable) : Closure {...}
    ...
}

Синтаксис квадратной скобки для назначения деструктурирования массива

$array = [1, 2, 3];
// Присваивает $a, $b и $c значения их соответствующих элементов массива в $array с ключами, пронумерованными от нуля
[$a, $b, $c] = $array;
 
// Присваивает $a, $b и $c значения элементов массива в $array с ключами «a», «b» и «c» соответственно
["a" => $a, "b" => $b, "c" => $c] = $array;

Square bracket syntax for list()

$powersOfTwo = [1 => 2, 2 => 4, 3 => 8];
list(1 => $oneBit, 2 => $twoBit, 3 => $threeBit) = $powersOfTwo;

Класс постоянной видимости

class Token {
	// Константы по умолчанию для общедоступных
	const PUBLIC_CONST = 0;
 
        // Константы тогда также могут иметь определенную видимость
        private const PRIVATE_CONST = 0;
        protected const PROTECTED_CONST = 0;
        public const PUBLIC_CONST_TWO = 0;
 
        //Константы могут иметь только один список объявлений видимости
        private const FOO = 1, BAR = 2;
}

Отлов нескольких типов исключений

try {
   // Какой-то код ...
} catch (ExceptionType1 | ExceptionType2 $e) {
   // Код для обработки исключения
} catch (\Exception $e) {
   // ...
}

PHP 7.2

Подсчет неисчислимых объектов

Вызов count() для объекта, который не implement’ует интерфейс Countable (http://php.net/manual/en/class.countable.php), возвращает 1 (нелогично).
В этой версии добавлено предупреждение при вызове count () с параметром, который является скалярным, нулевым или объектом, который не реализует Countable.

Конечные запятые в синтаксисе списка в использовании пространства имен

use Foo\Bar\{ Foo, Bar, Baz, };

Хэш пароля Argon2

Существующие функции password_* обеспечивали совместимый, упрощенный интерфейс для хеширования паролей. В этом RFC предлагается реализация Argon2i (v1.3) в функциях password_ * для использования в качестве безопасной альтернативы Bcrypt.

Отладка эмуляции подготовленного оператора PDO

$db = new PDO(...);
 
// работает с операторами  без «связанных» значений
$stmt = $db->query('SELECT 1');
var_dump($stmt->activeQueryString()); // => string(8) "SELECT 1"
 
$stmt = $db->prepare('SELECT :string');
$stmt->bindValue(':string', 'foo');
 
// returns unparsed query before execution
var_dump($stmt->activeQueryString()); // => string(14) "SELECT :string"
 
// returns parsed query after execution
$stmt->execute();
var_dump($stmt->activeQueryString()); // => string(11) "SELECT 'foo'"

PHP 7.3

JSON_THROW_ON_ERROR

Отсутствие адекватного способа обработки ошибок при использовании JSON было проблемой довольно долгое время, и веб-разработчики во всем мире видели в этом огромный недостаток языка.
До PHP v7.2 нам приходилось использовать обходной путь для получения ошибки из JSON, и она не была ни надежной.

json_decode("{");
json_last_error() === JSON_ERROR_NONE // false
json_last_error_msg() // "Syntax error"

Итак, давайте посмотрим, как мы можем использовать новый метод

use JsonException;
 
try {
    $json = json_encode("{", JSON_THROW_ON_ERROR);
    return base64_encode($json);
} catch (JsonException $e) {
    throw new EncryptException('Could not encrypt the data.', 0, $e);
}

Как видно из предыдущего кода, функция json_encode теперь имеет необязательный параметр JSON_THROW_ON_ERROR — он будет перехватывать ошибку и отображать ее, используя следующие методы исключения:

$e->getMessage(); // как json_last_error_msg()
$e->getCode(); // как json_last_error()

Добавлена функция is_countable

// Before:
if (is_array($foo) || $foo instanceof Countable) {
    // $foo countable
}
// After
if (is_countable($foo)) {
    // $foo countable
}

Добавлены функции массива array_key_first (), array_key_last ()

$firstKey = array_key_first($array);
$lastKey = array_key_last($array);

Добавлена собственная поддержка cookie-файлов «samesite»

Теперь есть две возможности для куки, который использует флаг samesite: «Lax» и «Strict». Разница между Lax и Strict заключается в доступности cookie в запросах, исходящих из другого регистрируемого домена с использованием метода HTTP GET. Файлы cookie, использующие Lax, будут доступны в GET-запросе, исходящем из другого регистрируемого домена, тогда как файлы cookie, использующие Strict, не будут доступны в GET-запросе, отправленном из другого регистрируемого домена. Нет никакой разницы с методами POST: браузер не должен разрешать доступ к cookie в запросе POST, исходящем из другого регистрируемого домена.

Set-Cookie: key=value; path=/; domain=example.org; HttpOnly; SameSite=Lax|Strict

Мигрированный PCRE в PCRE2

Performance comparison

Я написал простой тест, чтобы легко сравнивать производительность разных версий PHP (используя Docker). Это даже позволит легко проверить производительность новых версий PHP, просто добавив новые имена контейнеров.

Работает на Macbook Pro, 2,5 ГГц Intel Core i7.

PHP version : 5.6.40
--------------------------------------
test_math                 : 1.101 sec.
test_stringmanipulation   : 1.144 sec.
test_loops                : 1.736 sec.
test_ifelse               : 1.122 sec.
Mem: 429.4609375 kb Peak mem: 687.65625 kb
--------------------------------------
Total time:               : 5.103

PHP version : 7.0.33
--------------------------------------
test_math                 : 0.344 sec.
test_stringmanipulation   : 0.516 sec.
test_loops                : 0.477 sec.
test_ifelse               : 0.373 sec.
Mem: 421.0859375 kb Peak mem: 422.2109375 kb
--------------------------------------
Total time:               : 1.71

PHP version : 7.1.28
--------------------------------------
test_math                 : 0.389 sec.
test_stringmanipulation   : 0.514 sec.
test_loops                : 0.501 sec.
test_ifelse               : 0.464 sec.
Mem: 420.9375 kb Peak mem: 421.3828125 kb
--------------------------------------
Total time:               : 1.868

PHP version : 7.2.17
--------------------------------------
test_math                 : 0.264 sec.
test_stringmanipulation   : 0.391 sec.
test_loops                : 0.182 sec.
test_ifelse               : 0.252 sec.
Mem: 456.578125 kb Peak mem: 457.0234375 kb
--------------------------------------
Total time:               : 1.089

PHP version : 7.3.4
--------------------------------------
test_math                 : 0.233 sec.
test_stringmanipulation   : 0.317 sec.
test_loops                : 0.171 sec.
test_ifelse               : 0.263 sec.
Mem: 459.953125 kb Peak mem: 460.3984375 kb
--------------------------------------
Total time:               : 0.984

PHP version : 7.4.0-dev
--------------------------------------
test_math                 : 0.212 sec.
test_stringmanipulation   : 0.358 sec.
test_loops                : 0.205 sec.
test_ifelse               : 0.228 sec.
Mem: 459.6640625 kb Peak mem: 460.109375 kb
--------------------------------------
Total time:               : 1.003

Тесты от PHP 5.6 и выше

Мне очень понравилась визуальная компиляция производительности с servebolt.com всех основных версий от 5.6 и выше. Смотрите результаты в таблицах ниже.

Визуальная производительность. PHP
Визуальная производительность. PHP
Визуальная производительность. PHP и база данных
Визуальная производительность. PHP и база данных

Заключение по производительности

PHP 7.0.0 стал важной вехой со значительно улучшенной производительностью и меньшим использованием памяти, но разработчикам PHP просто не хватает идей для ее улучшения. Одним из оставшихся пунктов является JIT (Just in time) компиляция. И это идет с PHP 8.0.

Development direction

В версиях PHP 7.x существует видимый путь к более типизированному (и немного более объективному) и современному языку программирования. Тем не менее, PHP любит перенимать полезные и полезные функции из других языков программирования.

PHP

About the Author

Ergashev Lazizbek

Добрый день, дорогие мои читатели, позвольте мне рассказать вам немного о себе. Я Лазизбек Эргашев, я веб-разработчик из Узбекистана. В основном я использую laravel/php для бэкэнда и vuejs/javascript для фронтэнда. Основная цель моего блога - поделиться с вами своим опытом и знаниями.

View All Articles