php8
PHPFuck для PHP8
Немного про программирование.
Один из читателей написал, что программа PHPFuck (это способ писать программы
В самом деле, восьмая версия стала куда строже к программисту,
<?=[[$_=@([].[]),$___=-!![],$$_=![]+![],$$_=$$_*$$_,$__=$$_+![],$$__=$$_+$__,
$____=$_[-![]-![]],$$___="{$$__}"^$____,$$___=($$___^"$___"). ($$___^"{$$__}"^"$___"^"{$$_}").
($$$_=$$___^"{$$_}"). $$$_.($_____="{$$__}"^"{$$_}"^++$____).("$___"&$____).
([$$___=$_[+![]],"{$$_}"|++$$___][+![]]).$_____.$_[+![]].$$$_. ("$__"^$_[-![]-![]]^"$___")
],$$___][+![]];Выше новая версия.
Кстати, она компактнее, но при этом выводит больше текста — «hello world», вместо «Hello!» предыдущего варианта. Ниже я немного расскажу откуда взялась экономия и с чем ещё пришлось столкнуться.
Принцип формирования текста остался, в общих чертах, прежним — корнем всего является комбинация @([].[]), которая даёт слово ArrayArray
Из этой строки потом рождаются все остальные символы. Дело в том, что в ПХП над символами можно производить некоторые бинарные и математические операции, что иногда позволяет получить другие символы. Например, первая буква — h в слове hello получается из сочетания '9' ^ 'a' ^ '0' (где ^ — бинарная операция «исключающего или»).
При этом цифры я получаю довольно простым способом — 0 как +!![] (преобразованием false в число), «девятку», — набирая нужные цифры по единицам (из +![]), а a — как предпоследнюю букву из полученной ранее строки ArrayArray, то есть @([].[])[-2] (аналогично, -2 получается тут из двух замаскированных единиц со знаком «минус»).
По сравнению с предыдущим вариантом в новом коде сделаны следующие изменения.
В этой связи весь код теперь — большое выражение, все переменные в нём лежат внутри массива. Если давать им нормальные имена, то основной принцип выглядит
Кроме того, мне надоело подбирать буквы вручную, вместо этого я написал небольшую программу, которая делает это за меня. Я не стал придумывать алгоритм, который бы анализировал какие биты нужно изменить, а решил задачу полным перебором.
Ниже приведен листинг этой программы, вдруг
<?php
declare(strict_types=1);
// Доступный алфавит
$abc = str_split(count_chars("Array0123456789", 3));
// буквы, которые надо получить
$need = str_split(count_chars("hello world", 3));
function gen(array $chrs): Generator
{
// чем операции длиннее, тем ближе к концу
// это нужно, чтобы более короткие конструкции выигрывали
foreach ($chrs as $ch) {
yield $ch => [$ch, $ch];
}
// возврат: из чего делаем, [как выглядит, значение]
foreach ($chrs as $ch) {
yield $ch => ["~$ch", ~$ch];
}
// для символа инкремент работает, а декремент — нет (особенность ПХП)
foreach ($chrs as $ch) {
$value = $ch;
yield $ch => ["++$ch", (string)++$value];
}
}
function eval_op(string $op, string $a, string $b): string
{
return ([
'|' => fn($a, $b) => $a | $b,
'&' => fn($a, $b) => $a & $b,
'^' => fn($a, $b) => $a ^ $b,
][$op])($a, $b);
}
function generate(array $set): Generator
{
foreach (gen($set) as $src1 => [$p1, $ch1]) {
foreach (gen($set) as $src2 => [$p2, $ch2]) {
foreach (['|', '&', '^'] as $op) {
$result = eval_op($op, $ch1, $ch2);
if ($result !== $ch1 && $result !== $ch2) {
yield [
$src1,
$src2,
"$p1 $op $p2",
$result,
];
}
}
}
}
}
function out(string $str): string
{
return preg_replace_callback(
'/[\x00-\x1F\x80-]/s',
fn($m) => sprintf("\\x%02X", ord($m[0])),
$str
);
}
$set = $abc;
do {
foreach (generate($set) as [$src1, $src2, $str, $res]) {
$idx = array_search($res, $need, true);
if (!isset($rules[$res])) {
$rules[$res] = $str;
}
if ($idx !== false) {
if (!in_array($src1, $abc, true)) {
echo out("{$rules[$src1]} = '$src1'"), "\n";
}
if (!in_array($src2, $abc, true)) {
echo out("{$rules[$src2]} = '$src2'"), "\n";
}
echo out("$str = '$res'"), "\n\n";
unset($need[$idx]);
}
$set[] = $res;
}
$set = array_unique($set);
} while ($need);Это ни в коем случае не инструмент для оптимизации программ в такой технике, а набросок, позволяющий ускорить подбор букв.
Для более глубоких оптимизаций нужно научить эту программу дробить выражения, которые уже вычислялись, на куски и эффективно использовать их, а так же подбирать частоиспользуемым кускам имена переменных меньшей длины.
У меня дробления нет, я использую предыдущие выражения целиком, а имена переменным моя программа вообще не подбирает.
PHP 7.4 vs. PHP 8.0a
Резюме — в моём частном случае стало ощутимо быстрее, PHP 7.4 работал 63,9 секунд, 8.0a — 42,6.
Как многие знают, основной фишкой восьмой версии интерпретатора ПХП будет JIT. Обещают ускорение вычислительных задач, что немного не про веб, где в основном используется этот язык, но всё равно выглядит интересно.
В официальном репозитории альфа восьмой версии лежит уже давно, стало интересно сравнить её с «семёркой» — много ли даёт JIT приросту. Для подобных тестов я давно использую свой оптимизирующий транслятор «Брейнфака», с загруженной в него задачей вычисления числа Пи.
Восьмой ПХП собрался на моём «Макбуке» следующей последовательностью команд:
PATH=/usr/local/Cellar/bison/3.5.3/bin/:$PATH ./buildconf
./configure --prefix=/opt/php-bin/ --without-iconv --disable-xml \
--without-libxml --disable-simplexml --disable-xmlwriter --disable-dom --disable-xmlreader
make -j4
make installВерсию 7.4 ставил через «брю», сборочную конфигурацию оставил как есть, ничего не менял.
После небольшой коррекции php.ini в информации об интерпретаторе появились OPcache и JIT:
Вообще свой брейфаковский транслятор
Каждую версию интерпретатора я тестировал со включенным и выключенным OPcache десять раз, а результаты усреднил. Как видите, JIT существенно ускорил выполнение программы в моём случае (там огромное количество циклов и ветвлений) — 63,9 секунды против 42,6.
Что интересно — PHP 8.a без OPcache обогнал версию 7.4 со включенным opcache, это примечательный результат— значит сам «голый» интерпретатор тоже оптимизируют.
PHP8 и strpos
Мне очень нравится куда двигается язык ПХП в последние годы. Много вещей удалось исправить, и движение в правильную сторону не останавливается. На список изменений восьмой версии приятно смотреть, кроме пресловутой компиляции «на лету», есть очень много других приятных изменений, в том числе, в ключе поднятой темы, буду убраны некоторые архаичные и вредные штуки.
В процессе изучения списка изменений, иногда находятся грабли, на которые я, к счастью, никогда не вставал. Хорошо, что они со временем из языка исчезают. Например:
The needle argument of strpos(), strrpos(), stripos(), strripos(), strstr(), strchr(), strrchr(), and stristr() will now always be interpreted as a string. Previously
non-string needles were interpreted as an ASCII code point. An explicit call to chr() can be used to restore the previous behavior.
Я даже не подозревал, что в качестве параметра в функции поиска подстроки сейчас можно (в восьмой версии будет нельзя) передать код символа. Так как в ПХП мало кто следит за типом переменной, можно получить несколько обескураживающие результаты:
// объявили строковую переменную и где-то по коду она превратилась в число,
// 49 — это код символа «1»
$needle = (int) "49";
// выдаёт «Есть»
echo @strpos("12349", $needle) === false ? "Нету\n" : "Есть\n";
// Несколько неожиданно выдаёт «Нету», ищется символ с кодом «49» (единица)
echo @strpos("02349", $needle) === false ? "Нету\n" : "Есть\n";В ПХП8 число будет преобразовано в строку и искаться будет именно строка, то есть оба этих примера выдадут «Есть».
PHP8 ★
Пока всё прогрессивное человечество переходит на ПХП7, а менее прогрессивное до сих пор чахнет
В официальной вики есть страница, где можно ознакомиться с довольно пока скудным списком предложений на восьмую версию. Меня больше всего заинтересовали два изменения, серьёзно ломающие обратную совместимость:
Изменение оправдано унификацией синтаксиса — сейчас используемая сущность выводится из контекста, скажем статические свойства начинаются с доллара, а методы — нет, но если имя метода хранится в переменной, синтаксис разваливается — нет способа указать что же имел ввиду программист.
Или вот похожий пример с константой:
class Sample
{
const test = 'const';
public static $test = 'variable';
}
$test = 'test';
// что ты такое?
var_dump(Sample::$test);
// на самом деле вернётся «variable», очевидно; константу придётся получить так:
var_dump(constant("Sample::$test"));В действительности можно было бы выкрутиться скобками (сейчас так можно вызывать анонимную функцию, записанную в свойство класса), не знаю рассматривали ли такой вариант, но возможно он менее интуитивен.
// так можно было бы получать константу по имени в переменной (не работает!)
var_dump(Sample::($text));Самым серьёзным внутренним изменением нового релиза, вероятно будет добавление JIT, Дмитрий Стогов, человек, которому мы обязаны фантастическим ускорением в «семёрке», ещё в позапрошлом году написал в рассылку php.internals, что работа над динамической компиляцией начата. Получившийся к тому времени прототип показал 20% прироста на синтетических тестах и снижение производительности на реальных приложениях.
Кстати, это не первая попытка совместить динамическую компиляцию и ПХП, в языке HHVM, диалекте ПХП от «Фейсбука», такая компиляция даёт хороший прирост в скорости. Да и Дмитрий сотоварищи ещё
Более свежий тест того, что будет в ПХП8 показывает, что на трёх тестах (два стандартных зендовских теста и построение множества Мандельброта) динамическая компиляция уделывает версию 7.1.0 примерно вдвое. Это отличный результат! Но понятно, что в реальной жизни всё будет скромнее.
Когда же выйдет «восьмёрка»? Дата, увы, неизвестна, но есть предположение, что новую мажорную версию вряд ли можно ожидать раньше сентября 2021 года.