{
    "version": "https:\/\/jsonfeed.org\/version\/1.1",
    "title": "Блоги: заметки с тегом php",
    "_rss_description": "Автоматически собираемая лента заметок, написанных в блогах на Эгее",
    "_rss_language": "ru",
    "_itunes_email": "",
    "_itunes_categories_xml": "",
    "_itunes_image": false,
    "_itunes_explicit": "no",
    "home_page_url": "https:\/\/blogengine.ru\/blogs\/tags\/php\/",
    "feed_url": "https:\/\/blogengine.ru\/blogs\/tags\/php\/json\/",
    "icon": false,
    "authors": [
        {
            "name": "Илья Бирман",
            "url": "https:\/\/blogengine.ru\/blogs\/",
            "avatar": false
        }
    ],
    "items": [
        {
            "id": "135694",
            "url": "https:\/\/bolknote.ru\/all\/mitap-po-php\/",
            "title": "Митап по PHP",
            "content_html": "<p>Признаюсь, я как-то подрастерял былой пыл в ведении моих записей, поэтому отлыниваю написать даже о самых ярких событиях, к которым я недавно имел отношение. Например, в прошлую субботу, 19 апреля наша компания устаивала <a href=\"https:\/\/e-kazan.ru\/news\/show\/51245\">митап по языку программирования <i>PHP<\/i><\/a>, а я был одним из её центральных организаторов.<\/p>\n<p>Если судить по отзывам, событие чрезвычайно всем понравилось. Многие сказали, что давно не участвовали в чём-либо настолько хорошо проведённом, хотя, на мой взгляд, огрехи тоже были. В конце-концов, это первый митап, который мы сделали своими силами.<\/p>\n<p>Не знал, что организация подобный событий отнимает <i>столько<\/i> сил. Несмотря на список, который мы начали вести с самого начала, то одно, то другое постоянно выпадало из зоны внимания, настолько много всего пришлось учесть.<\/p>\n<p>Мы начали примерно за два месяца, тем не менее, кое-что пришлось делать буквально в последнюю неделю — например, только в последнюю неделю стало понятно сколько примерно народу придёт, потому что люди записывались и записывались. От количества зависело сколько арендовать стульев для сидения и столов для кофе-брейка, а так же количество еды.<\/p>\n<p>Я очень переживал за эти вещи, потому что при форс-мажоре мы бы просто не успели найти новых подрядчиков.<\/p>\n<p>Большое спасибо докладчиками — Александру Макарову, Максиму Пестову и Станиславу Кошевому за то, что вызвались поучаствовать и интересные доклады.<\/p>\n<p>Видео я показать не могу, верьте на слово, — мы его принципиально не записывали. Если мы сделаем наши митапы регулярными, не будем записывать и впредь — наша идея в том, чтобы люди больше общались, хотя бы вынуждено, приходя вот на такие события.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/bolknote.ru\/pictures\/2025.04.27.1@2x.webp\" width=\"1000\" height=\"562\" alt=\"\" \/>\n<div class=\"e2-text-caption\">Я (справа) объясняю что-то Максиму Пестову — одному из выступающих на нашем митапе<\/div>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/bolknote.ru\/pictures\/2025.04.27.2@2x.webp\" width=\"1000\" height=\"562\" alt=\"\" \/>\n<div class=\"e2-text-caption\">Александр Макаров, один из лидеров разработки фреймворка <i>yii<\/i> выступает на нашем митапе<\/div>\n<\/div>\n",
            "date_published": "2025-04-27T20:23:58+05:00",
            "date_modified": "2025-04-27T23:39:22+05:00",
            "tags": [
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sun, 27 Apr 2025 20:23:58 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "135694",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "135589",
            "url": "https:\/\/bolknote.ru\/all\/anonimnye-funkcii-v-php\/",
            "title": "Анонимные функции в PHP",
            "content_html": "<p>Относительно недавно в ПХП появился новый способ создания анонимных функций. Раньше мы писали <tt>array_filter($strs, &apos;strlen&apos;)<\/tt>, теперь можем писать <tt>array_filter($strs, strlen(...))<\/tt>.<\/p>\n<p>В первом случае у нас указывается строка, во втором — анонимная функция. Последнее намного лучше, конечно, потому что помогает строгой типизации и лучше понимается редакторами кода.<\/p>\n<p>Когда этот синтаксис появился, в моей голове он стал частным случаем стрелочного синтаксиса. В самом деле, ведь <tt>fn($t) => sin($t)<\/tt> и <tt>sin(...)<\/tt> — это одно и то же. Но недавно я понял, что у второго синтаксиса есть побочный эффект. По какой-то причине раньше мне это было не очевидно.<\/p>\n<p>Если посмотреть на код ниже, всё должно стать понятно, но если нет, то я поясню.<\/p>\n<p>Тут мы видим класс, конструктор которого печатает код, находящийся в строке, которая его вызвала. Ниже класса создаются две анонимные функции, одна — стрелочная через <tt>fn()<\/tt>, вторая — через многоточие.<\/p>\n<p>Этот код позволяет проиллюстрировать разницу.<\/p>\n<p>В случае создания анонимной функции через <tt>fn()<\/tt>, конструктор будет вызван каждый раз заново на любой вызов функции.<\/p>\n<p>В случае синтаксиса с многоточием, конструктор будет вызван сразу при создании, а при вызове,  в том числе многократном, он вызываться не будет.<\/p>\n<p>То есть каждый способ имеет особенности, которые надо учитывать.<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">class Sample\n{\n    public function __construct()\n    {\n        $bt = debug_backtrace();\n        [&#039;line&#039; =&gt; $line, &#039;file&#039; =&gt; $filename] = end($bt);\n\n        echo file($filename)[$line - 1];\n    }\n\n    public function method(): void\n    {\n    }\n}\n\n$a = fn() =&gt; (new Sample)-&gt;method(); \/\/ не выведет ничего\n$b = (new Sample)-&gt;method(...); \/\/ выведется «$b = (new Sample)-&gt;method(...);»\n\n$a(); $a(); \/\/ выведется «$a(); $a();» два раза\n$b(); $b(); \/\/ не выведет ничего<\/code><\/pre>",
            "date_published": "2025-04-16T22:34:46+05:00",
            "date_modified": "2025-04-17T08:31:58+05:00",
            "tags": [
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Wed, 16 Apr 2025 22:34:46 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "135589",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "129968",
            "url": "https:\/\/bolknote.ru\/all\/biblioteka-itertools-dlya-php\/",
            "title": "Библиотека itertools для PHP",
            "content_html": "<p>В качестве развлечения написал тесты к своей старой <a href=\"https:\/\/github.com\/bolknote\/itertools\">библиотеке <i>itertools<\/i> для ПХП<\/a> и выяснил, что в ней полным-полно ошибок. В итоге, переписал всё на класс, исправил ошибки, добавил функции, которые появились в оригинальной библиотеке за это время, обмазал всё типами, вставил тесты и поддержку «композера».<\/p>\n<p>Пример использования теперь стал выглядеть вот так:<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">use Itertools\\Itertools as it;\n\nforeach (it::islice(it::cycle(&#039;ABC&#039;), 10) as $element) {\n    echo $element;\n}<\/code><\/pre>",
            "date_published": "2024-08-07T23:32:03+05:00",
            "date_modified": "2024-08-07T23:31:59+05:00",
            "tags": [
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Wed, 07 Aug 2024 23:32:03 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "129968",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "125842",
            "url": "https:\/\/kini24.ru\/all\/svoy-server-otp\/",
            "title": "Свой сервер OTP",
            "content_html": "<p>Достаточно давно появилось желание завести свой сервер одноразовых паролей. Искал, не особо торопливо, как обычно. Отчасти потому, что еще немногие серверы поддерживали такой механизм аутентификации. Да и данных, которые нужно защищать было немного. Позже появились сервисы типа госуслуг, в почте стало храниться больше «чувствительных» данных, стало необходимо ограничить доступ к некоторым функциям своего сервера... В общем, причин накопилось достаточно. Про сервер OTP (One Time Password) стал вспоминать всё чаще и вот на днях мне попалась один простой, но в тоже время достаточно функциональный сервер <a href=\"https:\/\/github.com\/Bubka\/2FAuth\">2FAuth<\/a>.<br \/>\nКак пишет автор, он создал его потому, что:<\/p>\n<blockquote>\n<p><i>Я хотел, чтобы мои учетные записи 2FA хранились в отдельной базе данных и я мог легко создавать и восстанавливать ее резервные копии.<\/i><br \/>\n<i>Я ненавижу доставать свой смартфон, чтобы получить OTP, когда пользуюсь настольным компьютером.<\/i><br \/>\n<i>Я люблю программировать и люблю самостоятельные решения.<\/i><\/p>\n<\/blockquote>\n<p>Какие есть «плюсы» данного решения:<\/p>\n<ul>\n<li>возможность регистрации новых пользователей. Кто-то отнесет ее к «минусам», я не вижу ничего плохого. Способа отключить не нашел.<\/li>\n<li>Восстановление пароля от своей учетной записи, используемой для входа. Не проверял.<\/li>\n<li>Возможность импорта и экспорта данных.<\/li>\n<li>Возможность загрузки QR-кода из файла.<\/li>\n<li>Возможность ручного ввода данных (сервис, учетная запись, секретный шифр).<\/li>\n<li>Поддержка TOTP, HOTP, Steam, WebAuthn.<\/li>\n<li>Загрузка логотипов. Мелочь, а приятно.<\/li>\n<li>Поддержка SQLite, MariaDB, MySQL, PostgreSQL и SQL Server (внезапно).<\/li>\n<li>Присутствует REST API (снова сюрприз).<\/li>\n<li>Есть темная тема.<\/li>\n<\/ul>\n<p>Пройдемся по «минусам»:<\/p>\n<ul>\n<li>Отсутствует приложение для мобильных телефонов, только веб-страница.<\/li>\n<li>Нет поддержки кодов, генерируемых Яндексом. То есть у вас не получится запихать в сервис OTP от Яндекса. Ну тут «на любителя».<\/li>\n<li>На мой непрофессиональный взгляд слишком много файлов находится в папке.<\/li>\n<\/ul>\n<p>Теперь немного скриншотов:<\/p>\n<div class=\"e2-text-picture\">\n<div class=\"fotorama\" data-width=\"933\" data-ratio=\"1.9276859504132\">\n<img src=\"https:\/\/kini24.ru\/pictures\/0001@2x.png\" width=\"933\" height=\"484\" alt=\"Вход в учетную запись\" \/>\n<img src=\"https:\/\/kini24.ru\/pictures\/0002@2x.png\" width=\"933\" height=\"484\" alt=\"Список паролей OTP (убрал данные из вредности)\" \/>\n<img src=\"https:\/\/kini24.ru\/pictures\/0003@2x.png\" width=\"933\" height=\"484\" alt=\"Создание новой записи в ручном режиме\" \/>\n<img src=\"https:\/\/kini24.ru\/pictures\/0004@2x.png\" width=\"933\" height=\"624\" alt=\"Редактирование записи\" \/>\n<img src=\"https:\/\/kini24.ru\/pictures\/0005@2x.png\" width=\"933\" height=\"484\" alt=\"Создание новой записи\" \/>\n<img src=\"https:\/\/kini24.ru\/pictures\/0006@2x.png.jpg\" width=\"1750\" height=\"2560\" alt=\"Настройки\" \/>\n<img src=\"https:\/\/kini24.ru\/pictures\/0007@2x.png\" width=\"933\" height=\"855\" alt=\"Настройки\" \/>\n<img src=\"https:\/\/kini24.ru\/pictures\/0008@2x.png\" width=\"933\" height=\"484\" alt=\"Настройки\" \/>\n<img src=\"https:\/\/kini24.ru\/pictures\/0009@2x.png\" width=\"933\" height=\"484\" alt=\"Настройки\" \/>\n<\/div>\n<\/div>\n",
            "date_published": "2024-02-10T22:58:59+05:00",
            "date_modified": "2024-02-10T22:58:55+05:00",
            "tags": [
                "2fauth",
                "github",
                "hotp",
                "otp",
                "php",
                "Self-Hosted",
                "steam",
                "totp",
                "двуфакторная авторизация",
                "свой",
                "сервер"
            ],
            "author": {
                "name": "Копытов Иван",
                "url": "https:\/\/kini24.ru\/",
                "avatar": ""
            },
            "_date_published_rfc2822": "Sat, 10 Feb 2024 22:58:59 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "125842",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "120943",
            "url": "https:\/\/bolknote.ru\/all\/prosteyshiy-http-proksi\/",
            "title": "Простейший HTTP-прокси",
            "content_html": "<p>У Романа Парпалака <a href=\"https:\/\/parpalak.com\/blog\/2023\/06\/22\/http_proxy_in_php\">прочитал<\/a> про «простейший <i>HTTP<\/i>-прокси». Ну нет, действительно простейший <i>HTTP<\/i>-прокси как-то я изобрёл, когда надо было обновить сервер у одного из заказчиков, а там интернета не было — запустил этот прокси у себя и прокинул соединение к себе через <i>SSH<\/i>-туннель.<\/p>\n<p>Выглядит он так:<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">&lt;?php readfile($_SERVER[&quot;REQUEST_URI&quot;]);<\/code><\/pre><p>Запускается так:<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">php -S 127.0.0.1:8080 proxy.php<\/code><\/pre><p>А вот так работает:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/bolknote.ru\/pictures\/2023.06.24@2x.jpg\" width=\"1000\" height=\"221\" alt=\"\" \/>\n<\/div>\n",
            "date_published": "2023-06-24T15:20:35+05:00",
            "date_modified": "2023-06-24T15:20:13+05:00",
            "tags": [
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sat, 24 Jun 2023 15:20:35 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "120943",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "120276",
            "url": "https:\/\/bolknote.ru\/all\/modul-scalar-objects\/",
            "title": "Модуль scalar_objects",
            "content_html": "<p>Очень мне нравится идея навести порядок в функциях ПХП при помощи присобачивания к примитивным типам что-то вроде методов.<\/p>\n<p>К именованию встроенных функций есть много претензий:<\/p>\n<ul>\n<li>различный стиль наименований: <tt>html_​entity_​decode<\/tt>, но <tt>htmlentities<\/tt>;<\/li>\n<li>неинтуитивность некоторых названий: <tt>strcspn<\/tt>, <tt>strpbrk<\/tt>;<\/li>\n<li>разный порядок параметров: <tt>strpos($haystack, $needle)<\/tt>, <tt>array_search($needle, $haystack)<\/tt>;<\/li>\n<\/ul>\n<p>Считается, что это один из серьёзных недостатков ПХП. Напрашивается очевидное решение — сделать всем проблемным функциям алиасы, но так язык замусорится ещё сильнее.<\/p>\n<p>А вот добавив псевдометоды у примитивных типов, можно сразу сразу сделать хорошо. Один из контрибьютеров даже сделал <a href=\"https:\/\/github.com\/nikic\/scalar_objects\">модуль<\/a>, который показывает как это могло бы выглядеть:<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">declare(strict_types=1);\r\n\r\nclass IntHandler {\r\n    public static function abs(int $self): int {\r\n        return abs($self);\r\n    }\r\n\r\n    public static function div(int $self, int $other): int {\r\n        return intdiv($self, $other);\r\n    }\r\n\r\n    public static function sqrt(int $self): float {\r\n\treturn sqrt($self);\r\n    }\r\n}\r\n\r\nregister_primitive_type_handler(&#039;int&#039;, &#039;IntHandler&#039;);\r\n\r\n$i = -5;\r\nvar_dump($i-&gt;abs()-&gt;div(2)-&gt;sqrt()); \/\/ 1.4142135623730951<\/code><\/pre><p>Разумеется, если это решение поместить в язык, никаких классов регистрировать не надо будет, так что можно смотреть только на последнюю строку.<\/p>\n<p>Кроме того, повышается читаемость. Сравните два подхода<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">$result = substr(strtr(rtrim($className, &#039;_&#039;), &#039;\\\\&#039;, &#039;_&#039;), 15);\r\n$result = $className-&gt;trimRight(&#039;_&#039;)-&gt;replace(&#039;\\\\&#039;, &#039;_&#039;)-&gt;slice(15);<\/code><\/pre><p>Как по мне, очень хорошее решение, совершенно непонятно почему оно ещё не в языке.<\/p>\n",
            "date_published": "2023-06-07T21:00:39+05:00",
            "date_modified": "2023-06-07T21:00:24+05:00",
            "tags": [
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Wed, 07 Jun 2023 21:00:39 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "120276",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "128981",
            "url": "https:\/\/bolknote.ru\/all\/neblokiruyuschiy-rezhim-faylov-v-php\/",
            "title": "Неблокирующий режим файлов в PHP",
            "content_html": "<p style=\"background: rgb(222, 235, 217); padding: 10px; color:#000\">Позже я внимательно прочитал документацию на теме. По всей видимости, это ничего не даст в отношении проверки системы хранения на зависание.<\/p>\n<p>Смотрел недавно исходники языка ПХП, хотел изучить подробнее как реализованы там стримы. Пока читал, случайно натолкнулся на недокументированную возможность — у функции <tt>fopen<\/tt>, как оказалось, есть режим открытия в неблокирующем режиме (буква <tt>n<\/tt>).<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">#if defined(O_NONBLOCK)\n        if (strchr(mode, &#039;n&#039;)) {\n                flags |= O_NONBLOCK;\n        }\n#endif<\/code><\/pre><p>Доступно не на всех системах, только там, где есть константа <tt>O_NONBLOCK<\/tt> в <tt>fcntl.h<\/tt>, то есть в Линуксах, например.<\/p>\n<p>Для меня это довольно полезное знание, так как я давно ищу возможность проверить из ПХП не зависла ли подмонтированная через <i>NFS<\/i> система хранения. Из-за таких зависаний виснет драйвер в ядре и все системные вызовы, которые к нему обращаются. В конечном счёте виснет процесс, который пытается работать с такой файловой системой, да так, что его не получается убить через <tt>SIGKILL<\/tt>.<\/p>\n<p>В общем, надо будет попробовать при следующем таком инциденте — не спасает ли открытие в неблокирующем режиме. Код, кажется, должен выглядеть примерно так:<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">function tryToOpen(string $path, float $timeout): ?bool\n{\n\t$dio_fd = dio_open($path, O_RDONLY | O_NONBLOCK);\n\tif ($dio_fd === false) {\n\t\treturn null;\n\t}\n\n\tforeach (glob(&#039;\/proc\/self\/fd\/*&#039;) as $file) {\n\t    if (is_link($file) &amp;&amp; readlink($file) === $path) {\n\t\t$fd = basename($file);\n\t\t$read = [fopen(&quot;php:\/\/fd\/$fd&quot;, &#039;rn&#039;)];\n\t\t$write = $except = null;\n\n\t\t$timeout_us = (int) ($timeout * 1e6);\n\t\treturn stream_select($read, $write, $except, 0, $timeout_us) === 1;\n             }\n\t}\n\treturn null;\n}<\/code><\/pre><p>Тут используется модуль ПХП <i>Direct IO<\/i>, чтобы обратиться к системной функции <tt>open<\/tt> мимо ПХП. Приходится так делать, потому что функция <tt>fopen<\/tt>, которая есть в языке, сначала попытается проверить существование файла и в моей задаче сразу зависнет.<\/p>\n<p>После этого я, пользуясь <tt>\/proc<\/tt>, ищу среди своих файловых дескрипторов тот, который отвечает за только что открытый файл и переоткрываю его через <tt>fopen<\/tt> (в этом месте делается дубликат файлового дескриптора, а прежний закрывается — вызывается <tt>dup<\/tt>), так как мы тут работаем с номером файлового дескриптора, то проверка на существование файла не используется.<\/p>\n<p>Переоткрытие файла нужно, так как в вызов <tt>stream_select<\/tt>, который я использую для ожидания данных из файла с таймаутом, можно передать результат <tt>fopen<\/tt>, но не <tt>dio_open<\/tt>.<\/p>\n<p>Надеюсь случай испытать этот код представится нескоро.<\/p>\n",
            "date_published": "2022-12-15T18:24:51+05:00",
            "date_modified": "2024-06-26T00:01:28+05:00",
            "tags": [
                "nfs",
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Thu, 15 Dec 2022 18:24:51 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "128981",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "124546",
            "url": "https:\/\/bolknote.ru\/all\/deskriptory-faylov-i-php\/",
            "title": "Дескрипторы файлов и PHP",
            "content_html": "<p>На днях подошёл на работе один из программистов и спросил не знаю ли я способа как передать из ПХП во входной поток утилиты не один файл, а два. Когда мы разобрались, что он имеет ввиду, оказалось, что задача следующая: имеется утилита командной строки, которая делает <i>PDF<\/i> из двух файлов — тела и заголовочной части. Нужно работать с ней из ПХП, но создавать промежуточные файлы не хочется.<\/p>\n<p>Один файл передаётся во входной поток, с этим многие знакомы и проблемы нет, но как передать два файла? В командной строке такой способ есть — создаём ещё один файловый дескриптор, связываем его с файлом или вводом\/выводом какой-то команды и радуемся. Например:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">#!\/bin\/bash\r\n# связываем третий дескриптор с чтением из указанного файла\r\nexec 3&lt; \/etc\/passwd\r\n# связываем четвёртый со входом команды, которая будет писать в другой файл\r\nexec 4&gt; &gt;(cat &gt; \/tmp\/passwd)\r\n# читаем из третьего дескриптора, пишем в четвёртый\r\ncat &lt;&amp;3 &gt;&amp;4<\/code><\/pre><p>А можно ли так в ПХП? Документация к <a href=\"https:\/\/www.php.net\/proc_open\"><i>proc_open<\/i><\/a> говорит, что да:<\/p>\n<blockquote>\n<p>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».<\/p>\n<\/blockquote>\n<p>Ну что же, давайте попробуем:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">&lt;?php\r\n\/\/ заполняем первые три файловых дескриптора как обычно и,\r\n\/\/ кое-что новое, — создаём ещё один\r\n$ds = [\r\n    &#039;stdin&#039;  =&gt; [&#039;file&#039;, &#039;\/dev\/null&#039;, &#039;r&#039;,],\r\n    &#039;stdout&#039; =&gt; [&#039;pipe&#039;, &#039;w&#039;,],\r\n    &#039;stderr&#039; =&gt; [&#039;file&#039;, &#039;\/dev\/null&#039;, &#039;w&#039;,],\r\n    &#039;stdnew&#039; =&gt; [&#039;pipe&#039;, &#039;r&#039;,],\r\n];\r\n\r\n\/\/ передаём наши дескрипторы команде 𝑐𝑎𝑡, специальный файл\r\n\/\/ \/𝑑𝑒𝑣\/𝑓𝑑\/3 связан с третьим (с нуля) дескриптором\r\n$process = proc_open(&quot;cat \/dev\/fd\/3&quot;, array_values($ds), $pipes);\r\n\r\n\/\/ связываем дескрипторы с переменными, указанными в массиве $??\r\n\/\/ (пожалуйста не используйте 𝑒𝑥𝑡𝑟𝑎𝑐𝑡 без 𝑝ℎ𝑝𝑑𝑜𝑐 в своём коде)\r\nextract(array_combine(array_intersect_key(array_keys($ds), $pipes), $pipes));\r\n\r\n\/\/ пишем в наш новый дескриптор\r\nfwrite($stdnew, &quot;Hello world!\\n&quot;);\r\nfclose($stdnew);\r\n\r\n\/\/ читаем из вывода переданное\r\nfpassthru($stdout);\r\nfclose($stdout);\r\n\r\nproc_close($process);<\/code><\/pre><p>Всё работает! Фраза, переданная на такой не совсем стандартный вход, благополучно выводится на экран.<\/p>\n<p>Должен сразу сказать — не все утилиты командной строки умеют работать таким образом, некоторые требуют указания настоящего файла, не знаю из каких соображений. Но с большинством это сделать получится.<\/p>\n",
            "date_published": "2019-12-22T22:34:49+05:00",
            "date_modified": "2023-11-21T15:48:33+05:00",
            "tags": [
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sun, 22 Dec 2019 22:34:49 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "124546",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "136262",
            "url": "https:\/\/bolknote.ru\/all\/4656\/",
            "title": "Дизассемблируй это",
            "content_html": "<div class=\"e2-text-picture\">\n<img src=\"https:\/\/bolknote.ru\/pictures\/2018.01.27.jpg\" width=\"640\" height=\"400\" alt=\"\" \/>\n<div class=\"e2-text-caption\">«ДУМ», скомпилированный с использованием одних только команд <i>MOV<\/i><\/div>\n<\/div>\n<p>В моей жизни последовательно сменяли друг друга три ассемблера. Первый, для компьютера «Радио-86РК», я выучил по комментариям к ассемблерному коду в журнале «Радио» — другой литературы не было, а до моего знакомства с интернетом оставалось несколько лет. Потом были последовательно ассемблеры для «Спектрума» и интеловских процессоров.<\/p>\n<p>В те времена я думал, что всю жизнь буду программировать на ассемблере, на деле же последнюю программу на нём написал в начале 2000-х. Сейчас на чистом ассемблере мало кто пишет, а тем временем в этом мире иногда происходят интересные вещи.<\/p>\n<p>В ассемблере есть такая команда — <i>MOV<\/i> (в некоторых ассемблерах — <i>LD<\/i>), записывает содержимое одного аргумента в другой.  Сейчас набор комманд разросся, аргументом может быть почти что угодно — регистр, ячейка в памяти, сумма некоторого числа, одного регистра и другого, умноженного на число, но по сути это всегда присваивание.<\/p>\n<p>И вот оказалось, что эта команда — полная по Тьюрингу. Звучит невероятно, но это так. Некие ребята заморочились и сделали компилятор, который компилирует любую программу на Си в последовательность команд <i>MOV<\/i>. Причём им даже <a href=\"https:\/\/github.com\/xoreaxeaxeax\/movfuscator\/tree\/master\/validation\/doom\">ДУМ удалось скомпилировать<\/a>, правда один кадр рисуется семь часов. Кстати, такая программа неуязвима для горюшка века — Мелтдауна и Спектра.<\/p>\n<p>Есть <a href=\"https:\/\/github.com\/xoreaxeaxeax\/movfuscator\/blob\/master\/slides\/domas_2015_the_movfuscator.pdf\">небольшая (на 156 страниц и 90% воды) презентация<\/a>, достаточно популярно объясняющая как этого удалось достичь, но для её чтения надо знать ассемблер, поэтому я позволю себе раскрыть детали трансляции двух инструкций, чтобы пояснить принцип для тех, кто ассемблера не знает или ленится причитать.<\/p>\n<p>Например, сравнение двух чисел делается при помощи следующего псеводокода:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">mov [X], 0\nmov [Y], 1\nmov R, [X]<\/code><\/pre><p>У нас есть два числа в аргументах «<i>X<\/i>» и «<i>Y<\/i>», результат сравнения которых попадает в «<i>R<\/i>» — там будет ноль, если числа не равны и единица в противном случае. Как же это работает?<\/p>\n<p>Первой командой ноль записывается в ячейку по адресу «<i>X<\/i>». Это ассемблер, у нас тут всё — число, остальное — человеческие интерператации, поэтому записанное в «<i>X<\/i>» мы используем как адрес. Второй командой единица записывается в ячейку по адресу «<i>Y<\/i>». Третьей командой мы читаем значение по адресу «<i>X<\/i>» и если значения в «<i>X<\/i>» и «<i>Y<\/i>» совпадают, то ноль перетрётся единицей (и она попадёт в <i>R<\/i>), если нет, то в ячейке по адресу «<i>X<\/i>» ноль останется (который попадёт в <i>R<\/i>).<\/p>\n<p>Несложно. Но из кода выше непонятно как получаются другие необходимые инструкции. Увы, но какого-то единого принципа для всего нет, авторам для каждого набора приходилось придумывать что-то новое. Думаю интересно будет взглянуть на реализацию чего-нибудь ещё.<\/p>\n<p>Возьмём, например, логическое «ИЛИ» («OR»), тут чуточку сложнее:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">OR_ADDRS: dd OR_0, OR_1\nOR_0: dd 0, 1\nOR_1: dd 1, 1\n; …\nmov eax, X\nmov edx, [OR_ADDRS + eax]\nmov eax, Y\nmov eax, [eax + edx]\nmov R, eax<\/code><\/pre><p>Во всех логических операциях ребята используют записанные заранее неизменяемые массивы значений.<\/p>\n<p>Что тут происходит? В регистр (переменную, с которыми работает процессор) «<i>eax<\/i>» записывается значение «<i>X<\/i>» (возможные входные значения у нас тут — ноль или единица, численное представление булевых значений).<\/p>\n<p>Далее в регистр «<i>edx<\/i>» записывается число из адреса, который является суммой адреса массива <i>OR_ADDRS<\/i> и содержимого регистра <i>eax<\/i>. Таким образом в <i>eax<\/i> попадёт <i>OR_0<\/i> или <i>OR_1<\/i>, в зависимости от того былы записаны в <i>eax<\/i> ноль или единица. Эти значения — тоже числа и являются адресами двух других массивов из двух элементов.<\/p>\n<p>Далее в <i>eax<\/i> мы записываем аргумент <i>Y<\/i>, его значение складывается с адресом полученным на предыдущем шаге и из получившегося адреса мы читаем записанное там значение. В переводе на ПХП получается следующее:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">function mov_or(int $X, int $Y): int\n{\n    define(&#039;OR_0&#039;, [0, 1]);\n    define(&#039;OR_1&#039;, [1, 1]);\n\n    define(&#039;OR_ADDRS&#039;, [OR_0, OR_1]);\n\n    $R = OR_ADDRS[$X][$Y];\n\n    return $R;\n}<\/code><\/pre><p>Кстати, интересно, что у знаменитого дисассемблера «<a href=\"https:\/\/ru.wikipedia.org\/wiki\/IDA\">ИДА<\/a>» от полученной таким образом программы крепко уносит крышу — при попытке отладки диссасемблер не видит никаких ветвлений и падает на анализе кода. Получился бы неплохой метод защиты от анализа, если бы не производительность.<\/p>\n",
            "date_published": "2018-01-27T15:19:00+05:00",
            "date_modified": "2025-06-10T23:55:11+05:00",
            "tags": [
                "php",
                "ассемблер",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sat, 27 Jan 2018 15:19:00 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "136262",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "124865",
            "url": "https:\/\/bolknote.ru\/all\/4261\/",
            "title": "Скорость PHP7 на интерпретаторе Brainfuck",
            "content_html": "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" class=\"e2-text-table\">\n<tr>\n<td><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\"><\/td>\n<\/tr>\n<\/table>\n<p>У меня есть старинная забава — брать свой <a href=\"https:\/\/github.com\/bolknote\/brainfuck\">оптимизирующий интерпретатор языка «Брейнфак»<\/a> и запускать программу, вычисляющую число «Пи» под разными версиями ПХП, чтобы посмотреть их производительность. Тест довольно синтетический, но посмотреть всё равно интересно.<\/p>\n<p>Сравнил в данном случае версии 5.5.14  и сегодняшнюю сборку 7.0, разница всё равно заметна, хоть и не так впечатляюща, как в случае с «<a href=\"https:\/\/wiki.php.net\/phpng#performance_evaluation\">Вордпрессом<\/a>» — там разница в два раза.<\/p>\n<p>Время у меня на скриншоте общее — то есть трансляция программы на «Брейнфаке» в ПХП и запуск получившегося через <i>eval<\/i>.<\/p>\n",
            "date_published": "2014-12-20T22:19:00+05:00",
            "date_modified": "2023-12-12T13:29:31+05:00",
            "tags": [
                "bf",
                "brainfuck",
                "php",
                "php7",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sat, 20 Dec 2014 22:19:00 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "124865",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "124866",
            "url": "https:\/\/bolknote.ru\/all\/3955\/",
            "title": "Оптимизирующий транслятор Brainfuck → PHP",
            "content_html": "<p>Грех было не запустить свою же программу <a href=\"\/2013\/04\/28\/~3954\">из предыдущего поста<\/a> на собственном стареньком (2005 года) оптимизирующем трансляторе Брейнфака в ПХП. Оказалось, оттранслировал его неправильно.<\/p>\n<p>Нашёл баг (неверно транслировалась последовательность «[&lt;]» в некоторых случаях), исправил, почистил, убрал наследие ПХП 4, ввёл небольшую новую оптимизацию и <a href=\"https:\/\/github.com\/bolknote\/brainfuck\">выхожил на ГитХаб<\/a>.<\/p>\n<p>История этого интерпретатора тянется ещё с 2001-го года и сайта <a href=\"http:\/\/web.archive.org\/web\/20100528182757\/http:\/\/bf.kzn.ru\/lang.htm\">bf.kzn.ru<\/a>, который ныне не существует. На нём у меня работал <i>jBF<\/i>, транслятор Брейнфака на Джаваскрипт и это был первый в мире интерпретатор Брейнфака, который использовал <a href=\"http:\/\/web.archive.org\/web\/20100605005331\/http:\/\/bf.kzn.ru\/readme.rus.txt\">оптимизирующие техники<\/a>. Вышло аж тринадцать версий интерпретатора и он довольно неплохо уделывал аналоги.<\/p>\n<p>В 2005 году я переделал его на ПХП и попробовал предложить на <a href=\"http:\/\/pear.php.net\">ПЕАР<\/a> (к тому времени у меня там уже было несколько модулей), но там он не прошёл предворительный осмотр, сказали, что такой модуль не нужен.<\/p>\n<p>Так он у меня и валялся в разделе «Храню», пока я не запустил сегодня на нём свою программу. Должен, кстати, сказать, что интерпретатор держит марку, например, вот вычисление числа «Пи» на моём трансляторе и <a href=\"https:\/\/github.com\/rickbutton\/bfi\">интерпретаторе на Джаве<\/a>:  <\/p>\n<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" class=\"e2-text-table\">\n<tr>\n<td><img width=\"480\" height=\"215\" src=\"\/\/bolknote.ru\/imgs\/2013.04.28.png\" alt=\"BFi vs PHP BF (15.14КиБ)\"><\/td>\n<\/tr>\n<\/table>\n<p>Хотя, может быть, Джава просто медленно стартует.<\/p>\n<p>Заодно, кстати, на своей же программе <a href=\"https:\/\/github.com\/bolknote\/highlight.js\/commit\/ab945645433d7872be9873434688b5c7edf18fb9\">нашёл баг<\/a> в расцветке Брейфака, которую я делал для библиотеки <i>highlight.js<\/i>.<\/p>\n",
            "date_published": "2013-04-28T21:47:00+05:00",
            "date_modified": "2023-12-12T13:29:36+05:00",
            "tags": [
                "bf",
                "brainfuck",
                "php"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sun, 28 Apr 2013 21:47:00 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "124866",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "127838",
            "url": "https:\/\/bolknote.ru\/all\/3704\/",
            "title": "Код для защиты GIF, PNG и JPEG (к предыдущей статье)",
            "content_html": "<p>В коментариях к предыдущей заметке о моём предложении по защите графических форматов от внедрённого кода один из читателей <a href=\"\/2012\/08\/02\/~3703#n35668\">попросил<\/a> привести код, который показывал бы как моё предложение работает.<\/p>\n<p>Для форматов <i>GIF<\/i>, <i>JPEG<\/i> и <i>PNG<\/i> я за полчаса написал следующее (язык — <i>PHP<\/i> 5.4 и выше):<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">&lt;?\n\/\/ \/\/bolknote.ru август 2012\n\/\/ Защищаем изображения, возращает false в случае неудачи\nfunction ProtectPicture($inname, $outname)\n{\n    \/\/ типы изображения, которые мы можем защитить и функции защиты\n    $enabled = [\n        IMAGETYPE_JPEG =&gt; &#039;Jpeg&#039;,\n        IMAGETYPE_PNG  =&gt; &#039;Png&#039;,\n        IMAGETYPE_GIF  =&gt; &#039;Gif&#039;,\n    ];\n\n    if (list(,, $type) = @getimagesize($inname)) {\n        if (isset($enabled[$type])) {\n            if ($fi = @fopen($inname, &#039;rb&#039;)) {\n                flock($fi, LOCK_SH);\n\n                if ($fo = @fopen($outname, &#039;wb&#039;)) {\n                    flock($fo, LOCK_EX);\n                    \/\/ если все файлы удалось открыть, то вызываем функцию защиты\n                    $ret = call_user_func(&#039;ProtectPicture&#039;.$enabled[$type], $fi, $fo, filesize($inname));\n\n                    fclose($fo);\n                    fclose($fi);\n\n                    if (!$ret) {\n                        unlink($outname);\n                    }\n\n                    return $ret;\n                } else {\n                    fclose($fi);\n                }\n            }\n        }\n    }\n    \n    return false;\n}\n\n\/* Private functions *\/\n\nfunction ProtectPictureJpeg($fi, $fo, $controllen)\n{\n    \/\/ заголовок JPEG\n    if (!@fwrite($fo, fread($fi, 2))) return false;\n\n    $protect = &#039;&lt;?php __halt_compiler();&#039;;\n    $length  = pack(&#039;n&#039;, strlen($protect) + 2);\n\n    \/\/ дописываю секцию COM (comment)\n    if (!@fwrite($fo, &quot;\\xFF\\xFE$length$protect&quot;)) return false;\n\n    \/\/ остаток файла\n    if (@stream_copy_to_stream($fi, $fo) != $controllen - 2) return false;\n\n    return true;\n}\n\nfunction ProtectPicturePng($fi, $fo, $controllen)\n{\n    \/\/ заголовок PNG\n    if (!@fwrite($fo, fread($fi, 8))) return false;\n\n    \/\/ пропускаем кусок IHDR (он должен быть первым))\n    $len  = unpack(&#039;N&#039;, $lenbin = fread($fi, 4))[1] + 8;\n    $ihdr = $lenbin . fread($fi, $len);\n\n    \/\/ Смотрим, не написал ли в этот кусок хакер свой код\n    if (($pos = strpos($ihdr, &#039;&lt;?&#039;)) !== false) {\n        \/\/ в стандарте перечислены поля, которые есть в этом куске — там должно быть 13 байт (+4 — длина)\n        if ($len != 17) return false;\n\n        \/\/ эта комбинация может встретиться только в первых восьми байтах заголовка — дальше поля не\n        \/\/ могут принимать такие большие значения\n        if ($pos &gt; 11) return false;\n\n        \/\/ если разрешены короткие теги, нужно присмотреться к этому коду\n        \/\/ в столь ограниченном пространстве вред можно натворить только через запуск\n        \/\/ чего-то внешнего\n        if (ini_get(&#039;short_open_tag&#039;) &amp;&amp; strpos($ihdr, &#039;`&#039;, $pos + 2) !== false) {\n            return false;\n        }\n    } \n\n    if (!@fwrite($fo, $ihdr)) return false;\n\n    \/\/ кусок iTXt (text) с нашим кодом\n    $protect = &quot;&lt;?php __halt_compiler();&quot;;\n    $length  = pack(&#039;N&#039;, strlen($protect));\n    $sum     = pack(&#039;N&#039;, sprintf(&#039;%u&#039;, crc32($protect)));\n\n    if (!@fwrite($fo, &quot;{$length}iTXt{$protect}{$sum}&quot;)) return false;\n\n    \/\/ остаток файла\n    if (@stream_copy_to_stream($fi, $fo) != $controllen - 8 - 4 - $len) return false;\n\n    return true;\n}\n\nfunction ProtectPictureGif($fi, $fo, $controllen)\n{\n    $protect = &quot;&lt;?php __halt_compiler();&quot;;\n\n    \/\/ заголовок GIF\n    if (!@fwrite($fo, fread($fi, 6))) return false;\n\n    \/\/ поле Logical Screen Descriptor\n    $lsd  = fread($fi, 7);\n    $flag = ord($lsd[4]);\n\n    \/\/ указана ли Global Color Table?\n    if ($flag &amp; 128) {\n        $pixel = 1 + ($flag &amp; 0b111);\n        $colors = pow(2, $pixel);\n        $gctlen = $colors * 3;\n\n        \/\/ читаем Global Color Table\n        $gct = fread($fi, $gctlen);\n\n        \/\/ не вставил ли уже туда злоумышленник „&lt;?“?\n        \/\/ меняем у такой комбинации последний бит — это малозаметно,\n        \/\/ безопасность важнее!\n        if (strpos($gct, &#039;&lt;?&#039;) !== false) {\n            $gct = str_replace(&#039;&lt;&#039;, &#039;=&#039;, $gct);\n        }\n\n        if (!@fwrite($fo, $lsd . $gct)) return false;\n    } else {\n        $gctlen = 0;\n\n        if (!@fwrite($fo, $lsd)) return false;   \n    }\n\n    \/\/ пишем комментарий с куском защиты\n    fwrite($fo, &quot;!\\xfe&quot; . chr(strlen($protect)) . $protect . &quot;\\0&quot;);\n\n    if (@stream_copy_to_stream($fi, $fo) != $controllen - 6 - 7 - $gctlen) return false;\n\n    return true;\n}<\/code><\/pre><\/pre><p>В коде есть два места где я ищу не встретилась ли комбинация <tt>&lt;?<\/tt> раньше, чем я вставляю защиту — это в заголовочном куске (<i>IHDR<\/i>) в ПНГ и в глобальной таблице цветов у ГИФа.<\/p>\n<p>В случае ПНГ я анализирую заголовок, чтобы понять как там оказалась эта комбинация, она валидна только в первых восьми байтах (на зло остаётся всего 6 байт), кроме того, в этом случае я контролирую размер заголовка, в стандарте <a href=\"http:\/\/www.w3.org\/TR\/PNG\/#11IHDR\">там указано<\/a> 13 байт, хотя, кажется, размер не ограничивается. В общем, в шести байтах особо не разгуляешься, всё что я придумал туда засунуть — <tt>`rm *`<\/tt>, т. е. очистка текущего каталога. На этот случай, я контролирую появление символа <tt>`<\/tt>. На мой взгляд, вероятность встретить в нормальном ПНГ в заголовке <tt>&lt;?<\/tt> после которого идёт <tt>`<\/tt> ничтожно мала.<\/p>\n<p>С ГИФом проще — меняю младший бит у цвета, это вряд ли будет заметно, а безопасность важнее.<\/p>\n",
            "date_published": "2012-08-05T21:07:00+05:00",
            "date_modified": "2024-05-08T00:00:42+05:00",
            "tags": [
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sun, 05 Aug 2012 21:07:00 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "127838",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "133174",
            "url": "https:\/\/bolknote.ru\/all\/3650\/",
            "title": "Размер имеет значение",
            "content_html": "<p>Такая ещё вещь в ПХП вызывает мою искреннюю печаль. В руководстве <a href=\"http:\/\/www.php.net\/boolean\">написано<\/a>, что при приведении переменной к булевскому типу, строка «&quot;0&quot;» трактуется как <i>false<\/i>:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">var_dump( (bool) &quot;0&quot;); \/\/ false\nvar_dump( (bool) &quot;00&quot;); \/\/ true<\/code><\/pre><\/pre><p>Сделано это по определённым причинам. ПХП, исторически, был языком не просто для чайников, а для фанерных чайников, когда снаружи приходят параметры в <i>GET<\/i> или <i>POST<\/i> запросе, ПХП их раскладывает в специальные переменные, но все данные имеют тип «строка» (иногда — массив строк).<\/p>\n<p>Дальше, и тут есть своя логика, если снаружи приходит ноль, ПХП решает (типизации-то данных запросов нет), что программист в своей ХТМЛ-форме имел ввиду число ноль и ведёт себя соответствующе. Благодаря этому «if ($variable_from_get) {}» ведёт себя «правильнее».<\/p>\n<p>Поэтому я называю ПХП языком не с «динамическим приведением типов», а с «нестабильным приведением типов». Потому что его правила приведения содержат исключения.<\/p>\n<p>Такой «строковый ноль» может привести к сюрпризам:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">var_dump(array_filter( [ 0, null, false, &quot;00&quot;, &quot;0&quot; ])); \/\/ останется один элемент — &quot;00&quot;\nvar_dump(in_array(&quot;0&quot;, [ false ])); \/\/ true<\/code><\/pre><\/pre><p>Или вот, например, поведение конструкции «<i>empty<\/i>» с той же строкой (именно из документации по этой конструкции я узнал о существовании такого нуля, ещё в ПХП3):<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">$z = &quot;0&quot;; $zz = &quot;00&quot;;\nvar_dump(empty($z)); \/\/ true\nvar_dump(empty($zz)); \/\/ false<\/code><\/pre><\/pre><p>При этом, один пробел и всё, ноль перестаёт быть «магическим»:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">var_dump(in_array(&quot; 0&quot;, [ false ])); \/\/ false<\/code><\/pre><\/pre>",
            "date_published": "2012-05-06T10:01:00+05:00",
            "date_modified": "2024-12-09T15:17:23+05:00",
            "tags": [
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sun, 06 May 2012 10:01:00 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "133174",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "120274",
            "url": "https:\/\/bolknote.ru\/all\/3478\/",
            "title": "Картинка на чистом CSS-2",
            "content_html": "<div class=\"e2-text-table\">\n<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n<tr>\n<td><img width=\"660\" height=\"306\" src=\"\/\/bolknote.ru\/imgs\/2011.11.12.jpg\" alt=\"Ещё картинка на чистом CSS (82.31КиБ)\"><\/td>\n<\/tr>\n<\/table>\n<\/div>\n<p>Я тут всё это время думал как дёшево улучшить свой <a href=\"\/2011\/11\/03\/~3466\">конвертор картинок в чистый CSS<\/a> и придумал.<\/p>\n<p>Выкинул «radial-gradient» и стал использовать «linear-gradient». Картинка, которая в прошлой версии занимала 164КБ, сейчас занимает <s>89<\/s> 79. Это уже неплохо, в сжатом (gzip) виде этот код занимает 19КБ, что всего в два с половиной раза больше, чем исходное изображение в PNG. Результат можно сильно улучшить, если отказаться от повтора цвета, но изображения с мелкими деталями (с текстом, например) пострадают.<\/p>\n<p>Кстати говоря, логотип студии Лебедева при конвертации из PNG (6,7КБ) уменьшился до <s>1,6КБ<\/s> 1,3КБ. Это уже реальная экономия, если бы не префиксы браузеров.<\/p>\n<p>Отказ от радиального градиента в пользу линейного позволил мне кодировать целую строку изображения одной конструкцией, что, в свою очередь, позволило сильно экономить на повторяющихся байтах (кстати, можно попробовать группировать пиксели по вертикали и горизонтали, а потом выбирать лучший вариант).<\/p>\n<p>Кстати этот раз «Опера», увы, не смогла показать картинку, так как, похоже, не поддерживает позиционирование для линейных градиентов.<\/p>\n<p><b>Добавлено позднее<\/b>: сделал оптимизацию, теперь конвертор делает два прогона: один просматривает пиксели по горизонтали, второй по вертикали. Выбирается тот, который даёт наименьший результат.<\/p>\n<p>Ключи запуска те же.<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">&lt;?\r\n\/\/ ffccbb → fcb\r\n\/\/ ff0000 → red\r\n\/\/ abcdef → abcdef\r\nfunction Bo_shortcolor($color) {\r\n    static $shortnames = array(\r\n        &quot;c0c0c0&quot; =&gt; &quot;silver&quot;,\r\n        &quot;808080&quot; =&gt; &quot;gray&quot;,\r\n        &quot;800000&quot; =&gt; &quot;maroon&quot;,\r\n        &quot;ff0000&quot; =&gt; &quot;red&quot;,\r\n        &quot;800080&quot; =&gt; &quot;purple&quot;,\r\n        &quot;008000&quot; =&gt; &quot;green&quot;,\r\n        &quot;808000&quot; =&gt; &quot;olive&quot;,\r\n        &quot;000080&quot; =&gt; &quot;navy&quot;,\r\n        &quot;008080&quot; =&gt; &quot;teal&quot;,\r\n        &quot;f0ffff&quot; =&gt; &quot;azure&quot;,\r\n        &quot;f5f5dc&quot; =&gt; &quot;beige&quot;,\r\n        &quot;ffe4c4&quot; =&gt; &quot;bisque&quot;,\r\n        &quot;a52a2a&quot; =&gt; &quot;brown&quot;,\r\n        &quot;ff7f50&quot; =&gt; &quot;coral&quot;,\r\n        &quot;ffd700&quot; =&gt; &quot;gold&quot;,\r\n        &quot;808080&quot; =&gt; &quot;gray&quot;,\r\n        &quot;008000&quot; =&gt; &quot;green&quot;,\r\n        &quot;808080&quot; =&gt; &quot;grey&quot;,\r\n        &quot;4b0082&quot; =&gt; &quot;indigo&quot;,\r\n        &quot;fffff0&quot; =&gt; &quot;ivory&quot;,\r\n        &quot;f0e68c&quot; =&gt; &quot;khaki&quot;,\r\n        &quot;faf0e6&quot; =&gt; &quot;linen&quot;,\r\n        &quot;800000&quot; =&gt; &quot;maroon&quot;,\r\n        &quot;000080&quot; =&gt; &quot;navy&quot;,\r\n        &quot;808000&quot; =&gt; &quot;olive&quot;,\r\n        &quot;ffa500&quot; =&gt; &quot;orange&quot;,\r\n        &quot;da70d6&quot; =&gt; &quot;orchid&quot;,\r\n        &quot;cd853f&quot; =&gt; &quot;peru&quot;,\r\n        &quot;ffc0cb&quot; =&gt; &quot;pink&quot;,\r\n        &quot;dda0dd&quot; =&gt; &quot;plum&quot;,\r\n        &quot;800080&quot; =&gt; &quot;purple&quot;,\r\n        &quot;ff0000&quot; =&gt; &quot;red&quot;,\r\n        &quot;fa8072&quot; =&gt; &quot;salmon&quot;,\r\n        &quot;a0522d&quot; =&gt; &quot;sienna&quot;,\r\n        &quot;c0c0c0&quot; =&gt; &quot;silver&quot;,\r\n        &quot;fffafa&quot; =&gt; &quot;snow&quot;,\r\n        &quot;d2b48c&quot; =&gt; &quot;tan&quot;,\r\n        &quot;008080&quot; =&gt; &quot;teal&quot;,\r\n        &quot;ff6347&quot; =&gt; &quot;tomato&quot;,\r\n        &quot;ee82ee&quot; =&gt; &quot;violet&quot;,\r\n        &quot;f5deb3&quot; =&gt; &quot;wheat&quot;,\r\n    );\r\n\r\n    $c = strtolower($color);\r\n    if (isset($shortnames[$c])) {\r\n        return $shortnames[$c];\r\n    }\r\n\r\n    if ($c[0] == $c[1] &amp;&amp; $c[2] == $c[3] &amp;&amp; $c[4] == $c[5]) {\r\n        return &#039;#&#039; . $c[1] . $c[3] . $c[5];\r\n    }\r\n\r\n    return &#039;#&#039; . $c;\r\n}\r\n\r\nclass Bo {\r\n    public function __get($num) {\r\n        return $num ? $num . &#039;px&#039; : 0;\r\n    }\r\n}\r\n\r\nfunction Bo_convert_helper($im, $lim1, $lim2, $getpix, $dotmask, $z) {\r\n    $end  = $lim1 - 1;\r\n    $dots = array();\r\n\r\n    for ($o1 = 0; $o1&lt;$lim2; $o1++) {\r\n        $coll = array();\r\n        $prev    = null;\r\n        $start   = 0;\r\n\r\n        for ($o2 = 0; $o2&lt;$lim1; $o2++) {\r\n            $pixel = $getpix($im, $o2, $o1);\r\n\r\n            if ($prev !== null &amp;&amp; $prev !== $pixel || $o2 == $end) {\r\n                if ($prev !== null) {\r\n                    $color = Bo_shortcolor(vsprintf(&#039;%02x%02x%02x&#039;, $prev));\r\n\r\n                    $coll[] = &quot;$color {$z-&gt;$start},$color {$z-&gt;$o2}&quot;;\r\n                }\r\n\r\n                if ($o2 == $end &amp;&amp; $prev != $pixel) {\r\n                    $color = Bo_shortcolor(vsprintf(&#039;%02x%02x%02x&#039;, $pixel));\r\n                    $cell[] = &quot;$color {$z-&gt;$o2}&quot;;\r\n                }\r\n\r\n                $start = $o2;\r\n            }\r\n\r\n            $prev = $pixel;\r\n        }\r\n\r\n        $dots[] = sprintf($dotmask, implode(&#039;,&#039;, $coll), $z-&gt;$o1);\r\n    }\r\n\r\n    return $dots;\r\n}\r\n\r\n\r\nfunction Bo_convert($filename, $prefix = &#039;&#039;) {\r\n    $z = new Bo();\r\n\r\n    if (!file_exists($filename) &amp;&amp; !is_readable($filename)) {\r\n        throw new Exception(&#039;File not found.&#039;);\r\n    }\r\n\r\n    $types = array(\r\n        IMAGETYPE_JPG =&gt; &#039;jpeg&#039;,\r\n        IMAGETYPE_PNG =&gt; &#039;png&#039;,\r\n        IMAGETYPE_GIF =&gt; &#039;gif&#039;,\r\n    );\r\n\r\n    $imagedata = @getimagesize($filename);\r\n    if (!is_array($imagedata) || !isset($imagedata[2]) || !isset($types[$imagedata[2]])) {\r\n        throw new Exception(&#039;Unknown image format&#039;);\r\n    }\r\n\r\n    $im = call_user_func(&quot;imagecreatefrom&quot; . $types[$imagedata[2]], $filename);\r\n    $sw = $w = imagesx($im);\r\n    $sh = $h = imagesy($im);\r\n\r\n    $hfunc = create_function(&#039;$im, $x, $y&#039;, &#039;return imagecolorsforindex($im, imagecolorat($im, $x, $y));&#039;);\r\n    $hmask = &quot;{$prefix}linear-gradient(0,%s) 0 %s&quot;;\r\n\r\n    $hdots = join(&#039;,&#039;, Bo_convert_helper($im, $w, $h, $hfunc, $hmask, $z));\r\n\r\n    $vfunc = create_function(&#039;$im, $y, $x&#039;, &#039;return imagecolorsforindex($im, imagecolorat($im, $x, $y));&#039;);\r\n    $vmask = &quot;{$prefix}linear-gradient(90,%s) %s 0&quot;;\r\n\r\n    $vdots = join(&#039;,&#039;, Bo_convert_helper($im, $h, $w, $vfunc, $vmask, $z));\r\n\r\n    if (strlen($hdots) &gt; strlen($vdots)) {\r\n        $dots = $vdots;\r\n        $sw = 1;\r\n    } else {\r\n        $dots = $hdots;\r\n        $sh = 1;\r\n    }\r\n\r\n    return &lt;&lt;&lt;HTML\r\n&lt;head&gt;\r\n&lt;title&gt;$filename converted in background by Evgeny Stepanischev \/\/bolknote.ru&lt;\/title&gt;\r\n&lt;style text=&quot;text\/css&quot;&gt;\r\n    div {\r\n        background: $dots;\r\n        background-repeat: no-repeat;\r\n        {$prefix}background-size: {$sw}px {$sh}px;\r\n        background-size: {$sw}px {$sh}px;\r\n        width: {$w}px; height: {$h}px;\r\n    }\r\n&lt;\/style&gt;\r\n&lt;body&gt;\r\n&lt;div&gt;&lt;\/div&gt;\r\n&lt;\/body&gt;\r\nHTML;\r\n}\r\n\r\nif ($_SERVER[&#039;argc&#039;] &lt; 2) {\r\n    echo &lt;&lt;&lt;HELP\r\nImage to background CSS convertor v2 by Evgeny Stepanischev. \/\/bolknote.ru Nov 2011\r\n\r\nUsage: {$_SERVER[&#039;argv&#039;][0]} filename [prefix]\r\n\r\nfilename  - image file name (PNG, JPEG or GIF)\r\nprefix    - your browser name (Opera, IE, FF, Safari, Chrome)\r\n\r\nHELP;\r\nexit;\r\n}\r\n\r\n$browsers = array(\r\n    &#039;ie&#039;     =&gt; &#039;-ms-&#039;,\r\n    &#039;opera&#039;  =&gt; &#039;-o-&#039;,\r\n    &#039;safari&#039; =&gt; &#039;-webkit-&#039;,\r\n    &#039;chrome&#039; =&gt; &#039;-webkit-&#039;,\r\n    &#039;chromium&#039; =&gt; &#039;-webkit-&#039;,\r\n    &#039;ff&#039; =&gt; &#039;-moz-&#039;,\r\n    &#039;firefox&#039; =&gt; &#039;-moz-&#039;,\r\n);\r\n\r\nif (isset($_SERVER[&#039;argv&#039;][2])) {\r\n    $type = strtolower($_SERVER[&#039;argv&#039;][2]);\r\n    $prefix = isset($browsers[$type]) ? $browsers[$type] : &#039;-&#039; . $type . &#039;-&#039;;\r\n} else {\r\n    $prefix = &#039;&#039;;\r\n}\r\n\r\ntry {\r\n    echo Bo_convert($_SERVER[&#039;argv&#039;][1], $prefix);\r\n} catch (Exception $e) {\r\n    echo &quot;Error: &quot;, $e-&gt;getMessage(), &quot;\\n&quot;;\r\n    exit(1);\r\n};<\/code><\/pre>",
            "date_published": "2011-11-12T01:24:00+05:00",
            "date_modified": "2023-06-07T17:41:14+05:00",
            "tags": [
                "css",
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sat, 12 Nov 2011 01:24:00 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "120274",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "137006",
            "url": "https:\/\/bolknote.ru\/all\/2830\/",
            "title": "Аппаратное ускорение PHP",
            "content_html": "<p><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\">Обсуждается возможность<\/a> аппаратного ускорения PHP, точнее использования в коде возможностей GPU, в данный момент речь идёт о библиотеке CUDA фирмы NVIDIA.<\/p>\n<p>Первое что приходит в голову — это ускорение сортировок чисел и вычисление md5\/sha1. Я слишком слабо разбираюсь в вопросе, чтобы понять с первого взгляда куда ещё можно приспособить графические ускорители.<\/p>\n",
            "date_published": "2010-12-01T19:27:00+05:00",
            "date_modified": "2025-08-24T16:44:16+05:00",
            "tags": [
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Wed, 01 Dec 2010 19:27:00 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "137006",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "119208",
            "url": "https:\/\/bolknote.ru\/all\/114\/",
            "title": "WinAPI и PHP",
            "content_html": "<p>Так случилось, что в <a href=\"http:\/\/www.php.net\">PHP<\/a> существует три модуля расширения — два для PHP4 и один для PHP5, позволяющих обращаться к Windows API. Способы их вызова различны, плюс ко всему, положение усугубляется тем, что модули расширения имеют одинаковые имена, документация есть только по первому из них, тогда как в комплект PHP включается второй.<\/p>\n<p>Ниже — результат моей потребности выяснить как же это всё работает. Прежде всего стоит заметить, что w32api не работает с mod_php, если старая версия вызывает ошибку при попытке повторного вызова в то же child, то новая вызывает ошибку сразу. В режиме CGI и с PHP5 я таких экспериментов не проводил.<\/p>\n<p>Итак, ниже — реализация функции link (hard link) для Windows NT. Под CLI пример работает как с PHP5, так и с PHP4. Конечно, для того, чтобы пример заработал, нужна файловая система, поддерживающая hard links. Под моей NTFS5 (Windows XP) ссылки ставятся.<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">\/\/ Written by Evgeny Stepanischev (aka BOLK)\r\nif (PHP_OS == &#039;WINNT&#039;) {\r\n    \/\/ Модуль расширения FFI для PHP5\r\n    if (extension_loaded(&#039;ffi&#039;) || intval(PHP_VERSION) &gt;= 5 &amp;&amp; @dl(&#039;php_ffi.dll&#039;)) {\r\n        function link($target, $link) {\r\n            $api = &amp; new ffi(&quot;[lib=&#039;kernel32.dll&#039;] int &quot; . &quot;CreateHardLinkA( char *link, char *target, long *attr);&quot;);\r\n            return (bool)$api-&gt;CreateHardLinkA($link, $target, NULL);\r\n        }\r\n    } else\r\n    \/\/ Модуль расширения w32API для PHP4\r\n    if (extension_loaded(&#039;w32api&#039;) || @dl(&#039;php_w32api.dll&#039;)) {\r\n        \/\/ Старая версия модуля\r\n        if (function_exists(&#039;w32api_register_function&#039;)) {\r\n            if (!function_exists(&#039;CreateHardLinkA&#039;)) {\r\n                @w32api_register_function(&#039;kernel32.dll&#039;, &#039;CreateHardLinkA&#039;, &#039;bool&#039;);\r\n            }\r\n            if (function_exists(&#039;CreateHardLinkA&#039;)) {\r\n                function link($target, $link) {\r\n                    return CreateHardLinkA($link, $target, NULL);\r\n                }\r\n            }\r\n        } else {\r\n            \/\/ Новая (примерно с версии PHP 4.3.0)\r\n            function link($target, $link) {\r\n                $api = &amp; new win32;\r\n                $api-&gt;registerfunction(&quot;bool CreateHardLinkA&quot; . &quot;(string &amp;link, string &amp;target) From kernel32.dll&quot;);\r\n                return $api-&gt;CreateHardLinkA($link, $target);\r\n            }\r\n        }\r\n    }\r\n}<\/code><\/pre>",
            "date_published": "2004-08-24T21:00:00+05:00",
            "date_modified": "2023-05-08T21:35:13+05:00",
            "tags": [
                "php",
                "prog",
                "windows",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Tue, 24 Aug 2004 21:00:00 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "119208",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "132927",
            "url": "https:\/\/bolknote.ru\/all\/925\/",
            "title": "Факт из жизни PHP",
            "content_html": "<p>Когда-то, ещё во второй версии, функции в <i>PHP<\/i> определялись иначе. Синтаксис сменился в версии 3. Для того, чтобы программисты могли безболезненно перейти на новую версию, в <i>PHP3<\/i> было введено новое ключевое слово <tt>old_function<\/tt> &#151; для поддержки старого синтаксиса. Таким образом, переименованием <tt>function<\/tt> в <tt>old_function<\/tt> можно было портировать функции <i>PHP\/FI2<\/i> в <i>PHP3<\/i>. Шли годы, а синтаксис остался. Правда, мало кто знает как он выглядел. Так вот, если вы хотите поразить знакомых <i>PHP<\/i>шников, вставьте в свою программу функцию вида:<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">old_function Test $a, $b (\n     echo $a, $b;\n);<\/code><\/pre><p>Но помните, что этот синтаксис не рекомендован к использованию.<\/p>\n",
            "date_published": "2003-08-17T14:11:02+05:00",
            "date_modified": "2024-11-24T12:37:29+05:00",
            "tags": [
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sun, 17 Aug 2003 14:11:02 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "132927",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "133175",
            "url": "https:\/\/bolknote.ru\/all\/941\/",
            "title": "Загадка про PHP. Номер 3.",
            "content_html": "<p>Дано:<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">$var = strval(TRUE-TRUE);\necho empty($var) ? &#039;empty&#039; : &#039;full&#039;;<\/code><\/pre><p>Что выведет оператор <tt>echo<\/tt> и почему я не подставил первую строку сразу в empty?<\/p>\n",
            "date_published": "2003-08-17T10:52:25+05:00",
            "date_modified": "2024-12-09T15:19:12+05:00",
            "tags": [
                "php",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sun, 17 Aug 2003 10:52:25 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "133175",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        }
    ],
    "_e2_version": 4079,
    "_e2_ua_string": "Aegea 11.0 (v4079e)"
}