Краткий обзор изменений в PHP v7.x менее чем за 15 минут

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

Осмотрев сокращенный список функций, реализованных 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" => "meaningful_value"];
var_dump($x["a"] ?? $x["b"] ?? $x["c"]); // string(16) "meaningful_value"

Добавлен новый оператор - космический корабль (‹=›)
Используется для оптимизации и упрощения операций сравнения.

// Before
f unction order_func($a, $b) {
    return ($a < $b) ? -1 : (($a > $b) ? 1 : 0);
}
// Using <=> operator
function order_func($a, $b) {
    return $a <=> $b;
}

Объявления скалярных типов
Это только первый шаг к достижению более сильных преимуществ типизированного языка программирования в 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();
    }
}

Заявления о групповом использовании

// Explicit use syntax:
 
use FooLibrary\Bar\Baz\ClassA;
use FooLibrary\Bar\Baz\ClassB;
use FooLibrary\Bar\Baz\ClassC;
use FooLibrary\Bar\Baz\ClassD as Fizbo;
// Grouped use syntax:
 
use FooLibrary\Bar\Baz\{ ClassA, ClassB, ClassC, ClassD as Fizbo };

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

yield from <expr>

Повышенная производительность

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

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

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

Разбрасываемый интерфейс
Классы исключений реструктурированы, чтобы иметь неинтуитивную схему именования, которая приведет к меньшей путанице, особенно для новых пользователей.

Errors и Exceptions сейчас реализуют 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

⚠ Осторожно! Вы можете реализовать только от Throwable до Error и Exception.

Синтаксис экранирования кодовой точки 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 из-за разрешения имени класса ::class.

Генератор возвращаемых выражений

Единый синтаксис переменных

Уровень поддержки функции dirname ()

PHP 7.1

Типы, допускающие значение NULL

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 -- prints hello
say(null); // ok -- does not print
say(); // error -- missing parameter
say(new stdclass); //error -- bad type

Возврат пустоты

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

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

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

function lacks_return(): void {
    // valid
}

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

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

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

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

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

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

Закрытие от вызываемого

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

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

$array = [1, 2, 3];
// Assigns to $a, $b and $c the values of their respective array elements in $array with keys numbered from zero
[$a, $b, $c] = $array;
 
// Assigns to $a, $b and $c the values of the array elements in $array with the keys "a", "b" and "c", respectively
["a" => $a, "b" => $b, "c" => $c] = $array;

Синтаксис квадратных скобок для list ()

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

Видимость констант класса

class Token {
	// Constants default to public
	const PUBLIC_CONST = 0;
 
        // Constants then also can have a defined visibility
        private const PRIVATE_CONST = 0;
        protected const PROTECTED_CONST = 0;
        public const PUBLIC_CONST_TWO = 0;
 
        //Constants can only have one visibility declaration list
        private const FOO = 1, BAR = 2;
}

Перехват нескольких типов исключений

try {
   // Some code...
} catch (ExceptionType1 | ExceptionType2 $e) {
   // Code to handle the exception
} catch (\Exception $e) {
   // ...
}

PHP 7.2

Расширение типа параметра

<?php
 
class ArrayClass {
  public function foo(array $foo) { /* ... */ }
}
 
 
// This RFC proposes allowing the type to be widened to be untyped aka any
// type can be passed as the parameter.
// Any type restrictions can be done via user code in the method body.
class EverythingClass extends ArrayClass {
  public function foo($foo) { /* ... */ }
}

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

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

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

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

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

$db = new PDO(...);
 
// works with statements without bound values
$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 // the result is false
json_last_error_msg() // The result is "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(); // like json_last_error_msg()
$e->getCode(); // like json_last_error()

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

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

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

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

Добавлена ​​поддержка файлов cookie« на одном сайте »
Теперь есть две возможности для файла cookie, использующего флаг одного и того же сайта: Слабый и Строгий. Разница между 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

Улучшения хеширования паролей Argon2
Существующие функции password_ * обеспечивают прямой совместимый упрощенный интерфейс для хеширования паролей. Этот RFC предлагает реализацию Argon2id в функциях password_ * для использования в качестве безопасной альтернативы первоначально предложенному Argon2i.

Разрешить конечную запятую в вызовах функций

$newArray = array_merge(
    $arrayOne,
    $arrayTwo,
    ['foo', 'bar'], // comma is allowed in function calls
);

list () Назначение ссылки

$array = [1, 2];
list($a, &$b) = $array;

Это эквивалентно следующему:

$array = [1, 2];
$a = $array[0];
$b =& $array[1];

Устаревшие константы без учета регистра

PHP 7.4 (в разработке)

Типизированные свойства

class User {
    public int $id;
    public string $name;
 
    public function __construct(int $id, string $name) {
        $this->id = $id;
        $this->name = $name;
    }
}

Интерфейс внешних функций
FFI - одна из функций, которые сделали Python и LuaJIT очень полезными для быстрого прототипирования. Он позволяет вызывать функции C и использовать типы данных C из чистого языка сценариев и, следовательно, более продуктивно разрабатывать системный код. Для PHP FFI открывает способ писать расширения PHP и привязки к библиотекам C на чистом PHP.

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

// The folloving lines are doing the same
$this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value';
// Instead of repeating variables with long names, the equal coalesce operator is used
$this->request->data['comments']['user_id'] ??= 'value';

Предварительная загрузка
PHP давно использует кеши опкодов (APC, Turck MMCache, Zend OpCache). Они достигают значительного повышения производительности за счет ПОЧТИ полного устранения накладных расходов на перекомпиляцию кода PHP. Предварительная загрузка будет контролироваться только одной новой директивой php.ini - opcache.preload. Используя эту директиву, мы укажем один файл PHP, который будет выполнять задачу предварительной загрузки. После загрузки этот файл полностью выполняется - и может предварительно загружать другие файлы, либо путем их включения, либо с помощью функции opcache_compile_file ().

Всегда доступное расширение хеша
Это сделает расширение хеша (`ext / hash`) всегда доступным, подобно` date`. Расширение хеширования предоставляет очень богатую утилиту со множеством алгоритмов хеширования, которая чрезвычайно полезна в современных приложениях, причем не только в коде пользовательского пространства, но и во многом внутреннем устройстве.

На пути к PHP 8.0

JIT.

Суммируя. Когда вы запускаете программу PHP, Zend Engine анализирует код в абстрактное синтаксическое дерево (AST) и преобразует его в коды операций. Коды операций - это исполнительные единицы для виртуальной машины Zend (Zend VM). Коды операций довольно низкоуровневые, поэтому их гораздо быстрее переводить в машинный код, чем исходный код PHP. В ядре PHP есть расширение OPcache для кэширования этих кодов операций.

«JIT» - это метод, который компилирует части кода во время выполнения, так что вместо него можно использовать скомпилированную версию.

Это одна из последних и самых больших стратегий оптимизации PHP, которая все еще остается на столе. Инженеры PHP с нетерпением ждут, чтобы увидеть, насколько этот новый подход может выжать из их приложений. Очень хочу увидеть это сам.

Ошибки согласованного типа для внутренних функций
Сделать так, чтобы API синтаксического анализа внутренних параметров всегда генерировали TypeError, если синтаксический анализ параметров не удался. Следует отметить, что это также включает ArgumentCountError (дочерний элемент TypeError) для случаев, когда было передано слишком мало / много аргументов.

Сравнение производительности

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

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

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

Направление развития

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

Вскоре мы можем увидеть еще несколько интересных функций, например:

С их помощью разработчики PHP присоединятся к группе приверженцев современного языка. Нет идеального языка, но PHP прокладывает путь в будущее.

TL;DR

Чтобы сократить еще больше, я выбрал самые важные изменения в моем произвольном мнении, учитывая последнюю версию PHP 7.3. Они здесь:

Ссылки

Https://wiki.php.net/rfc
https://www.cloudways.com/blog/php-5-6-vs-php-7-symfony-benchmarks/ женщина
«Https://servebolt.com/articles/wordpress-5-0-php-7-2-vs-php-7-3-performance-and-speed-benchmark/

Иллюстрация головы сделана командой Badoo для блога Badoo Tech
https://tech.badoo.com/ru/article/511/php-8-chego-zhdat-pismo/