Verification: a143cc29221c9be0

Php 8 jit как включить

Php 8 jit как включить

Включение репозитория PHP

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

sudo apt install software-properties-common
sudo add-apt-repository ppa:ondrej/php

После включения PPA вы можете установить PHP 8.

Установка PHP 8.0 с Apache

Если вы используете Apache в качестве веб-сервера, вы можете запускать PHP как модуль Apache или PHP-FPM.

Установите PHP как модуль Apache

Установить PHP как модуль Apache – простая задача:

sudo apt update
sudo apt install php8.0 libapache2-mod-php8.0

После установки пакетов перезапустите Apache, чтобы модуль PHP загрузился:

sudo systemctl restart apache2

Настройте Apache с помощью PHP-FPM

Php-FPM – это менеджер процессов FastCGI для PHP. Выполните следующую команду, чтобы установить необходимые пакеты:

sudo apt update
sudo apt install php8.0-fpm libapache2-mod-fcgid

По умолчанию PHP-FPM не включен в Apache. Чтобы включить его, запустите:

sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.0-fpm

Чтобы активировать изменения, перезапустите Apache:

systemctl restart apache2

Установка PHP 8.0 с Nginx

Nginx не имеет встроенной поддержки обработки файлов PHP. Мы будем использовать PHP-FPM («менеджер процессов fastCGI») для обработки файлов PHP.

Выполните следующие команды для установки пакетов PHP и PHP FPM:

sudo apt update
sudo apt install php8.0-fpm

После завершения установки служба FPM запустится автоматически. Чтобы проверить статус службы, запустите

systemctl status php8.0-fpm
● php8.0-fpm.service - The PHP 8.0 FastCGI Process Manager
     Loaded: loaded (/lib/systemd/system/php8.0-fpm.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2020-12-03 16:10:47 UTC; 6s ago

Теперь вы можете отредактировать блок сервера Nginx и добавить следующие строки, чтобы Nginx мог обрабатывать файлы PHP:

server {

    # . . . other code

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.0-fpm.sock;
    }
}

Не забудьте перезапустить службу Nginx, чтобы новая конфигурация вступила в силу:

sudo systemctl restart nginx

Установка расширений PHP

Расширения PHP – это скомпилированные библиотеки, которые расширяют основные функции PHP. Расширения доступны в виде пакетов и могут быть легко установлены с помощью apt:

sudo apt install php8.0-[extname]

Например, чтобы установить расширения MySQL и GD, вы должны выполнить следующую команду:

sudo apt install php8.0-mysql php8.0-gd

После установки нового расширения PHP не забудьте перезапустить службу Apache или PHP FPM, в зависимости от ваших настроек.

Тестирование обработки PHP

Чтобы проверить, правильно ли настроен веб-сервер для обработки PHP, создайте новый файл с именем info.php внутри каталога /var/www/html со следующим кодом:

/var/www/html/info.php

Сохраните файл, откройте браузер, и визит: http://your_server_ip/info.php.

Вы увидите информацию о своей конфигурации PHP, подобную следующей:

Как установить PHP 8 в Ubuntu 20.04

PHP JIT

JIT в PHP появилась давно. На самом деле он находился в разработке в течение нескольких лет и почти был выпущен в PHP 7.4. Работа по созданию JIT в PHP была тем импульсом, который привел к серьезному переписыванию движка, что дало версии 7.0 значительный прирост производительности.

PHP JIT построен как расширение кеша опкодов. Это означает, что его можно включать и отключать либо при сборке самого PHP, либо во время выполнения через php.ini.

Честно говоря, настройка JIT - один из самых запутанных способов настройки расширения PHP, который я когда-либо видел. К счастью, есть несколько сокращений конфигурации, которые упрощают настройку. Тем не менее, будет очень полезным знать как настраивать JIT, так что приступим.

Кратко для системных администраторов

В PHP 8.0 JIT как бы включен по умолчанию, но и выключен. Требуется только минимальная конфигурация php.ini, подобная этой:

opcache.enable=1
opcache.enable_cli=1
opcache.jit_buffer_size=128M

Как настроить JIT в PHP 8

Прежде всего, JIT будет работать, только если включен opcache, хоть по умолчанию в PHP он и включен, вы все равно должны убедиться, что в вашем php.ini файле установлено значение opcache.enable = 1. Включение самого JIT осуществляется путем установки в php.ini параметра opcache.jit_buffer_size в ненулевое значение. Это контролирует, сколько места в памяти JIT может заполнить оптимизированным машинным кодом. Однако больше не всегда лучше, поскольку JIT также может тратить время на компиляцию кода, который на самом деле не выигрывает от компиляции.

Обратите внимание, что если вы запускаете PHP через командную строку, вы также можете передать эти параметры через флаг -d вместо того, чтобы добавлять их в php.ini:

php -d opcache.enable=1 -d opcache.jit_buffer_size=128M

Если эта директива отсутствует, то значение по умолчанию будет равно 0, и JIT не будет работать.

Так же, если вы тестируете JIT в сценарии CLI, вы должны использовать opcache.enable_cli для включения opcache:

php -d opcache.enable_cli=1 -d opcache.jit_buffer_size=128M

Разница между opcache.enable и opcache.enable_cli заключается в том, что первый вариант следует использовать тогда, когда вы, например, используете встроенный PHP-сервер. Если вы на самом деле запускаете сценарий CLI, вам понадобится opcache.enable_cli.

Прежде чем продолжить, давайте убедимся, что JIT действительно работает, создадим PHP-скрипт, доступный через браузер или CLI (в зависимости от того, где вы тестируете JIT), и посмотрим на вывод opcache_get_status(): 

var_dump(opcache_get_status()['jit']);

Результат будет примерно таким:

array:7 [
"enabled" => true
"on" => true
"kind" => 5
"opt_level" => 4
"opt_flags" => 6
"buffer_size" => 4080
"buffer_free" => 0
]

 Если enabled и on стоят в true, то все работает как надо!

Далее есть несколько способов настроить JIT (и здесь мы попадем в круговорот беспорядка конфигурации). Вы можете настроить, когда должен работать JIT, с какой степенью он должен пытаться оптимизировать и т.д. Все эти параметры настраиваются с помощью всего одной записи конфигурации(!): opcache.jit. Выглядит это так:

opcache.enable=1 
opcache.jit=1255

В RFC можно почитать что означает каждое из чисел. Имейте в виду: это не битовая маска, каждое число просто представляет собой различный вариант конфигурации. Итак, RFC дает нам следующие варианты:

O - Уровень оптимизации 

0 не JIT
1 минимальный JIT (вызов стандартных обработчиков ВМ)
2 выборочное встраивание обработчика ВМ
3 оптимизированный JIT на основе статического вывода типов отдельной функции
4 оптимизированный JIT на основе статического вывода типов и дерева вызовов
5 оптимизированная JIT на основе статического вывода типов и анализа внутренних процедур

T - триггер JIT

0 JIT все функции при первой загрузке скрипта
1 Функция JIT при первом выполнении
2 Профилировать по первому запросу и компилировать горячие функции по второму запросу
3 Профилировать на лету и компилировать горячие функции
4 Компиляция функций с тегом @jit в комментариях к документации (больше не используется)
5 Трассировка JIT

R - распределение регистров

0 не выполнять распределение регистров
1 использовать локальный распределитель регистров liner-scan
2 использовать глобальный распределитель регистров liner-scan

C - флаги оптимизации CPU

0 нет
1 включить генерацию инструкций AVX
Одно небольшое уточнение: Перечислять эти параметры нужно в обратном порядке, поэтому первая цифра представляет C значение, вторая - R, третья - T и четвертая - O. Почему просто не были добавлены четыре записи конфигурации, я не понимаю, вероятно, чтобы ускорить настройку JIT, может быть...

В любом случае, рекомендуется устанавливать значение 1255 в качестве наиболее оптимального, тогда будет выполняться максимальный "джитинг", используя JIT трассировки, глобальный распределитель регистров liner-scan - что бы это ни было, черт возьми, и позволяет генерировать инструкции AVX.

Итак, ваши ini-настройки (или флаги -d ) должны иметь следующие значения:

opcache.enable=1 
opcache.jit_buffer_size=128M
opcache.jit=1255

Имейте в виду, что необязательно настраивать opcache.jit. JIT будет использовать значение по умолчанию, если это свойство не указано. А какой по умолчанию, спросите вы? А вот какое opcache.jit=tracing.

Что еще за трассировка, спросите вы, это совсем не та странная структура, похожая на битовую маску, которую мы видели ранее. И вы будете правы: после того, как был принят исходный RFC, контрибьюторы ядра PHP осознали, что параметры-шифры, подобные битовой маске, не очень удобны для пользователя, поэтому они добавили два человечески понятные псевдонима, которые транслируются в ту самую битовую маску под капотом. Есть opcache.jit=tracing и opcache.jit=function.

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

Таким образом, единственно что от вас потребуется, чтобы включить JIT с его оптимальной конфигурацией - это установка opcache.jit_buffer_size, но если вы хотите установить конфигурацию явно, включение параметра opcache.jit будет не такой уж плохой идеей:

opcache.enable=1 
opcache.jit_buffer_size=128M
opcache.jit=tracing

Отладка JIT (opcache.jit_debug)

PHP JIT обеспечивает способ выдачи отладочной информации JIT путем установки конфигурации INI. Когда установлен определенный флаг, он выводит код сборки для дальнейшей проверки.

opcache.jit_debug=1

Директива opcache.jit_debug принимает значение битовой маски для включения определенных функций. В настоящее время он принимает значение в диапазоне от 1 (0b1) до 20 двоичных разрядов. Значение 1048576 представляет максимальный уровень вывода отладки. 

php -d opcache.jit_debug=1 test.php
TRACE-1$/test.php$7: ; (unknown)
sub $0x10, %rsp
mov $EG(jit_trace_num), %rax
mov $0x1, (%rax)
.L1:
cmp $0x4, 0x58(%r14)
jnz jit$$trace_exit_0
cmp $0x4e20, 0x50(%r14)
...

JIT поддерживает несколько других параметров конфигурации, чтобы настроить, сколько вызовов функций делает ее «горячей» функцией, которая затем компилируется JIT, и порог для определения того, какие функции следует "JIT'ировать", на основе процента общих вызовов функций.

Полный список см. В разделе Конфигурация Opcache.  

Новые особенности

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

Union Types

Учитывая динамически типизированную природу PHP, существует множество случаев, когда использование типов объединения может быть полезным. Union Types представляют собой совокупность двух или более типов, которые указывают, что можно использовать любой из них.

public function foo(Foo|Bar $input): int|float;

При этом void никогда не может быть частью Union Types, так как он означает «вообще никакого возвращаемого значения». Кроме того, с помощью |null или ?-нотации можно объявить nullable-объединения:

public function foo(Foo|null $foo): void;

public function bar(?Bar $bar): void;

JIT

Разработчики обещают улучшения в производительности PHP 8 в том числе благодаря стратегии компиляции JIT (just in time, "в нужный момент"). В JIT код сначала переводится в промежуточное представление, и только потом — в машинный код, зависящий от архитектуры. Это значит, что он в ходе исполнения будет превращаться в машинный код, который будет выполняться не виртуальной машиной Zend VM, на которой построен язык, а непосредственно на процессоре.

Nullsafe-оператор

Если вы работали с оператором объединения с null, вы уже знакомы с его недостатками: его нельзя применить в цепочке вызовов методов. Вместо этого вам нужны промежуточные проверки или придется полагаться на optional помощников, предоставляемых некоторыми платформами:

$startDate = $dateAsString = $booking->getStartDate();

$dateAsString = $startDate ? $startDate->asDateTimeString() : null;

С добавлением nullsafe-оператора разработчик может видеть поведение методов, подобное поведению при объединению с null.

$dateAsString = $booking->getStartDate()?->asDateTimeString();

Именованные аргументы

Именованные аргументы позволяют передавать значения в функцию, указав имя этого значения. Это позволит разработчику не учитывать порядок значений, а также пропустить какие-нибудь необязательные параметры.

function foo(string $a, string $b, ?string $c = null, ?string $d = null)
{ /* … */ }
foo(
    b: 'value b',
    a: 'value a',
    d: 'value d',
);

Атрибуты

Атрибуты — в других языках программирования известные как аннотации, предлагают новый способ добавить метаданные в классы, без необходимости парсить докблок-комментарии.

Вот пример того, как выглядят атрибуты:

use App\Attributes\ExampleAttribute;
@@ExampleAttribute
class Foo
{
    @@ExampleAttribute
    public const FOO = 'foo';

    @@ExampleAttribute
    public $x;

    @@ExampleAttribute
    public function foo(@@ExampleAttribute $bar) { }
}
@@Attribute
class ExampleAttribute
{
    public $value;

    public function __construct($value)
    {
        $this->value = $value;
    }
}

Выражение соответствия

Выражение match можно назвать старшим братом выражения switch — оно может возвращать значения, комбинировать условия, и при этом не требует break операторов. Кроме того, match не выполняет никакого приведения типов.

$result = match($input) {
    0 => "hello",
  '1', '2', '3' => "world",
};

Краткий синтаксис для объединения свойств класса и конструктора

В PHP 8 появится синтаксический сахар для создания объектов значений или объектов передачи данных. Вместо отдельного указания определения свойств класса и конструктора для них, PHP теперь может объединять их в один краткий синтаксис.

Вместо такого кода:

class Money 
{
    public Currency $currency;

    public int $amount;

    public function __construct(
        Currency $currency,
        int $amount,
    ) {
        $this->currency = $currency;
        $this->amount = $amount;
    }
}

Теперь можно будет кратко писать вот так:

class Money 
{
    public function __construct(
        public Currency $currency,
        public int $amount,
    ) {}
}

Новый тип возврата static

Несмотря на то, что в PHP уже были возвращаемые типы — self и parent, до сих пор static не был допустимым типом возврата для этого языка программирования. Учитывая динамически типизированный характер PHP, эта функция будет полезна для многих разработчиков.

class Foo
{
    public function test(): static
    {
        return new static();
    }
}

Новый тип mixed

С добавлением скалярных типов в PHP 7, обнуляемых — в PHP 7.1, и, наконец, — объединенных типов в 8.0, PHP-разработчики могут явно объявлять информацию о типе для большинства параметров функции, callable-функций, а также свойств класса. Тем не менее, PHP не всегда поддерживает типы, и, скорее всего, он иногда будет пропускать информацию о них. Это приводит к тому, что значение будет неоднозначным, когда отсутствует информация о типе.

Явный mixed тип позволит разработчикам добавлять типы в параметры, свойства класса и возвращаемые значения из функции, чтобы указать, что информация о типе не была забыта, а её просто нельзя указать более точно.

mixed сам по себе означает один из этих типов:

  • array
  • bool
  • callable
  • int
  • float
  • null
  • object
  • resource
  • string

Обратите внимание, что mixed также может использоваться как параметр или тип свойства, а не только как тип возвращаемого значения.

Также обратите внимание, что, поскольку mixed уже включает null, его нельзя делать nullable. Такой код вызовет ошибку:

// Fatal error: Mixed types cannot be nullable, null is already part of the mixed type.
function bar(): ?mixed {}

Зачем изучать PHP: рейтинг, перспективы, сферы применения

Throw-выражения

В PHP оператор throw не может создавать исключения там, где были разрешены только выражения, например стрелочные функции, оператор объединения и тернарный оператор. После обновления PHP будет преобразовывать инструкцию throw в выражение, это расширит функциональность использования языка.

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

$foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');

Наследование частными методами

Раньше PHP применял одинаковые проверки наследования для public, protected и private- методов. Другими словами: private методы должны были быть определены так же, как protected и public. При этом такое определение не имеет смысла, так как private методы не будут доступны дочерним классам.

Это обновление изменило поведение таких методов — теперь проверки наследования больше не выполняются для private методов. Кроме того, использование final private function и раньше не имело особого смысла, поэтому теперь оно вызовет предупреждение:

Warning: Private methods cannot be final as they are never overridden by other classes

Weak maps

Слабая карта — это набор объектов, на которые в коде слабо ссылаются ключи, что может привести к их удалению сборщиками мусора. В PHP 8 добавляется класс WeakMaps для создания сохранения ссылки на объект, при этом она не препятствует удалению самого объекта.

Например, возьмем пример ORM — они часто реализуют кэши, где содержатся ссылки на классы сущностей, чтобы улучшить производительность отношений между сущностями. Эти сущности не могут быть собраны сборщиком мусора, пока кэш имеет ссылку на них, даже если кэш является единственной реальной ссылкой на них. Если теперь кэш будет использовать слабые ссылки и WeakMaps, то PHP сможет справляться с мусором. Особенно в случае с ORM, которые могут управлять несколькими сотнями, если не тысячами объектов в ходе запроса.

Пример использования WeakMaps в PHP 8:

class Foo 
{
    private WeakMap $cache;

    public function getSomethingWithCaching(object $obj): object
    {
        return $this->cache[$obj]
           ??= $this->computeSomethingExpensive($obj);
    }
}

::class на объектах

Небольшая, но полезная новая фича: теперь можно использовать ::class на объектах, вместо того, чтобы использовать get_class(). Работает так же, как и get_class().

$foo = new Foo();

var_dump($foo::class);

Неименованные исключения

Всякий раз, когда вы хотели перехватить исключение до PHP 8, нужно было сохранить его в переменной независимо от того, использовали ли вы эту переменную или нет. Теперь можно перехватить исключения без указания переменной.

Если раньше программисту приходилось писать такой код:

try {
    // Something goes wrong
} catch (MySpecialException $exception) {
    Log::error("Something went wrong");
}

То в PHP 8 это будет выглядеть уже так:

try {
    // Something goes wrong
} catch (MySpecialException) {
    Log::error("Something went wrong");
}

Обратите внимание, что необходимо всегда указывать тип, поскольку PHP 8 не разрешает иметь пустой catch. Если вы хотите перехватить все исключения и ошибки, вы можете использовать их Throwable как тип исключения.

Завершающая запятая в списках параметров

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

public function(
    string $parameterA,
    int $parameterB,
    Foo $objectfoo,
) {
    // …
}

Обратите внимание на запятую после $objectfoo.

Создать DateTime объекты из интерфейса

Вы уже можете создать DateTime объект из DateTimeImmutable объекта. Однако наоборот в PHP было сделать очень сложно. Теперь в языке появился новый обобщенный способ конвертировать эти объекты друг в друга.

DateTime::createFromImmutable($immutableDateTime)DateTime::createFromInterface()DatetimeImmutable::createFromInterface()DateTimeDateTimeImmutable
DateTime::createFromInterface(DateTimeInterface $other);

DateTimeImmutable::createFromInterface(DateTimeInterface $other);

Новый Stringable интерфейс

Интерфейс Stringable можно использовать для аннотации типов или имплементации метода __toString (). Кроме того, всякий раз, когда класс, содержащий __toString (), будет автоматически имплементировать этот интерфейс "под капотом", и нет необходимости вручную реализовывать его.

class Foo
{
    public function __toString(): string
    {
        return 'foo';
    }
}

function bar(Stringable $stringable) { /* … */ }

bar(new Foo());
bar('abc');

Новая функция проверки str_contains()

Наконец-то на PHP больше не нужно полагаться на strpos(), чтобы узнать, содержит ли строка другую строку.

Вместо этого:

if (strpos('string with lots of words', 'words') !== false) { /* … */ }

Можно использовать вот это:

if (str_contains('string with lots of words', 'words')) { /* … */ }

Новые функции str_starts_with() и str_ends_with()

Ещё две давно назревшие функции. str_starts_with() проверяет, начинается ли строка с другой строки, и возвращает логическое значение ( true/ false).

str_ends_with() проверяет, заканчивается ли строка другой строкой, и возвращает логическое значение ( true/ false).

str_starts_with('haystack', 'hay'); // true
str_ends_with('haystack', 'stack'); // true

Новая функция деления на 0 fdiv()

Новая fdiv() функция делает процессы, похожие на функции fmod()и intdiv() функции — позволяет делить на 0. Вместо ошибки в результате вы получите INF-INF или NaN, в зависимости от случая.

Новая функция get_debug_type()

Функция get_debug_type() возвращает тип переменной. При этом get_debug_type() возвращает более полезный вывод для массивов, строк, анонимных классов и объектов, чем стандартный gettype().

Например, вызов gettype() для класса \Foo\Bar вернет object. Использование get_debug_type() вернет имя класса.

Новая функция get_resource_id()

Ресурсы — это специальные переменные в PHP, ссылающиеся на внешние ресурсы. Например, соединение MySQL или обработчик файла — каждому из них присваивается идентификатор, хотя ранее единственным способом узнать, что это идентификатор, было преобразование ресурса в int:

$resourceId = (int) $resource;

PHP 8 добавляет get_resource_id() функции, делая эту операцию более очевидной и безопасной для типов:

$resourceId = get_resource_id($resource);

Улучшение абстрактных методов трейтов

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

trait Test {
    abstract public function test(int $input): int;
}

class UsesTrait
{
    use Test;

    public function test($input)
    {
        return $input;
    }
}

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

class UsesTrait
{
    use Test;

    public function test(int $input): int
    {
        return $input;
    }
}

Объект реализации token_get_all()

token_get_all() в настоящее время возвращает токены либо в виде односимвольной строки, либо в виде массива с идентификатором токена, текстом токена и номером строки. Это обновление языка предлагает добавить альтернативу token_get_all (), которая вместо этого возвращает массив объектов. Это уменьшает использование памяти и делает код, работающий с токенами, более читабельным.

Изменения синтаксиса переменных

В этом обновлении изменились правила синтаксиса при работе с переменными в PHP. Например, при их разыменовывании.

Аннотация аргументов внутренней функции и типов возвращаемых значений

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

Запрет на выключение JSON

В настоящее время возможно скомпилировать PHP без расширения JSON с помощью ./configure --disable-json. Тем не менее, JSON чрезвычайно полезен, потому что он широко используется во многих случаях — в работе сайтов, выходных данных журналов, в качестве формата данных, который можно использовать для обмена данными со многими приложениями и языками программирования. Поэтому разработчики языка запретили выключать JSON.