<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Блоги: заметки с тегом php</title>
<link>https://blogengine.ru/blogs/tags/php/</link>
<description>Автоматически собираемая лента заметок, написанных в блогах на Эгее</description>
<author></author>
<language>ru</language>
<generator>Aegea 11.0 (v4079e)</generator>

<itunes:subtitle>Автоматически собираемая лента заметок, написанных в блогах на Эгее</itunes:subtitle>
<itunes:image href="" />
<itunes:explicit>no</itunes:explicit>

<item>
<title>Митап по PHP</title>
<guid isPermaLink="false">135694</guid>
<link>https://bolknote.ru/all/mitap-po-php/</link>
<pubDate>Sun, 27 Apr 2025 20:23:58 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/mitap-po-php/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Признаюсь, я как-то подрастерял былой пыл в ведении моих записей, поэтому отлыниваю написать даже о самых ярких событиях, к которым я недавно имел отношение. Например, в прошлую субботу, 19 апреля наша компания устаивала &lt;a href="https://e-kazan.ru/news/show/51245"&gt;митап по языку программирования &lt;i&gt;PHP&lt;/i&gt;&lt;/a&gt;, а я был одним из её центральных организаторов.&lt;/p&gt;
&lt;p&gt;Если судить по отзывам, событие чрезвычайно всем понравилось. Многие сказали, что давно не участвовали в чём-либо настолько хорошо проведённом, хотя, на мой взгляд, огрехи тоже были. В конце-концов, это первый митап, который мы сделали своими силами.&lt;/p&gt;
&lt;p&gt;Не знал, что организация подобный событий отнимает &lt;i&gt;столько&lt;/i&gt; сил. Несмотря на список, который мы начали вести с самого начала, то одно, то другое постоянно выпадало из зоны внимания, настолько много всего пришлось учесть.&lt;/p&gt;
&lt;p&gt;Мы начали примерно за два месяца, тем не менее, кое-что пришлось делать буквально в последнюю неделю — например, только в последнюю неделю стало понятно сколько примерно народу придёт, потому что люди записывались и записывались. От количества зависело сколько арендовать стульев для сидения и столов для кофе-брейка, а так же количество еды.&lt;/p&gt;
&lt;p&gt;Я очень переживал за эти вещи, потому что при форс-мажоре мы бы просто не успели найти новых подрядчиков.&lt;/p&gt;
&lt;p&gt;Большое спасибо докладчиками — Александру Макарову, Максиму Пестову и Станиславу Кошевому за то, что вызвались поучаствовать и интересные доклады.&lt;/p&gt;
&lt;p&gt;Видео я показать не могу, верьте на слово, — мы его принципиально не записывали. Если мы сделаем наши митапы регулярными, не будем записывать и впредь — наша идея в том, чтобы люди больше общались, хотя бы вынуждено, приходя вот на такие события.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://bolknote.ru/pictures/2025.04.27.1@2x.webp" width="1000" height="562" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;Я (справа) объясняю что-то Максиму Пестову — одному из выступающих на нашем митапе&lt;/div&gt;
&lt;/div&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://bolknote.ru/pictures/2025.04.27.2@2x.webp" width="1000" height="562" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;Александр Макаров, один из лидеров разработки фреймворка &lt;i&gt;yii&lt;/i&gt; выступает на нашем митапе&lt;/div&gt;
&lt;/div&gt;
</description>
</item>

<item>
<title>Анонимные функции в PHP</title>
<guid isPermaLink="false">135589</guid>
<link>https://bolknote.ru/all/anonimnye-funkcii-v-php/</link>
<pubDate>Wed, 16 Apr 2025 22:34:46 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/anonimnye-funkcii-v-php/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Относительно недавно в ПХП появился новый способ создания анонимных функций. Раньше мы писали &lt;tt&gt;array_filter($strs, &amp;apos;strlen&amp;apos;)&lt;/tt&gt;, теперь можем писать &lt;tt&gt;array_filter($strs, strlen(...))&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;В первом случае у нас указывается строка, во втором — анонимная функция. Последнее намного лучше, конечно, потому что помогает строгой типизации и лучше понимается редакторами кода.&lt;/p&gt;
&lt;p&gt;Когда этот синтаксис появился, в моей голове он стал частным случаем стрелочного синтаксиса. В самом деле, ведь &lt;tt&gt;fn($t) =&gt; sin($t)&lt;/tt&gt; и &lt;tt&gt;sin(...)&lt;/tt&gt; — это одно и то же. Но недавно я понял, что у второго синтаксиса есть побочный эффект. По какой-то причине раньше мне это было не очевидно.&lt;/p&gt;
&lt;p&gt;Если посмотреть на код ниже, всё должно стать понятно, но если нет, то я поясню.&lt;/p&gt;
&lt;p&gt;Тут мы видим класс, конструктор которого печатает код, находящийся в строке, которая его вызвала. Ниже класса создаются две анонимные функции, одна — стрелочная через &lt;tt&gt;fn()&lt;/tt&gt;, вторая — через многоточие.&lt;/p&gt;
&lt;p&gt;Этот код позволяет проиллюстрировать разницу.&lt;/p&gt;
&lt;p&gt;В случае создания анонимной функции через &lt;tt&gt;fn()&lt;/tt&gt;, конструктор будет вызван каждый раз заново на любой вызов функции.&lt;/p&gt;
&lt;p&gt;В случае синтаксиса с многоточием, конструктор будет вызван сразу при создании, а при вызове,  в том числе многократном, он вызываться не будет.&lt;/p&gt;
&lt;p&gt;То есть каждый способ имеет особенности, которые надо учитывать.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="php"&gt;class Sample
{
    public function __construct()
    {
        $bt = debug_backtrace();
        [&amp;#039;line&amp;#039; =&amp;gt; $line, &amp;#039;file&amp;#039; =&amp;gt; $filename] = end($bt);

        echo file($filename)[$line - 1];
    }

    public function method(): void
    {
    }
}

$a = fn() =&amp;gt; (new Sample)-&amp;gt;method(); // не выведет ничего
$b = (new Sample)-&amp;gt;method(...); // выведется «$b = (new Sample)-&amp;gt;method(...);»

$a(); $a(); // выведется «$a(); $a();» два раза
$b(); $b(); // не выведет ничего&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Библиотека itertools для PHP</title>
<guid isPermaLink="false">129968</guid>
<link>https://bolknote.ru/all/biblioteka-itertools-dlya-php/</link>
<pubDate>Wed, 07 Aug 2024 23:32:03 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/biblioteka-itertools-dlya-php/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;В качестве развлечения написал тесты к своей старой &lt;a href="https://github.com/bolknote/itertools"&gt;библиотеке &lt;i&gt;itertools&lt;/i&gt; для ПХП&lt;/a&gt; и выяснил, что в ней полным-полно ошибок. В итоге, переписал всё на класс, исправил ошибки, добавил функции, которые появились в оригинальной библиотеке за это время, обмазал всё типами, вставил тесты и поддержку «композера».&lt;/p&gt;
&lt;p&gt;Пример использования теперь стал выглядеть вот так:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="php"&gt;use Itertools\Itertools as it;

foreach (it::islice(it::cycle(&amp;#039;ABC&amp;#039;), 10) as $element) {
    echo $element;
}&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Свой сервер OTP</title>
<guid isPermaLink="false">125842</guid>
<link>https://kini24.ru/all/svoy-server-otp/</link>
<pubDate>Sat, 10 Feb 2024 22:58:59 +0500</pubDate>
<author>Копытов Иван</author>
<comments>https://kini24.ru/all/svoy-server-otp/</comments>
<description>
&lt;p&gt;&lt;a href="https://kini24.ru/"&gt;Копытов Иван&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Достаточно давно появилось желание завести свой сервер одноразовых паролей. Искал, не особо торопливо, как обычно. Отчасти потому, что еще немногие серверы поддерживали такой механизм аутентификации. Да и данных, которые нужно защищать было немного. Позже появились сервисы типа госуслуг, в почте стало храниться больше «чувствительных» данных, стало необходимо ограничить доступ к некоторым функциям своего сервера... В общем, причин накопилось достаточно. Про сервер OTP (One Time Password) стал вспоминать всё чаще и вот на днях мне попалась один простой, но в тоже время достаточно функциональный сервер &lt;a href="https://github.com/Bubka/2FAuth"&gt;2FAuth&lt;/a&gt;.&lt;br /&gt;
Как пишет автор, он создал его потому, что:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;i&gt;Я хотел, чтобы мои учетные записи 2FA хранились в отдельной базе данных и я мог легко создавать и восстанавливать ее резервные копии.&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;Я ненавижу доставать свой смартфон, чтобы получить OTP, когда пользуюсь настольным компьютером.&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;Я люблю программировать и люблю самостоятельные решения.&lt;/i&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Какие есть «плюсы» данного решения:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;возможность регистрации новых пользователей. Кто-то отнесет ее к «минусам», я не вижу ничего плохого. Способа отключить не нашел.&lt;/li&gt;
&lt;li&gt;Восстановление пароля от своей учетной записи, используемой для входа. Не проверял.&lt;/li&gt;
&lt;li&gt;Возможность импорта и экспорта данных.&lt;/li&gt;
&lt;li&gt;Возможность загрузки QR-кода из файла.&lt;/li&gt;
&lt;li&gt;Возможность ручного ввода данных (сервис, учетная запись, секретный шифр).&lt;/li&gt;
&lt;li&gt;Поддержка TOTP, HOTP, Steam, WebAuthn.&lt;/li&gt;
&lt;li&gt;Загрузка логотипов. Мелочь, а приятно.&lt;/li&gt;
&lt;li&gt;Поддержка SQLite, MariaDB, MySQL, PostgreSQL и SQL Server (внезапно).&lt;/li&gt;
&lt;li&gt;Присутствует REST API (снова сюрприз).&lt;/li&gt;
&lt;li&gt;Есть темная тема.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Пройдемся по «минусам»:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Отсутствует приложение для мобильных телефонов, только веб-страница.&lt;/li&gt;
&lt;li&gt;Нет поддержки кодов, генерируемых Яндексом. То есть у вас не получится запихать в сервис OTP от Яндекса. Ну тут «на любителя».&lt;/li&gt;
&lt;li&gt;На мой непрофессиональный взгляд слишком много файлов находится в папке.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Теперь немного скриншотов:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;div class="fotorama" data-width="933" data-ratio="1.9276859504132"&gt;
&lt;img src="https://kini24.ru/pictures/0001@2x.png" width="933" height="484" alt="Вход в учетную запись" /&gt;
&lt;img src="https://kini24.ru/pictures/0002@2x.png" width="933" height="484" alt="Список паролей OTP (убрал данные из вредности)" /&gt;
&lt;img src="https://kini24.ru/pictures/0003@2x.png" width="933" height="484" alt="Создание новой записи в ручном режиме" /&gt;
&lt;img src="https://kini24.ru/pictures/0004@2x.png" width="933" height="624" alt="Редактирование записи" /&gt;
&lt;img src="https://kini24.ru/pictures/0005@2x.png" width="933" height="484" alt="Создание новой записи" /&gt;
&lt;img src="https://kini24.ru/pictures/0006@2x.png.jpg" width="1750" height="2560" alt="Настройки" /&gt;
&lt;img src="https://kini24.ru/pictures/0007@2x.png" width="933" height="855" alt="Настройки" /&gt;
&lt;img src="https://kini24.ru/pictures/0008@2x.png" width="933" height="484" alt="Настройки" /&gt;
&lt;img src="https://kini24.ru/pictures/0009@2x.png" width="933" height="484" alt="Настройки" /&gt;
&lt;/div&gt;
&lt;/div&gt;
</description>
</item>

<item>
<title>Простейший HTTP-прокси</title>
<guid isPermaLink="false">120943</guid>
<link>https://bolknote.ru/all/prosteyshiy-http-proksi/</link>
<pubDate>Sat, 24 Jun 2023 15:20:35 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/prosteyshiy-http-proksi/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;У Романа Парпалака &lt;a href="https://parpalak.com/blog/2023/06/22/http_proxy_in_php"&gt;прочитал&lt;/a&gt; про «простейший &lt;i&gt;HTTP&lt;/i&gt;-прокси». Ну нет, действительно простейший &lt;i&gt;HTTP&lt;/i&gt;-прокси как-то я изобрёл, когда надо было обновить сервер у одного из заказчиков, а там интернета не было — запустил этот прокси у себя и прокинул соединение к себе через &lt;i&gt;SSH&lt;/i&gt;-туннель.&lt;/p&gt;
&lt;p&gt;Выглядит он так:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="php"&gt;&amp;lt;?php readfile($_SERVER[&amp;quot;REQUEST_URI&amp;quot;]);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Запускается так:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="php"&gt;php -S 127.0.0.1:8080 proxy.php&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;А вот так работает:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://bolknote.ru/pictures/2023.06.24@2x.jpg" width="1000" height="221" alt="" /&gt;
&lt;/div&gt;
</description>
</item>

<item>
<title>Модуль scalar_objects</title>
<guid isPermaLink="false">120276</guid>
<link>https://bolknote.ru/all/modul-scalar-objects/</link>
<pubDate>Wed, 07 Jun 2023 21:00:39 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/modul-scalar-objects/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Очень мне нравится идея навести порядок в функциях ПХП при помощи присобачивания к примитивным типам что-то вроде методов.&lt;/p&gt;
&lt;p&gt;К именованию встроенных функций есть много претензий:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;различный стиль наименований: &lt;tt&gt;html_​entity_​decode&lt;/tt&gt;, но &lt;tt&gt;htmlentities&lt;/tt&gt;;&lt;/li&gt;
&lt;li&gt;неинтуитивность некоторых названий: &lt;tt&gt;strcspn&lt;/tt&gt;, &lt;tt&gt;strpbrk&lt;/tt&gt;;&lt;/li&gt;
&lt;li&gt;разный порядок параметров: &lt;tt&gt;strpos($haystack, $needle)&lt;/tt&gt;, &lt;tt&gt;array_search($needle, $haystack)&lt;/tt&gt;;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Считается, что это один из серьёзных недостатков ПХП. Напрашивается очевидное решение — сделать всем проблемным функциям алиасы, но так язык замусорится ещё сильнее.&lt;/p&gt;
&lt;p&gt;А вот добавив псевдометоды у примитивных типов, можно сразу сразу сделать хорошо. Один из контрибьютеров даже сделал &lt;a href="https://github.com/nikic/scalar_objects"&gt;модуль&lt;/a&gt;, который показывает как это могло бы выглядеть:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="php"&gt;declare(strict_types=1);

class IntHandler {
    public static function abs(int $self): int {
        return abs($self);
    }

    public static function div(int $self, int $other): int {
        return intdiv($self, $other);
    }

    public static function sqrt(int $self): float {
	return sqrt($self);
    }
}

register_primitive_type_handler(&amp;#039;int&amp;#039;, &amp;#039;IntHandler&amp;#039;);

$i = -5;
var_dump($i-&amp;gt;abs()-&amp;gt;div(2)-&amp;gt;sqrt()); // 1.4142135623730951&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Разумеется, если это решение поместить в язык, никаких классов регистрировать не надо будет, так что можно смотреть только на последнюю строку.&lt;/p&gt;
&lt;p&gt;Кроме того, повышается читаемость. Сравните два подхода&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="php"&gt;$result = substr(strtr(rtrim($className, &amp;#039;_&amp;#039;), &amp;#039;\\&amp;#039;, &amp;#039;_&amp;#039;), 15);
$result = $className-&amp;gt;trimRight(&amp;#039;_&amp;#039;)-&amp;gt;replace(&amp;#039;\\&amp;#039;, &amp;#039;_&amp;#039;)-&amp;gt;slice(15);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Как по мне, очень хорошее решение, совершенно непонятно почему оно ещё не в языке.&lt;/p&gt;
</description>
</item>

<item>
<title>Неблокирующий режим файлов в PHP</title>
<guid isPermaLink="false">128981</guid>
<link>https://bolknote.ru/all/neblokiruyuschiy-rezhim-faylov-v-php/</link>
<pubDate>Thu, 15 Dec 2022 18:24:51 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/neblokiruyuschiy-rezhim-faylov-v-php/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p style="background: rgb(222, 235, 217); padding: 10px; color:#000"&gt;Позже я внимательно прочитал документацию на теме. По всей видимости, это ничего не даст в отношении проверки системы хранения на зависание.&lt;/p&gt;
&lt;p&gt;Смотрел недавно исходники языка ПХП, хотел изучить подробнее как реализованы там стримы. Пока читал, случайно натолкнулся на недокументированную возможность — у функции &lt;tt&gt;fopen&lt;/tt&gt;, как оказалось, есть режим открытия в неблокирующем режиме (буква &lt;tt&gt;n&lt;/tt&gt;).&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="cpp"&gt;#if defined(O_NONBLOCK)
        if (strchr(mode, &amp;#039;n&amp;#039;)) {
                flags |= O_NONBLOCK;
        }
#endif&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Доступно не на всех системах, только там, где есть константа &lt;tt&gt;O_NONBLOCK&lt;/tt&gt; в &lt;tt&gt;fcntl.h&lt;/tt&gt;, то есть в Линуксах, например.&lt;/p&gt;
&lt;p&gt;Для меня это довольно полезное знание, так как я давно ищу возможность проверить из ПХП не зависла ли подмонтированная через &lt;i&gt;NFS&lt;/i&gt; система хранения. Из-за таких зависаний виснет драйвер в ядре и все системные вызовы, которые к нему обращаются. В конечном счёте виснет процесс, который пытается работать с такой файловой системой, да так, что его не получается убить через &lt;tt&gt;SIGKILL&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;В общем, надо будет попробовать при следующем таком инциденте — не спасает ли открытие в неблокирующем режиме. Код, кажется, должен выглядеть примерно так:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="php"&gt;function tryToOpen(string $path, float $timeout): ?bool
{
	$dio_fd = dio_open($path, O_RDONLY | O_NONBLOCK);
	if ($dio_fd === false) {
		return null;
	}

	foreach (glob(&amp;#039;/proc/self/fd/*&amp;#039;) as $file) {
	    if (is_link($file) &amp;amp;&amp;amp; readlink($file) === $path) {
		$fd = basename($file);
		$read = [fopen(&amp;quot;php://fd/$fd&amp;quot;, &amp;#039;rn&amp;#039;)];
		$write = $except = null;

		$timeout_us = (int) ($timeout * 1e6);
		return stream_select($read, $write, $except, 0, $timeout_us) === 1;
             }
	}
	return null;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Тут используется модуль ПХП &lt;i&gt;Direct IO&lt;/i&gt;, чтобы обратиться к системной функции &lt;tt&gt;open&lt;/tt&gt; мимо ПХП. Приходится так делать, потому что функция &lt;tt&gt;fopen&lt;/tt&gt;, которая есть в языке, сначала попытается проверить существование файла и в моей задаче сразу зависнет.&lt;/p&gt;
&lt;p&gt;После этого я, пользуясь &lt;tt&gt;/proc&lt;/tt&gt;, ищу среди своих файловых дескрипторов тот, который отвечает за только что открытый файл и переоткрываю его через &lt;tt&gt;fopen&lt;/tt&gt; (в этом месте делается дубликат файлового дескриптора, а прежний закрывается — вызывается &lt;tt&gt;dup&lt;/tt&gt;), так как мы тут работаем с номером файлового дескриптора, то проверка на существование файла не используется.&lt;/p&gt;
&lt;p&gt;Переоткрытие файла нужно, так как в вызов &lt;tt&gt;stream_select&lt;/tt&gt;, который я использую для ожидания данных из файла с таймаутом, можно передать результат &lt;tt&gt;fopen&lt;/tt&gt;, но не &lt;tt&gt;dio_open&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Надеюсь случай испытать этот код представится нескоро.&lt;/p&gt;
</description>
</item>

<item>
<title>Дескрипторы файлов и PHP</title>
<guid isPermaLink="false">124546</guid>
<link>https://bolknote.ru/all/deskriptory-faylov-i-php/</link>
<pubDate>Sun, 22 Dec 2019 22:34:49 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/deskriptory-faylov-i-php/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;На днях подошёл на работе один из программистов и спросил не знаю ли я способа как передать из ПХП во входной поток утилиты не один файл, а два. Когда мы разобрались, что он имеет ввиду, оказалось, что задача следующая: имеется утилита командной строки, которая делает &lt;i&gt;PDF&lt;/i&gt; из двух файлов — тела и заголовочной части. Нужно работать с ней из ПХП, но создавать промежуточные файлы не хочется.&lt;/p&gt;
&lt;p&gt;Один файл передаётся во входной поток, с этим многие знакомы и проблемы нет, но как передать два файла? В командной строке такой способ есть — создаём ещё один файловый дескриптор, связываем его с файлом или вводом/выводом какой-то команды и радуемся. Например:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;#!/bin/bash
# связываем третий дескриптор с чтением из указанного файла
exec 3&amp;lt; /etc/passwd
# связываем четвёртый со входом команды, которая будет писать в другой файл
exec 4&amp;gt; &amp;gt;(cat &amp;gt; /tmp/passwd)
# читаем из третьего дескриптора, пишем в четвёртый
cat &amp;lt;&amp;amp;3 &amp;gt;&amp;amp;4&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;А можно ли так в ПХП? Документация к &lt;a href="https://www.php.net/proc_open"&gt;&lt;i&gt;proc_open&lt;/i&gt;&lt;/a&gt; говорит, что да:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The file descriptor numbers are not limited to 0, 1 and 2 — you may specify any valid file descriptor number and it will be passed to the child process. This allows your script to interoperate with other scripts that run as «co-processes».&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ну что же, давайте попробуем:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;?php
// заполняем первые три файловых дескриптора как обычно и,
// кое-что новое, — создаём ещё один
$ds = [
    &amp;#039;stdin&amp;#039;  =&amp;gt; [&amp;#039;file&amp;#039;, &amp;#039;/dev/null&amp;#039;, &amp;#039;r&amp;#039;,],
    &amp;#039;stdout&amp;#039; =&amp;gt; [&amp;#039;pipe&amp;#039;, &amp;#039;w&amp;#039;,],
    &amp;#039;stderr&amp;#039; =&amp;gt; [&amp;#039;file&amp;#039;, &amp;#039;/dev/null&amp;#039;, &amp;#039;w&amp;#039;,],
    &amp;#039;stdnew&amp;#039; =&amp;gt; [&amp;#039;pipe&amp;#039;, &amp;#039;r&amp;#039;,],
];

// передаём наши дескрипторы команде 𝑐𝑎𝑡, специальный файл
// /𝑑𝑒𝑣/𝑓𝑑/3 связан с третьим (с нуля) дескриптором
$process = proc_open(&amp;quot;cat /dev/fd/3&amp;quot;, array_values($ds), $pipes);

// связываем дескрипторы с переменными, указанными в массиве $??
// (пожалуйста не используйте 𝑒𝑥𝑡𝑟𝑎𝑐𝑡 без 𝑝ℎ𝑝𝑑𝑜𝑐 в своём коде)
extract(array_combine(array_intersect_key(array_keys($ds), $pipes), $pipes));

// пишем в наш новый дескриптор
fwrite($stdnew, &amp;quot;Hello world!\n&amp;quot;);
fclose($stdnew);

// читаем из вывода переданное
fpassthru($stdout);
fclose($stdout);

proc_close($process);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Всё работает! Фраза, переданная на такой не совсем стандартный вход, благополучно выводится на экран.&lt;/p&gt;
&lt;p&gt;Должен сразу сказать — не все утилиты командной строки умеют работать таким образом, некоторые требуют указания настоящего файла, не знаю из каких соображений. Но с большинством это сделать получится.&lt;/p&gt;
</description>
</item>

<item>
<title>Дизассемблируй это</title>
<guid isPermaLink="false">136262</guid>
<link>https://bolknote.ru/all/4656/</link>
<pubDate>Sat, 27 Jan 2018 15:19:00 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/4656/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://bolknote.ru/pictures/2018.01.27.jpg" width="640" height="400" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;«ДУМ», скомпилированный с использованием одних только команд &lt;i&gt;MOV&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;В моей жизни последовательно сменяли друг друга три ассемблера. Первый, для компьютера «Радио-86РК», я выучил по комментариям к ассемблерному коду в журнале «Радио» — другой литературы не было, а до моего знакомства с интернетом оставалось несколько лет. Потом были последовательно ассемблеры для «Спектрума» и интеловских процессоров.&lt;/p&gt;
&lt;p&gt;В те времена я думал, что всю жизнь буду программировать на ассемблере, на деле же последнюю программу на нём написал в начале 2000-х. Сейчас на чистом ассемблере мало кто пишет, а тем временем в этом мире иногда происходят интересные вещи.&lt;/p&gt;
&lt;p&gt;В ассемблере есть такая команда — &lt;i&gt;MOV&lt;/i&gt; (в некоторых ассемблерах — &lt;i&gt;LD&lt;/i&gt;), записывает содержимое одного аргумента в другой.  Сейчас набор комманд разросся, аргументом может быть почти что угодно — регистр, ячейка в памяти, сумма некоторого числа, одного регистра и другого, умноженного на число, но по сути это всегда присваивание.&lt;/p&gt;
&lt;p&gt;И вот оказалось, что эта команда — полная по Тьюрингу. Звучит невероятно, но это так. Некие ребята заморочились и сделали компилятор, который компилирует любую программу на Си в последовательность команд &lt;i&gt;MOV&lt;/i&gt;. Причём им даже &lt;a href="https://github.com/xoreaxeaxeax/movfuscator/tree/master/validation/doom"&gt;ДУМ удалось скомпилировать&lt;/a&gt;, правда один кадр рисуется семь часов. Кстати, такая программа неуязвима для горюшка века — Мелтдауна и Спектра.&lt;/p&gt;
&lt;p&gt;Есть &lt;a href="https://github.com/xoreaxeaxeax/movfuscator/blob/master/slides/domas_2015_the_movfuscator.pdf"&gt;небольшая (на 156 страниц и 90% воды) презентация&lt;/a&gt;, достаточно популярно объясняющая как этого удалось достичь, но для её чтения надо знать ассемблер, поэтому я позволю себе раскрыть детали трансляции двух инструкций, чтобы пояснить принцип для тех, кто ассемблера не знает или ленится причитать.&lt;/p&gt;
&lt;p&gt;Например, сравнение двух чисел делается при помощи следующего псеводокода:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;mov [X], 0
mov [Y], 1
mov R, [X]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;У нас есть два числа в аргументах «&lt;i&gt;X&lt;/i&gt;» и «&lt;i&gt;Y&lt;/i&gt;», результат сравнения которых попадает в «&lt;i&gt;R&lt;/i&gt;» — там будет ноль, если числа не равны и единица в противном случае. Как же это работает?&lt;/p&gt;
&lt;p&gt;Первой командой ноль записывается в ячейку по адресу «&lt;i&gt;X&lt;/i&gt;». Это ассемблер, у нас тут всё — число, остальное — человеческие интерператации, поэтому записанное в «&lt;i&gt;X&lt;/i&gt;» мы используем как адрес. Второй командой единица записывается в ячейку по адресу «&lt;i&gt;Y&lt;/i&gt;». Третьей командой мы читаем значение по адресу «&lt;i&gt;X&lt;/i&gt;» и если значения в «&lt;i&gt;X&lt;/i&gt;» и «&lt;i&gt;Y&lt;/i&gt;» совпадают, то ноль перетрётся единицей (и она попадёт в &lt;i&gt;R&lt;/i&gt;), если нет, то в ячейке по адресу «&lt;i&gt;X&lt;/i&gt;» ноль останется (который попадёт в &lt;i&gt;R&lt;/i&gt;).&lt;/p&gt;
&lt;p&gt;Несложно. Но из кода выше непонятно как получаются другие необходимые инструкции. Увы, но какого-то единого принципа для всего нет, авторам для каждого набора приходилось придумывать что-то новое. Думаю интересно будет взглянуть на реализацию чего-нибудь ещё.&lt;/p&gt;
&lt;p&gt;Возьмём, например, логическое «ИЛИ» («OR»), тут чуточку сложнее:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;OR_ADDRS: dd OR_0, OR_1
OR_0: dd 0, 1
OR_1: dd 1, 1
; …
mov eax, X
mov edx, [OR_ADDRS + eax]
mov eax, Y
mov eax, [eax + edx]
mov R, eax&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Во всех логических операциях ребята используют записанные заранее неизменяемые массивы значений.&lt;/p&gt;
&lt;p&gt;Что тут происходит? В регистр (переменную, с которыми работает процессор) «&lt;i&gt;eax&lt;/i&gt;» записывается значение «&lt;i&gt;X&lt;/i&gt;» (возможные входные значения у нас тут — ноль или единица, численное представление булевых значений).&lt;/p&gt;
&lt;p&gt;Далее в регистр «&lt;i&gt;edx&lt;/i&gt;» записывается число из адреса, который является суммой адреса массива &lt;i&gt;OR_ADDRS&lt;/i&gt; и содержимого регистра &lt;i&gt;eax&lt;/i&gt;. Таким образом в &lt;i&gt;eax&lt;/i&gt; попадёт &lt;i&gt;OR_0&lt;/i&gt; или &lt;i&gt;OR_1&lt;/i&gt;, в зависимости от того былы записаны в &lt;i&gt;eax&lt;/i&gt; ноль или единица. Эти значения — тоже числа и являются адресами двух других массивов из двух элементов.&lt;/p&gt;
&lt;p&gt;Далее в &lt;i&gt;eax&lt;/i&gt; мы записываем аргумент &lt;i&gt;Y&lt;/i&gt;, его значение складывается с адресом полученным на предыдущем шаге и из получившегося адреса мы читаем записанное там значение. В переводе на ПХП получается следующее:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;function mov_or(int $X, int $Y): int
{
    define(&amp;#039;OR_0&amp;#039;, [0, 1]);
    define(&amp;#039;OR_1&amp;#039;, [1, 1]);

    define(&amp;#039;OR_ADDRS&amp;#039;, [OR_0, OR_1]);

    $R = OR_ADDRS[$X][$Y];

    return $R;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Кстати, интересно, что у знаменитого дисассемблера «&lt;a href="https://ru.wikipedia.org/wiki/IDA"&gt;ИДА&lt;/a&gt;» от полученной таким образом программы крепко уносит крышу — при попытке отладки диссасемблер не видит никаких ветвлений и падает на анализе кода. Получился бы неплохой метод защиты от анализа, если бы не производительность.&lt;/p&gt;
</description>
</item>

<item>
<title>Скорость PHP7 на интерпретаторе Brainfuck</title>
<guid isPermaLink="false">124865</guid>
<link>https://bolknote.ru/all/4261/</link>
<pubDate>Sat, 20 Dec 2014 22:19:00 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/4261/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img width="660" height="268.5" src="//bolknote.ru/imgs/2014.12.20.1.jpg" alt="Производительность PHP7 (161.98КиБ)" srcset="//bolknote.ru/imgs//2014.12.20.1@2x.jpg 2x"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;У меня есть старинная забава — брать свой &lt;a href="https://github.com/bolknote/brainfuck"&gt;оптимизирующий интерпретатор языка «Брейнфак»&lt;/a&gt; и запускать программу, вычисляющую число «Пи» под разными версиями ПХП, чтобы посмотреть их производительность. Тест довольно синтетический, но посмотреть всё равно интересно.&lt;/p&gt;
&lt;p&gt;Сравнил в данном случае версии 5.5.14  и сегодняшнюю сборку 7.0, разница всё равно заметна, хоть и не так впечатляюща, как в случае с «&lt;a href="https://wiki.php.net/phpng#performance_evaluation"&gt;Вордпрессом&lt;/a&gt;» — там разница в два раза.&lt;/p&gt;
&lt;p&gt;Время у меня на скриншоте общее — то есть трансляция программы на «Брейнфаке» в ПХП и запуск получившегося через &lt;i&gt;eval&lt;/i&gt;.&lt;/p&gt;
</description>
</item>

<item>
<title>Оптимизирующий транслятор Brainfuck → PHP</title>
<guid isPermaLink="false">124866</guid>
<link>https://bolknote.ru/all/3955/</link>
<pubDate>Sun, 28 Apr 2013 21:47:00 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/3955/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Грех было не запустить свою же программу &lt;a href="/2013/04/28/~3954"&gt;из предыдущего поста&lt;/a&gt; на собственном стареньком (2005 года) оптимизирующем трансляторе Брейнфака в ПХП. Оказалось, оттранслировал его неправильно.&lt;/p&gt;
&lt;p&gt;Нашёл баг (неверно транслировалась последовательность «[&amp;lt;]» в некоторых случаях), исправил, почистил, убрал наследие ПХП 4, ввёл небольшую новую оптимизацию и &lt;a href="https://github.com/bolknote/brainfuck"&gt;выхожил на ГитХаб&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;История этого интерпретатора тянется ещё с 2001-го года и сайта &lt;a href="http://web.archive.org/web/20100528182757/http://bf.kzn.ru/lang.htm"&gt;bf.kzn.ru&lt;/a&gt;, который ныне не существует. На нём у меня работал &lt;i&gt;jBF&lt;/i&gt;, транслятор Брейнфака на Джаваскрипт и это был первый в мире интерпретатор Брейнфака, который использовал &lt;a href="http://web.archive.org/web/20100605005331/http://bf.kzn.ru/readme.rus.txt"&gt;оптимизирующие техники&lt;/a&gt;. Вышло аж тринадцать версий интерпретатора и он довольно неплохо уделывал аналоги.&lt;/p&gt;
&lt;p&gt;В 2005 году я переделал его на ПХП и попробовал предложить на &lt;a href="http://pear.php.net"&gt;ПЕАР&lt;/a&gt; (к тому времени у меня там уже было несколько модулей), но там он не прошёл предворительный осмотр, сказали, что такой модуль не нужен.&lt;/p&gt;
&lt;p&gt;Так он у меня и валялся в разделе «Храню», пока я не запустил сегодня на нём свою программу. Должен, кстати, сказать, что интерпретатор держит марку, например, вот вычисление числа «Пи» на моём трансляторе и &lt;a href="https://github.com/rickbutton/bfi"&gt;интерпретаторе на Джаве&lt;/a&gt;:  &lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img width="480" height="215" src="//bolknote.ru/imgs/2013.04.28.png" alt="BFi vs PHP BF (15.14КиБ)"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Хотя, может быть, Джава просто медленно стартует.&lt;/p&gt;
&lt;p&gt;Заодно, кстати, на своей же программе &lt;a href="https://github.com/bolknote/highlight.js/commit/ab945645433d7872be9873434688b5c7edf18fb9"&gt;нашёл баг&lt;/a&gt; в расцветке Брейфака, которую я делал для библиотеки &lt;i&gt;highlight.js&lt;/i&gt;.&lt;/p&gt;
</description>
</item>

<item>
<title>Код для защиты GIF, PNG и JPEG (к предыдущей статье)</title>
<guid isPermaLink="false">127838</guid>
<link>https://bolknote.ru/all/3704/</link>
<pubDate>Sun, 05 Aug 2012 21:07:00 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/3704/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;В коментариях к предыдущей заметке о моём предложении по защите графических форматов от внедрённого кода один из читателей &lt;a href="/2012/08/02/~3703#n35668"&gt;попросил&lt;/a&gt; привести код, который показывал бы как моё предложение работает.&lt;/p&gt;
&lt;p&gt;Для форматов &lt;i&gt;GIF&lt;/i&gt;, &lt;i&gt;JPEG&lt;/i&gt; и &lt;i&gt;PNG&lt;/i&gt; я за полчаса написал следующее (язык — &lt;i&gt;PHP&lt;/i&gt; 5.4 и выше):&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;?
// //bolknote.ru август 2012
// Защищаем изображения, возращает false в случае неудачи
function ProtectPicture($inname, $outname)
{
    // типы изображения, которые мы можем защитить и функции защиты
    $enabled = [
        IMAGETYPE_JPEG =&amp;gt; &amp;#039;Jpeg&amp;#039;,
        IMAGETYPE_PNG  =&amp;gt; &amp;#039;Png&amp;#039;,
        IMAGETYPE_GIF  =&amp;gt; &amp;#039;Gif&amp;#039;,
    ];

    if (list(,, $type) = @getimagesize($inname)) {
        if (isset($enabled[$type])) {
            if ($fi = @fopen($inname, &amp;#039;rb&amp;#039;)) {
                flock($fi, LOCK_SH);

                if ($fo = @fopen($outname, &amp;#039;wb&amp;#039;)) {
                    flock($fo, LOCK_EX);
                    // если все файлы удалось открыть, то вызываем функцию защиты
                    $ret = call_user_func(&amp;#039;ProtectPicture&amp;#039;.$enabled[$type], $fi, $fo, filesize($inname));

                    fclose($fo);
                    fclose($fi);

                    if (!$ret) {
                        unlink($outname);
                    }

                    return $ret;
                } else {
                    fclose($fi);
                }
            }
        }
    }
    
    return false;
}

/* Private functions */

function ProtectPictureJpeg($fi, $fo, $controllen)
{
    // заголовок JPEG
    if (!@fwrite($fo, fread($fi, 2))) return false;

    $protect = &amp;#039;&amp;lt;?php __halt_compiler();&amp;#039;;
    $length  = pack(&amp;#039;n&amp;#039;, strlen($protect) + 2);

    // дописываю секцию COM (comment)
    if (!@fwrite($fo, &amp;quot;\xFF\xFE$length$protect&amp;quot;)) return false;

    // остаток файла
    if (@stream_copy_to_stream($fi, $fo) != $controllen - 2) return false;

    return true;
}

function ProtectPicturePng($fi, $fo, $controllen)
{
    // заголовок PNG
    if (!@fwrite($fo, fread($fi, 8))) return false;

    // пропускаем кусок IHDR (он должен быть первым))
    $len  = unpack(&amp;#039;N&amp;#039;, $lenbin = fread($fi, 4))[1] + 8;
    $ihdr = $lenbin . fread($fi, $len);

    // Смотрим, не написал ли в этот кусок хакер свой код
    if (($pos = strpos($ihdr, &amp;#039;&amp;lt;?&amp;#039;)) !== false) {
        // в стандарте перечислены поля, которые есть в этом куске — там должно быть 13 байт (+4 — длина)
        if ($len != 17) return false;

        // эта комбинация может встретиться только в первых восьми байтах заголовка — дальше поля не
        // могут принимать такие большие значения
        if ($pos &amp;gt; 11) return false;

        // если разрешены короткие теги, нужно присмотреться к этому коду
        // в столь ограниченном пространстве вред можно натворить только через запуск
        // чего-то внешнего
        if (ini_get(&amp;#039;short_open_tag&amp;#039;) &amp;amp;&amp;amp; strpos($ihdr, &amp;#039;`&amp;#039;, $pos + 2) !== false) {
            return false;
        }
    } 

    if (!@fwrite($fo, $ihdr)) return false;

    // кусок iTXt (text) с нашим кодом
    $protect = &amp;quot;&amp;lt;?php __halt_compiler();&amp;quot;;
    $length  = pack(&amp;#039;N&amp;#039;, strlen($protect));
    $sum     = pack(&amp;#039;N&amp;#039;, sprintf(&amp;#039;%u&amp;#039;, crc32($protect)));

    if (!@fwrite($fo, &amp;quot;{$length}iTXt{$protect}{$sum}&amp;quot;)) return false;

    // остаток файла
    if (@stream_copy_to_stream($fi, $fo) != $controllen - 8 - 4 - $len) return false;

    return true;
}

function ProtectPictureGif($fi, $fo, $controllen)
{
    $protect = &amp;quot;&amp;lt;?php __halt_compiler();&amp;quot;;

    // заголовок GIF
    if (!@fwrite($fo, fread($fi, 6))) return false;

    // поле Logical Screen Descriptor
    $lsd  = fread($fi, 7);
    $flag = ord($lsd[4]);

    // указана ли Global Color Table?
    if ($flag &amp;amp; 128) {
        $pixel = 1 + ($flag &amp;amp; 0b111);
        $colors = pow(2, $pixel);
        $gctlen = $colors * 3;

        // читаем Global Color Table
        $gct = fread($fi, $gctlen);

        // не вставил ли уже туда злоумышленник „&amp;lt;?“?
        // меняем у такой комбинации последний бит — это малозаметно,
        // безопасность важнее!
        if (strpos($gct, &amp;#039;&amp;lt;?&amp;#039;) !== false) {
            $gct = str_replace(&amp;#039;&amp;lt;&amp;#039;, &amp;#039;=&amp;#039;, $gct);
        }

        if (!@fwrite($fo, $lsd . $gct)) return false;
    } else {
        $gctlen = 0;

        if (!@fwrite($fo, $lsd)) return false;   
    }

    // пишем комментарий с куском защиты
    fwrite($fo, &amp;quot;!\xfe&amp;quot; . chr(strlen($protect)) . $protect . &amp;quot;\0&amp;quot;);

    if (@stream_copy_to_stream($fi, $fo) != $controllen - 6 - 7 - $gctlen) return false;

    return true;
}&lt;/code&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;p&gt;В коде есть два места где я ищу не встретилась ли комбинация &lt;tt&gt;&amp;lt;?&lt;/tt&gt; раньше, чем я вставляю защиту — это в заголовочном куске (&lt;i&gt;IHDR&lt;/i&gt;) в ПНГ и в глобальной таблице цветов у ГИФа.&lt;/p&gt;
&lt;p&gt;В случае ПНГ я анализирую заголовок, чтобы понять как там оказалась эта комбинация, она валидна только в первых восьми байтах (на зло остаётся всего 6 байт), кроме того, в этом случае я контролирую размер заголовка, в стандарте &lt;a href="http://www.w3.org/TR/PNG/#11IHDR"&gt;там указано&lt;/a&gt; 13 байт, хотя, кажется, размер не ограничивается. В общем, в шести байтах особо не разгуляешься, всё что я придумал туда засунуть — &lt;tt&gt;`rm *`&lt;/tt&gt;, т. е. очистка текущего каталога. На этот случай, я контролирую появление символа &lt;tt&gt;`&lt;/tt&gt;. На мой взгляд, вероятность встретить в нормальном ПНГ в заголовке &lt;tt&gt;&amp;lt;?&lt;/tt&gt; после которого идёт &lt;tt&gt;`&lt;/tt&gt; ничтожно мала.&lt;/p&gt;
&lt;p&gt;С ГИФом проще — меняю младший бит у цвета, это вряд ли будет заметно, а безопасность важнее.&lt;/p&gt;
</description>
</item>

<item>
<title>Размер имеет значение</title>
<guid isPermaLink="false">133174</guid>
<link>https://bolknote.ru/all/3650/</link>
<pubDate>Sun, 06 May 2012 10:01:00 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/3650/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Такая ещё вещь в ПХП вызывает мою искреннюю печаль. В руководстве &lt;a href="http://www.php.net/boolean"&gt;написано&lt;/a&gt;, что при приведении переменной к булевскому типу, строка «&amp;quot;0&amp;quot;» трактуется как &lt;i&gt;false&lt;/i&gt;:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;var_dump( (bool) &amp;quot;0&amp;quot;); // false
var_dump( (bool) &amp;quot;00&amp;quot;); // true&lt;/code&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;p&gt;Сделано это по определённым причинам. ПХП, исторически, был языком не просто для чайников, а для фанерных чайников, когда снаружи приходят параметры в &lt;i&gt;GET&lt;/i&gt; или &lt;i&gt;POST&lt;/i&gt; запросе, ПХП их раскладывает в специальные переменные, но все данные имеют тип «строка» (иногда — массив строк).&lt;/p&gt;
&lt;p&gt;Дальше, и тут есть своя логика, если снаружи приходит ноль, ПХП решает (типизации-то данных запросов нет), что программист в своей ХТМЛ-форме имел ввиду число ноль и ведёт себя соответствующе. Благодаря этому «if ($variable_from_get) {}» ведёт себя «правильнее».&lt;/p&gt;
&lt;p&gt;Поэтому я называю ПХП языком не с «динамическим приведением типов», а с «нестабильным приведением типов». Потому что его правила приведения содержат исключения.&lt;/p&gt;
&lt;p&gt;Такой «строковый ноль» может привести к сюрпризам:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;var_dump(array_filter( [ 0, null, false, &amp;quot;00&amp;quot;, &amp;quot;0&amp;quot; ])); // останется один элемент — &amp;quot;00&amp;quot;
var_dump(in_array(&amp;quot;0&amp;quot;, [ false ])); // true&lt;/code&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;p&gt;Или вот, например, поведение конструкции «&lt;i&gt;empty&lt;/i&gt;» с той же строкой (именно из документации по этой конструкции я узнал о существовании такого нуля, ещё в ПХП3):&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;$z = &amp;quot;0&amp;quot;; $zz = &amp;quot;00&amp;quot;;
var_dump(empty($z)); // true
var_dump(empty($zz)); // false&lt;/code&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;p&gt;При этом, один пробел и всё, ноль перестаёт быть «магическим»:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;var_dump(in_array(&amp;quot; 0&amp;quot;, [ false ])); // false&lt;/code&gt;&lt;/pre&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Картинка на чистом CSS-2</title>
<guid isPermaLink="false">120274</guid>
<link>https://bolknote.ru/all/3478/</link>
<pubDate>Sat, 12 Nov 2011 01:24:00 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/3478/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;div class="e2-text-table"&gt;
&lt;table cellpadding="0" cellspacing="0" border="0"&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img width="660" height="306" src="//bolknote.ru/imgs/2011.11.12.jpg" alt="Ещё картинка на чистом CSS (82.31КиБ)"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Я тут всё это время думал как дёшево улучшить свой &lt;a href="/2011/11/03/~3466"&gt;конвертор картинок в чистый CSS&lt;/a&gt; и придумал.&lt;/p&gt;
&lt;p&gt;Выкинул «radial-gradient» и стал использовать «linear-gradient». Картинка, которая в прошлой версии занимала 164КБ, сейчас занимает &lt;s&gt;89&lt;/s&gt; 79. Это уже неплохо, в сжатом (gzip) виде этот код занимает 19КБ, что всего в два с половиной раза больше, чем исходное изображение в PNG. Результат можно сильно улучшить, если отказаться от повтора цвета, но изображения с мелкими деталями (с текстом, например) пострадают.&lt;/p&gt;
&lt;p&gt;Кстати говоря, логотип студии Лебедева при конвертации из PNG (6,7КБ) уменьшился до &lt;s&gt;1,6КБ&lt;/s&gt; 1,3КБ. Это уже реальная экономия, если бы не префиксы браузеров.&lt;/p&gt;
&lt;p&gt;Отказ от радиального градиента в пользу линейного позволил мне кодировать целую строку изображения одной конструкцией, что, в свою очередь, позволило сильно экономить на повторяющихся байтах (кстати, можно попробовать группировать пиксели по вертикали и горизонтали, а потом выбирать лучший вариант).&lt;/p&gt;
&lt;p&gt;Кстати этот раз «Опера», увы, не смогла показать картинку, так как, похоже, не поддерживает позиционирование для линейных градиентов.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Добавлено позднее&lt;/b&gt;: сделал оптимизацию, теперь конвертор делает два прогона: один просматривает пиксели по горизонтали, второй по вертикали. Выбирается тот, который даёт наименьший результат.&lt;/p&gt;
&lt;p&gt;Ключи запуска те же.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="php"&gt;&amp;lt;?
// ffccbb → fcb
// ff0000 → red
// abcdef → abcdef
function Bo_shortcolor($color) {
    static $shortnames = array(
        &amp;quot;c0c0c0&amp;quot; =&amp;gt; &amp;quot;silver&amp;quot;,
        &amp;quot;808080&amp;quot; =&amp;gt; &amp;quot;gray&amp;quot;,
        &amp;quot;800000&amp;quot; =&amp;gt; &amp;quot;maroon&amp;quot;,
        &amp;quot;ff0000&amp;quot; =&amp;gt; &amp;quot;red&amp;quot;,
        &amp;quot;800080&amp;quot; =&amp;gt; &amp;quot;purple&amp;quot;,
        &amp;quot;008000&amp;quot; =&amp;gt; &amp;quot;green&amp;quot;,
        &amp;quot;808000&amp;quot; =&amp;gt; &amp;quot;olive&amp;quot;,
        &amp;quot;000080&amp;quot; =&amp;gt; &amp;quot;navy&amp;quot;,
        &amp;quot;008080&amp;quot; =&amp;gt; &amp;quot;teal&amp;quot;,
        &amp;quot;f0ffff&amp;quot; =&amp;gt; &amp;quot;azure&amp;quot;,
        &amp;quot;f5f5dc&amp;quot; =&amp;gt; &amp;quot;beige&amp;quot;,
        &amp;quot;ffe4c4&amp;quot; =&amp;gt; &amp;quot;bisque&amp;quot;,
        &amp;quot;a52a2a&amp;quot; =&amp;gt; &amp;quot;brown&amp;quot;,
        &amp;quot;ff7f50&amp;quot; =&amp;gt; &amp;quot;coral&amp;quot;,
        &amp;quot;ffd700&amp;quot; =&amp;gt; &amp;quot;gold&amp;quot;,
        &amp;quot;808080&amp;quot; =&amp;gt; &amp;quot;gray&amp;quot;,
        &amp;quot;008000&amp;quot; =&amp;gt; &amp;quot;green&amp;quot;,
        &amp;quot;808080&amp;quot; =&amp;gt; &amp;quot;grey&amp;quot;,
        &amp;quot;4b0082&amp;quot; =&amp;gt; &amp;quot;indigo&amp;quot;,
        &amp;quot;fffff0&amp;quot; =&amp;gt; &amp;quot;ivory&amp;quot;,
        &amp;quot;f0e68c&amp;quot; =&amp;gt; &amp;quot;khaki&amp;quot;,
        &amp;quot;faf0e6&amp;quot; =&amp;gt; &amp;quot;linen&amp;quot;,
        &amp;quot;800000&amp;quot; =&amp;gt; &amp;quot;maroon&amp;quot;,
        &amp;quot;000080&amp;quot; =&amp;gt; &amp;quot;navy&amp;quot;,
        &amp;quot;808000&amp;quot; =&amp;gt; &amp;quot;olive&amp;quot;,
        &amp;quot;ffa500&amp;quot; =&amp;gt; &amp;quot;orange&amp;quot;,
        &amp;quot;da70d6&amp;quot; =&amp;gt; &amp;quot;orchid&amp;quot;,
        &amp;quot;cd853f&amp;quot; =&amp;gt; &amp;quot;peru&amp;quot;,
        &amp;quot;ffc0cb&amp;quot; =&amp;gt; &amp;quot;pink&amp;quot;,
        &amp;quot;dda0dd&amp;quot; =&amp;gt; &amp;quot;plum&amp;quot;,
        &amp;quot;800080&amp;quot; =&amp;gt; &amp;quot;purple&amp;quot;,
        &amp;quot;ff0000&amp;quot; =&amp;gt; &amp;quot;red&amp;quot;,
        &amp;quot;fa8072&amp;quot; =&amp;gt; &amp;quot;salmon&amp;quot;,
        &amp;quot;a0522d&amp;quot; =&amp;gt; &amp;quot;sienna&amp;quot;,
        &amp;quot;c0c0c0&amp;quot; =&amp;gt; &amp;quot;silver&amp;quot;,
        &amp;quot;fffafa&amp;quot; =&amp;gt; &amp;quot;snow&amp;quot;,
        &amp;quot;d2b48c&amp;quot; =&amp;gt; &amp;quot;tan&amp;quot;,
        &amp;quot;008080&amp;quot; =&amp;gt; &amp;quot;teal&amp;quot;,
        &amp;quot;ff6347&amp;quot; =&amp;gt; &amp;quot;tomato&amp;quot;,
        &amp;quot;ee82ee&amp;quot; =&amp;gt; &amp;quot;violet&amp;quot;,
        &amp;quot;f5deb3&amp;quot; =&amp;gt; &amp;quot;wheat&amp;quot;,
    );

    $c = strtolower($color);
    if (isset($shortnames[$c])) {
        return $shortnames[$c];
    }

    if ($c[0] == $c[1] &amp;amp;&amp;amp; $c[2] == $c[3] &amp;amp;&amp;amp; $c[4] == $c[5]) {
        return &amp;#039;#&amp;#039; . $c[1] . $c[3] . $c[5];
    }

    return &amp;#039;#&amp;#039; . $c;
}

class Bo {
    public function __get($num) {
        return $num ? $num . &amp;#039;px&amp;#039; : 0;
    }
}

function Bo_convert_helper($im, $lim1, $lim2, $getpix, $dotmask, $z) {
    $end  = $lim1 - 1;
    $dots = array();

    for ($o1 = 0; $o1&amp;lt;$lim2; $o1++) {
        $coll = array();
        $prev    = null;
        $start   = 0;

        for ($o2 = 0; $o2&amp;lt;$lim1; $o2++) {
            $pixel = $getpix($im, $o2, $o1);

            if ($prev !== null &amp;amp;&amp;amp; $prev !== $pixel || $o2 == $end) {
                if ($prev !== null) {
                    $color = Bo_shortcolor(vsprintf(&amp;#039;%02x%02x%02x&amp;#039;, $prev));

                    $coll[] = &amp;quot;$color {$z-&amp;gt;$start},$color {$z-&amp;gt;$o2}&amp;quot;;
                }

                if ($o2 == $end &amp;amp;&amp;amp; $prev != $pixel) {
                    $color = Bo_shortcolor(vsprintf(&amp;#039;%02x%02x%02x&amp;#039;, $pixel));
                    $cell[] = &amp;quot;$color {$z-&amp;gt;$o2}&amp;quot;;
                }

                $start = $o2;
            }

            $prev = $pixel;
        }

        $dots[] = sprintf($dotmask, implode(&amp;#039;,&amp;#039;, $coll), $z-&amp;gt;$o1);
    }

    return $dots;
}


function Bo_convert($filename, $prefix = &amp;#039;&amp;#039;) {
    $z = new Bo();

    if (!file_exists($filename) &amp;amp;&amp;amp; !is_readable($filename)) {
        throw new Exception(&amp;#039;File not found.&amp;#039;);
    }

    $types = array(
        IMAGETYPE_JPG =&amp;gt; &amp;#039;jpeg&amp;#039;,
        IMAGETYPE_PNG =&amp;gt; &amp;#039;png&amp;#039;,
        IMAGETYPE_GIF =&amp;gt; &amp;#039;gif&amp;#039;,
    );

    $imagedata = @getimagesize($filename);
    if (!is_array($imagedata) || !isset($imagedata[2]) || !isset($types[$imagedata[2]])) {
        throw new Exception(&amp;#039;Unknown image format&amp;#039;);
    }

    $im = call_user_func(&amp;quot;imagecreatefrom&amp;quot; . $types[$imagedata[2]], $filename);
    $sw = $w = imagesx($im);
    $sh = $h = imagesy($im);

    $hfunc = create_function(&amp;#039;$im, $x, $y&amp;#039;, &amp;#039;return imagecolorsforindex($im, imagecolorat($im, $x, $y));&amp;#039;);
    $hmask = &amp;quot;{$prefix}linear-gradient(0,%s) 0 %s&amp;quot;;

    $hdots = join(&amp;#039;,&amp;#039;, Bo_convert_helper($im, $w, $h, $hfunc, $hmask, $z));

    $vfunc = create_function(&amp;#039;$im, $y, $x&amp;#039;, &amp;#039;return imagecolorsforindex($im, imagecolorat($im, $x, $y));&amp;#039;);
    $vmask = &amp;quot;{$prefix}linear-gradient(90,%s) %s 0&amp;quot;;

    $vdots = join(&amp;#039;,&amp;#039;, Bo_convert_helper($im, $h, $w, $vfunc, $vmask, $z));

    if (strlen($hdots) &amp;gt; strlen($vdots)) {
        $dots = $vdots;
        $sw = 1;
    } else {
        $dots = $hdots;
        $sh = 1;
    }

    return &amp;lt;&amp;lt;&amp;lt;HTML
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;$filename converted in background by Evgeny Stepanischev //bolknote.ru&amp;lt;/title&amp;gt;
&amp;lt;style text=&amp;quot;text/css&amp;quot;&amp;gt;
    div {
        background: $dots;
        background-repeat: no-repeat;
        {$prefix}background-size: {$sw}px {$sh}px;
        background-size: {$sw}px {$sh}px;
        width: {$w}px; height: {$h}px;
    }
&amp;lt;/style&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
HTML;
}

if ($_SERVER[&amp;#039;argc&amp;#039;] &amp;lt; 2) {
    echo &amp;lt;&amp;lt;&amp;lt;HELP
Image to background CSS convertor v2 by Evgeny Stepanischev. //bolknote.ru Nov 2011

Usage: {$_SERVER[&amp;#039;argv&amp;#039;][0]} filename [prefix]

filename  - image file name (PNG, JPEG or GIF)
prefix    - your browser name (Opera, IE, FF, Safari, Chrome)

HELP;
exit;
}

$browsers = array(
    &amp;#039;ie&amp;#039;     =&amp;gt; &amp;#039;-ms-&amp;#039;,
    &amp;#039;opera&amp;#039;  =&amp;gt; &amp;#039;-o-&amp;#039;,
    &amp;#039;safari&amp;#039; =&amp;gt; &amp;#039;-webkit-&amp;#039;,
    &amp;#039;chrome&amp;#039; =&amp;gt; &amp;#039;-webkit-&amp;#039;,
    &amp;#039;chromium&amp;#039; =&amp;gt; &amp;#039;-webkit-&amp;#039;,
    &amp;#039;ff&amp;#039; =&amp;gt; &amp;#039;-moz-&amp;#039;,
    &amp;#039;firefox&amp;#039; =&amp;gt; &amp;#039;-moz-&amp;#039;,
);

if (isset($_SERVER[&amp;#039;argv&amp;#039;][2])) {
    $type = strtolower($_SERVER[&amp;#039;argv&amp;#039;][2]);
    $prefix = isset($browsers[$type]) ? $browsers[$type] : &amp;#039;-&amp;#039; . $type . &amp;#039;-&amp;#039;;
} else {
    $prefix = &amp;#039;&amp;#039;;
}

try {
    echo Bo_convert($_SERVER[&amp;#039;argv&amp;#039;][1], $prefix);
} catch (Exception $e) {
    echo &amp;quot;Error: &amp;quot;, $e-&amp;gt;getMessage(), &amp;quot;\n&amp;quot;;
    exit(1);
};&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Аппаратное ускорение PHP</title>
<guid isPermaLink="false">137006</guid>
<link>https://bolknote.ru/all/2830/</link>
<pubDate>Wed, 01 Dec 2010 19:27:00 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/2830/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://web.archive.org/web/20101204113502/https://www.phpclasses.org/blog/post/137-Hardware-Accelerated-PHP-PHP-534-PHP-54-hold-off-modPageSpeed-and-AJAX-2--Lately-in-PHP-podcast-episode-7.html"&gt;Обсуждается возможность&lt;/a&gt; аппаратного ускорения PHP, точнее использования в коде возможностей GPU, в данный момент речь идёт о библиотеке CUDA фирмы NVIDIA.&lt;/p&gt;
&lt;p&gt;Первое что приходит в голову — это ускорение сортировок чисел и вычисление md5/sha1. Я слишком слабо разбираюсь в вопросе, чтобы понять с первого взгляда куда ещё можно приспособить графические ускорители.&lt;/p&gt;
</description>
</item>

<item>
<title>WinAPI и PHP</title>
<guid isPermaLink="false">119208</guid>
<link>https://bolknote.ru/all/114/</link>
<pubDate>Tue, 24 Aug 2004 21:00:00 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/114/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Так случилось, что в &lt;a href="http://www.php.net"&gt;PHP&lt;/a&gt; существует три модуля расширения — два для PHP4 и один для PHP5, позволяющих обращаться к Windows API. Способы их вызова различны, плюс ко всему, положение усугубляется тем, что модули расширения имеют одинаковые имена, документация есть только по первому из них, тогда как в комплект PHP включается второй.&lt;/p&gt;
&lt;p&gt;Ниже — результат моей потребности выяснить как же это всё работает. Прежде всего стоит заметить, что w32api не работает с mod_php, если старая версия вызывает ошибку при попытке повторного вызова в то же child, то новая вызывает ошибку сразу. В режиме CGI и с PHP5 я таких экспериментов не проводил.&lt;/p&gt;
&lt;p&gt;Итак, ниже — реализация функции link (hard link) для Windows NT. Под CLI пример работает как с PHP5, так и с PHP4. Конечно, для того, чтобы пример заработал, нужна файловая система, поддерживающая hard links. Под моей NTFS5 (Windows XP) ссылки ставятся.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="php"&gt;// Written by Evgeny Stepanischev (aka BOLK)
if (PHP_OS == &amp;#039;WINNT&amp;#039;) {
    // Модуль расширения FFI для PHP5
    if (extension_loaded(&amp;#039;ffi&amp;#039;) || intval(PHP_VERSION) &amp;gt;= 5 &amp;amp;&amp;amp; @dl(&amp;#039;php_ffi.dll&amp;#039;)) {
        function link($target, $link) {
            $api = &amp;amp; new ffi(&amp;quot;[lib=&amp;#039;kernel32.dll&amp;#039;] int &amp;quot; . &amp;quot;CreateHardLinkA( char *link, char *target, long *attr);&amp;quot;);
            return (bool)$api-&amp;gt;CreateHardLinkA($link, $target, NULL);
        }
    } else
    // Модуль расширения w32API для PHP4
    if (extension_loaded(&amp;#039;w32api&amp;#039;) || @dl(&amp;#039;php_w32api.dll&amp;#039;)) {
        // Старая версия модуля
        if (function_exists(&amp;#039;w32api_register_function&amp;#039;)) {
            if (!function_exists(&amp;#039;CreateHardLinkA&amp;#039;)) {
                @w32api_register_function(&amp;#039;kernel32.dll&amp;#039;, &amp;#039;CreateHardLinkA&amp;#039;, &amp;#039;bool&amp;#039;);
            }
            if (function_exists(&amp;#039;CreateHardLinkA&amp;#039;)) {
                function link($target, $link) {
                    return CreateHardLinkA($link, $target, NULL);
                }
            }
        } else {
            // Новая (примерно с версии PHP 4.3.0)
            function link($target, $link) {
                $api = &amp;amp; new win32;
                $api-&amp;gt;registerfunction(&amp;quot;bool CreateHardLinkA&amp;quot; . &amp;quot;(string &amp;amp;link, string &amp;amp;target) From kernel32.dll&amp;quot;);
                return $api-&amp;gt;CreateHardLinkA($link, $target);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Факт из жизни PHP</title>
<guid isPermaLink="false">132927</guid>
<link>https://bolknote.ru/all/925/</link>
<pubDate>Sun, 17 Aug 2003 14:11:02 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/925/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Когда-то, ещё во второй версии, функции в &lt;i&gt;PHP&lt;/i&gt; определялись иначе. Синтаксис сменился в версии 3. Для того, чтобы программисты могли безболезненно перейти на новую версию, в &lt;i&gt;PHP3&lt;/i&gt; было введено новое ключевое слово &lt;tt&gt;old_function&lt;/tt&gt; &amp;#151; для поддержки старого синтаксиса. Таким образом, переименованием &lt;tt&gt;function&lt;/tt&gt; в &lt;tt&gt;old_function&lt;/tt&gt; можно было портировать функции &lt;i&gt;PHP/FI2&lt;/i&gt; в &lt;i&gt;PHP3&lt;/i&gt;. Шли годы, а синтаксис остался. Правда, мало кто знает как он выглядел. Так вот, если вы хотите поразить знакомых &lt;i&gt;PHP&lt;/i&gt;шников, вставьте в свою программу функцию вида:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="php"&gt;old_function Test $a, $b (
     echo $a, $b;
);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Но помните, что этот синтаксис не рекомендован к использованию.&lt;/p&gt;
</description>
</item>

<item>
<title>Загадка про PHP. Номер 3.</title>
<guid isPermaLink="false">133175</guid>
<link>https://bolknote.ru/all/941/</link>
<pubDate>Sun, 17 Aug 2003 10:52:25 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/941/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Дано:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="php"&gt;$var = strval(TRUE-TRUE);
echo empty($var) ? &amp;#039;empty&amp;#039; : &amp;#039;full&amp;#039;;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Что выведет оператор &lt;tt&gt;echo&lt;/tt&gt; и почему я не подставил первую строку сразу в empty?&lt;/p&gt;
</description>
</item>


</channel>
</rss>