программирование
Надпись в «Гитхабе» ★
С тех пор как в первый раз увидел панель активности «Гитхаба», хочу на ней
И вот, годы спустя, я наконец сподобился сделать скрипт, чтобы автоматизировать процесс и написать себе там слово «ПРИВЕТ».
Скрипт переводит подготовленную картинку размером 52×7 пискелей в нужную интенсивность коммитов и распределяет их по датам. Для надписи я использовал шрифт silkscreen, русифицированный
Если захотите себе такую же, вам понадобится так же bash, git и утилиты из набора ImageMagick. Создаёте пустой репозиторий на «Гитхабе», привязываете его к локальному, запускаете мою утилиту с картинкой и любуетесь изображением на панели активности.
Бессонный хакатон: где экран?
Продолжаю писать о рефакторинге кода, написанного в свой бессонный хакатон. На этот раз хочется избавиться от подобранных вручную координат экрана эмулируемого компьютера — это неаккуратно и не универсально, — в другом браузере эти координаты скорее всего будут другими.
У меня
Такое решение наводило на меня тоску, но потом вспомнилось, что в графической библиотеке ПХП есть фильтр, который выделяет края и я решил глянуть не будет ли от него пользы.
Код для наложения фильтров выглядит следующим образом:
$im = imagecreatefrompng($screenshot_file);
imagefilter($im, IMG_FILTER_GRAYSCALE);
imagefilter($im, IMG_FILTER_EDGEDETECT);Мне казалось, что на получившемся изображении можно искать четыре точки экрана на известном расстоянии, но оказалось, что у такого метода есть ложные срабатывания. Поэтому я стал перебирать на экране все горизонтальные белые линии, пока не встречаю две параллельные нужной мне длины.
Таким образом очень надёжно находится граница экрана эмулятора, от которой на фиксированном расстоянии находится искомый экран. В итоге получается вот такой код:
const FRAME_W = 1039;
const FRAME_H = 808;
const FRAME_COLOR = 0xFFFFFF;
const FRAME_BORDER = 30;
$im = imagecreatefrompng($_SERVER["argv"][1]);
[$w, $h] = [imagesx($im), imagesy($im)];
imagefilter($im, IMG_FILTER_GRAYSCALE);
imagefilter($im, IMG_FILTER_EDGEDETECT);
for ($y = 0; $y <= $h - FRAME_H; $y++) {
for ($x = 0; $x <= $w - FRAME_W; $x++) {
$is_frame = imagecolorat($im, $x, $y) === FRAME_COLOR;
for ($i = 1; $i<FRAME_W; $i+=2) {
$is_frame =
$is_frame &&
imagecolorat($im, $x + $i, $y) === FRAME_COLOR &&
imagecolorat($im, $x + $i, $y + FRAME_H - 1) === FRAME_COLOR;
if (!$is_frame) {
break;
}
}
if ($is_frame) {
printf(
"%d,%d\n",
intdiv($x, 2) + FRAME_BORDER,
intdiv($y, 2) + FRAME_BORDER
);
exit(0);
}
}
}
exit(1);У меня тут всё в удвоенных координатах, которые я потом делю на два, чтобы получить реальные, потому что у меня экран «Ретина». Надо бы найти
CVE 2023-0567
В ПХП нашли достаточно серьёзный, на мой взгляд, баг — на некоторых повреждённых хешах функция password_verify возвращает true на любой пароль. Получается, хитрый хакер может оставить себе тайный ход — поменять в базе хеш на битый, и пользователь ничего не заметит —
Вот как это выглядит:
var_dump(password_verify("foo", '$2y$04$00000000$')); // true
var_dump(password_verify("bar", '$2y$04$00000000$')); // trueУдивляет, что один из разработчиков попытался защитить текущее поведение интерпретатора:
The documentation for password_verify() clearly states that the $hash has to be valid:
> hash
> A hash created by password_hash().I fail to see how this is a bug if you feed it garbage; also the documentation to crypt states:
> Using characters outside of this range in the salt will cause crypt() to return
a zero-length string. The two digit cost parameter isthe base-2 logarithm of the iteration count for the underlyingBlowfish-based hashing algorithm and must be in range04-31, values outside this range will cause crypt() to fail.PHP clearly behaves as described.
Что бы ни было написано в документации, возвращать true на любой пароль из функции проверки правильности пароля — очень плохая идея, как мне кажется, это должно быть довольно очевидно.
При анализе оказалось, что баг занесён ещё
PHP’s implementation of crypt_blowfish differs from the upstream Openwall version by adding a «PHP Hack», which allows one to cut short the BCrypt salt by including a $ character within the characters that represent the salt.
Hashes that are affected by the «PHP Hack» may erroneously validate any password as valid when used with password_verify and when comparing the return value of crypt() against the input.
The PHP Hack exists since the first version of PHP’s own crypt_blowfish implementation that was added in 1e820ec.
No clear reason is given for the PHP Hack’s existence. This commit removes it, because BCrypt hashes containing a $ character in their salt are not valid BCrypt hashes.
Бессонный хакатон: нажатие на кнопку
Продолжаю писать про свой бессонный хакатон.
В решении, которое я накидал за ту ночь, планировалось нажимать на кнопки эмулятора «ЭплСкриптом» — на «Маке» это логичнее всего, так как этот язык заточен как раз под такие задачи — автоматизацию работы со сторонними графическими приложениями.
Удивительно, но у меня это сделать не получилось — несмотря на то, что я слал кнопки как описано в документации, в эмуляторе ничего не происходило.
Так как время уходило, — сроку я себе выделил до утра, пришлось использовать другое решение — через утилиту cliclick, которая умеет эмулировать нажатие мышкой:
cliclick m:800,630 w:200 c:. w:200 m:+100,+100Я просто закодировал координаты нужной кнопки в том месте, где у меня она была на экране. Одна проблема — кнопка после нажатия «залипала», но я решил тем, что через 200 миллисекунд отводил курсор в сторону — w:200 m:+100,+100 на птичьем языке утилиты.
Уже позже, во время рефакторинга, я обратил внимание, что когда я нажимаю кнопку на своём ноутбуке, то в эмуляторе иногда нажимаются не одна кнопка, а две. Если жать стрелки вниз или вверх, то в эмуляторе происходит нажатие на CS+6 и на CS+7 соответственно, причём оболочка электронного журнала правильно реагирует и на эту комбинацию, и на одиночные «6» и «7».
Для эксперимента попробовал слать вместе с основной клавишей ещё
function press_key {
local key
if [[ "$1" =~ ^[0-9]{2,}$ ]]; then
key="key code $1"
else
key="keystroke \"$1\""
fi
osascript <<PRESS
tell application "System Events" to $key using command down
delay .03
tell application "System Events" to $key
PRESS
}Она определяет что ей передали — код символа или сам символ и «нажимает» запрошенную клавишу. Второй раз я «нажимаю» ту же клавишу уже без модификатора, иначе она
Скриншоты из «ZX-Ревю»
Продолжаю писать про свой «бессонный хакатон».
В комментариях к первой заметке читатель Евгений Суреев посоветовал использовать внутренние средства оболочки электронного журнала, чтобы получать скриншоты. Этот способ существует, но мне он показался чересчур сложным и медленным. Но для очистки совести я сего всё же попробовал.
Получились следующие шаги:
- в эмулятор, кроме виртуального «диска» с журналом подключаем второй пустой диск;
- листаем на нужный экран внутри эмулятора;
- нажимаем в эмуляторе кнопки SS+S;
- переводим в появившемся окне курсор в поле выбора дисковода, пишем там B;
- в этом же окне переводим курсор в поле «Имя файла», пишем туда номер текущей страницы;
- повторяем шаги 2—5 до конца раздела;
- скидываем
куда-то на диск компьютера образ со скриншотами; - при помощи утилиты mctrd достаём из образа скриншоты;
- при помощи утилиты zx-image преобразовываем скриншоты в PNG;
- преобразовываем полученное в JPG, склеиваем с большой файл, закидываем в оболочку;
Весь этот интерактив с браузером (вызвать меню, ввести диск, ввести имя) — довольно долгий,
В общем, кажется слишком много проблем, которые надо решать, причём я ещё не знаю удастся ли их решить хорошо.
А теперь неспеша всё отрефакторим
Несколько дней назад я рассказывал о своём бессоном хакатоне, — ухаживая за дочкой во время её болезни, я особо не спал, а чтобы не умереть от безделья, придумал себе задачу — за ночь изобрести и реализовать способ вытащить из эмулятора спектрумовский электронный журнал «ZX-Ревю».
Способ я придумал, реализовал и выложил на «Гитхаб». Но мозгу задача понравилось, не могу остановиться и перестать думать о том как всё переделать. Уже начал накидывать новый прототип. Выкладывать целиком пока не буду, там не готово,
Первое, что мне хотелось бы сделать — избавиться от привязки к браузеру. В прошлой реализации я просто активизировал «Сафари», после чего считал, что в активном окне открыт нужный мне эмулятор. Я подумал, что хорошо бы посмотреть какие у нас запущены браузеры и проверить открыто ли
Так как я делаю всё под «Мак», программировать решил на «ЭплСкрипте» — так проще всего.
on is_running(appName)
tell application "System Events" to (name of processes) contains appName
end is_running
set browsers to {"Safari", "Chrome", "Opera", "Firefox", "Edge", "Yandex"}
repeat with browser in browsers
if is_running(browser) then
tell application browser
if exists window "ZX Spectrum online emulator" then
log browser
activate
exit repeat
end if
end tell
end if
end repeatЧто тут происходит? Я перебираю все известные мне браузеры по имени, ищу какие из них запущены, в них смотрю — называется ли активное окно «ZX Spectrum online emulator», если да, остаюсь в нём, сообщая наружу какой из браузеров найден.
Можно было бы ещё перебрать окна каждого из браузеров, но есть опасность остановиться не в том эмуляторе — мало ли, вдруг у человека несколько запущено. Сделать несложно, но кажется это может добавить проблем.
Так как основная оболочка у меня на «Баше», осталось
BROWSER=$(osascript <<BROWSERS 2>&1
# … тут программа на «ЭплСкрипте»
BROWSERS
)В итоге, в переменной $BROWSER будет лежать имя браузера или пусто, если подходящий не найден.
Перенаправление потока 2>&1 необходимо, так как команда log «ЭплСкрипта» выведет имя браузера в поток для ошибок, а конструкция $( … ) ждёт его в стандартном выводе.
ZX-Ревю 11 и 12 за 1997 год
В результате своего хакатона я сконвертировал в формат HTML совмещённый номер 11 и 12 за 1997 год электронного журнала «ZX-Ревю». Результат захостил на «Гитхабе», можно посмотреть что получилось.
Вся навигация — курсором мыши, на пункты меню можно щёлкать, текст прокручивается (полосы прокрутки не видно, так как в оригинале её тоже нет).
Совершенно без сил отправляюсь почивать.
Бессонный хакатон
Думаю
А тут дочка после ветрянки подхватила
В итоге, было проще не спать, чем спать вот так урывками, но просто так без сна листать ленты социальных сетей скучно, и я надумал устроить себе хакатон.
Условия — до рассвета решить
В момент этого решения я как раз долистал
Некоторые из них я тогда не смог посмотреть, так как они были в виде программ для «Спектрума» — издательство пыталось перейти на более прогрессивный формат электронного журнала.
Так я и определился с целью: выковырять содержимое этих журналов наружу, чтобы их можно было смотреть на моём ноутбуке не только в эмуляторе.
Для начала я поискал чем можно расковырять формат, в котором распространяется журнал. Утилита нашлась быстро и я даже поправил в ней небольшой баг — она не собиралась под «МакОС».
Изучение расковырянного содержимого принесло одно расстройство — внутри оказалось бинарное представление программы на Бейсике и несколько файлов неизвестного формата. Судя по тому как отображается журнал в эмуляторе, формат не текстовый. Вряд ли я бы справился с его обратным инжинирингом за ночь.
Поэтому я решил попробовать другую идею — сделать автоматическую скриншотилку, которая сдирала бы содержимое экрана эмулятора и собирала бы из него длинный графический файл. Так как у журнала двухуровневое меню, файлов должно образоваться несколько, а всю интерактивщину я решить сделать в обычном HTML и CSS.
В итоге получился набор инструментов из двух файлов на смеси bash и PHP.
Первый, make_screenshots скриншотит эмулятор, вырезает оттуда экран «Спектрума», жмёт на кнопку «вниз», заботится о том, чтобы экраны не дублировались и понимает когда больше не удаётся пролистнуть. Многие вещи там захардкожены — например, задержки, положение экрана эмулятора и кнопки на виртуальной клавиатуре.
Второй, convert_to_jpegs превращает полученный набор скриншотов в файлы JPEG. Во множественном числе, потому что оказалось, что максимальная высота файла JPEG — 65535 пикселей. В такую высоту у меня всё не влезало, поэтому пришлось разбивать получающуюся простыню два файла. Вообще, можно было бы использовать PNG, там такого ограничения нет, но он в таком размере очень сильно нагружает браузер.
Потом я эти скриншоты уже руками обверстал в HTML и планирую результат тоже положить на «Гитхаб», как только у меня закончится процесс оптимизации JPEG. Он очень долгий, но овчинка выделки стоит — около 20—25% экономии, а файлы там большие — по несколько мегабайт.
Как будет готово, напишу про это отдельный небольшой пост.
Заказ щедро оплачен
На днях баловался со стилями магазина «Озон» при помощи браузерного плагина Stylus — избавлялся от навязчивой рекламы внутри списка товаров. Это те бесячие предложения, которые игнорируют выбранную сортировку.
Скрипт приводить не буду, всё равно его приходится править время от времени — у «Озона» используется фреймворк «vue»
Заодно, в качестве баловства, заменил себе надпись «Заказ оплачен» на «Заказ щедро оплачен». Эту часть могу привести, может
.tsBodyM[data-widget="orderStatus"]::before {
content: 'Заказ щедро оплачен';
color: var(--ozTextPrimaryNegative);
font-size: var(--bodyMFont);
line-height: 1.29;
}
Angie
Спросили как я отношусь к появлению Angie — отечественного форка известнейшего
Это минимальный набор инструментов, чтобы сделать любой сайт. Времена менялись, Перл сменился ПХП, тот уступил Пайтону, МайЭсКуЭль заменила МарияДБ, но аббревиатура держалась.
Всё сломалось, когда пришёл nginx и вытеснил Апач.
Мне было жаль LAMP, — аббревиатуру, которую я знал с начала веба, но теперь есть шанс, что она починится по крайней мере на территории нашей страны.
А если серьёзно, никаких заметных телодвижений ребята пока не сделали, поэтому не вижу повода о них разговаривать. В стране постоянно появляются форки известных западных продуктов (в реестре отечественного ПО они достаточно заметны) и с тем же постоянством исчезают.
Анекдот на Пайтоне
У меня есть друг, которому я регулярно шлю шутки про «Вархаммер». Сам в этой теме не разбираюсь, но хочется думать, что его они веселят. А я — тот самый друг, которому шлют то же, но на тему программирования. Пару раз даже присылали мои собственные шутеечки.
Вчера прислали известный неприличный анекдот про Василия Ивановича и Петьку, но написанный на «Пайтоне». И хотя у меня довольно пуританский блог, анекдот так вырвиглазно написан, что пришлось его переписать.
Вот что мне прислали:
class Chapaev:
nyuans = True
def fuck(Class):
Class.nyuans = True
Class.ass.append('Dick')
class Petyka:
nyuans = False
ass = []
if Chapaev.nyuans == True and Petyka.nyuans == False:
Chapaev.fuck(Petyka)Как по мне, плохо написано примерно всё, мне очень не понравилось, так что держите, вот вам моя версия, которая, кстати, как мне кажется, более точно отражает суть анекдота:
Chapaev = []
Petka = []
dick = [20, Chapaev]
Petka.insert(*dick)
isNuance = Chapaev in Petka and Petka not in Chapaev
print(isNuance) // TrueНу и пока у меня ещё был запал, переписал на ПХП известнейший мем из фильма «ДМБ»:
class Gopher implements Countable
{
public function count(): int
{
return 0;
}
}
$gopher = new Gopher;
var_dump(count($gopher)); // 0
var_dump(empty($gopher)); // falseОпределение ширины и высоты файла JPEG
В «Эгее» есть
Пока рабочая теория такая, что функция ПХП getimagesize (она возвращает тип изображения и его высоту с шириной) иногда выдаёт неправильные значения.
Опровергнуть или подтвердить эту теорию
Вот Илья и попросил написать меня такой код на чистом ПХП. Надеюсь это позволит продвинуться в изучении проблемы — код вошёл в одиннадцатую версию «Эгеи».
Выложу его у себя тоже, чтобы не потерялся, ну и может
function readJPEGWxH(string $filename): array
{
$file = new SplFileObject($filename);
if (!$file->flock(LOCK_SH)) {
throw new RuntimeException('Cannot lock the file');
}
if ($file->fread(2) !== "\xFF\xD8") {
throw new RuntimeException('Unknown format');
}
for (;;) {
$decoded = @unpack('H4segment/nlen', $file->fread(4));
if ($decoded === false) {
throw new RuntimeException('Unknown format');
}
['segment' => $segment, 'len' => $len] = $decoded;
// SOFn (Start Of Frame)?
if (preg_match('/^ffc[0-3]/', $segment)) {
$segdata = @unpack('Cbits/nheight/nwidth', $file->fread(5));
if ($segdata === false) {
throw new RuntimeException('Unknown format');
}
return [$segdata['width'], $segdata['height']];
}
// len = full lenght of segment except segment magic code
if ($file->fseek($len - 2, SEEK_CUR) !== 0) {
throw new RuntimeException('Cannot find start of frame');
}
}
}AVIF в «Эгее»!
Как говорится, если нельзя, но очень хочется, то можно. «Эгея», как я писал не понимает формат AVIF, но мне его удалось
Изображение ниже я взял из своей заметки про Красную площадь в коронавирусный период. В оригинале оно весит 1,1 мегабайт в формате JPEG, в этой заметке используется сконвертированное изображение размером в 179 килобайт. Почувствуйте разницу.
Добавлено позднее: в небе, кстати, я отчётливо вижу артефакты сжатия, надо играть с параметром качества, прежде чем использовать. Но в остальном картинка как будто бы не попортилась.
Конечно, если ваш браузер не поддерживает AVIF, это будет не та разница, на которую я надеялся. Напишите мне, кстати, если картинка у вас не отображается — я хотел бы узнать что у вас за браузер и насколько он современный.
Итак, «Эгея» не поддерживает AVIF, но я её в «Эгею» вставил. Как же мне это удалось?
Дело в том, что «Эгея» поддерживает формат SVG, а SVG поддерживает внутри себя растровую графику, в том числе вставленную через протокол «data». Протокол «data» — это картинка, закодированная в текстовую строку, вида data:mime/type;encoding,encoded_data.
Вообще, SVG поддерживает и внешние ресурсы (то есть в теории способен подгрузить картинку снаружи), но при вставке через тег img (как в «Эгее») в нём это запрещено по соображениям безопасности.
Поэтому мысль такая — переводим графику в AVIF, кодируем в base64 («data» так умеет, а иначе бинарные данные не вставить),
Для конвертации я написал вот такой небольшой bash-скрипт (для работы требуется пакет ImageMagick):
#!/bin/bash
if [ -z "$1" ]; then
echo "Usage: $0 <picturename>"
exit 1
fi
NAME="$1"
NEWNAME="${NAME%.*}.svg"
WH=( $(identify -ping -format '%w %h' "$NAME") )
if [[ "$NAME" == *@2x* ]]; then
WH=( $(( ${WH[0]} / 2 )) $(( ${WH[1]} / 2 )) )
fi
cat<<XML > "$NEWNAME"
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 ${WH[*]}" width="${WH[0]}" height="${WH[1]}">
<image width="${WH[0]}" height="${WH[1]}"
href="data:image/avif;base64,$(convert "$NAME" "avif:-" | base64)"/>
</svg>
XML
echo "$NAME" to "$NEWNAME" was converted.Последовательность теперь у меня такая — готовлю графику в более привычном формате, запускаю скрипт, получаю файл SVG, вставляю его
Дополнение: уже сообщают, что
Формат AVIF и «Эгея»
Есть такой современный формат графики — AVIF, который родился из видеоформата AV1. Интересен он тем, что даёт иногда существенную выгоду от использования — в моём непоказательном эксперименте на первой попавшейся картинке он уделал формат JPEG на порядок).
Вместе с тем он неплохо поддерживается современными браузерами — 79% по данным сайта CanIUse. Это ещё без учёта полифилов.
Мне очень хочется его использовать и я ищу способ это сделать в движке «Эгея», который я использую у себя в блоге. Можно было бы просто вставлять эту картинку как HTML, но это
У меня возникла хорошая, на первый взгляд, идея чуть упростить себе жизнь через модуль mod_rewrite
<IfModule mod_rewrite.c>
AddType image/avif .avif
RewriteEngine On
RewriteCond %{HTTP_ACCEPT} image/avif
RewriteCond %{REQUEST_FILENAME} \.jpg$
RewriteCond "%{REQUEST_FILENAME}.avif" -s
RewriteRule ^.*$ $0.avif [END,T=image/avif]
</IfModule>Что тут написано? Если браузер поддерживает формат AVIF (смотрим в заголовке), и запрашивает JPEG (определяется по расширению), и существует файл ненулевой длины с расширением .avif (то есть image.jpg.avif, например), то отдаём ему AVIF.
Хорошо работает, пока в папке с картинками лежит только image.jpg.avif, как только кладёшь ещё и image.jpg — перестаёт, отдаётся только она. Вчера час наверное думал что не так, пока наконец не понял — поверх «Апача» на хостинге стоит nginx, а ему мои правила по барабану.
Если картинки по урлу нет, то nginx отдаёт запрос дальше в «Апач», срабатывает правило, отдаётся AVIF, как только кладёшь JPEG, nginx его видит, отдаёт по урлу, не спрашивая «Апач» ни о чём.
Что с этим делать пока — не знаю, вероятно ничего, nginx никто отключить не даст (у меня обычный дешёвый хостинг без затей), буду думать над другими способами.
«⠀» (Braille Pattern Blank)
Недавно один из символов Юникода немного потрепал мне нервы — в одном из структурированных файлов, содержащих пользовательский ввод встретилось
Случаи, когда
Вот и в этот раз
Этот пробел — странный символ, библиотеки для работы с Юникодом не считают его пробельным, но во многих шрифтах он визуально неотличим от пробела. Как его ввёл пользователь — непонятно, может случайно через
Библиотека libiconv конвертировать его отказалась, хотя, как по мне, он вполне может быть сконвертирован в обычный пробел, что мы теперь и делаем.
Почему ПЗУ «прошивают»?
Во времена, когда я чаще паял
Если эту программу надо было обновить, то ПЗУ «перепрошивали» — при помощи специального устройства — программатора, стирали предыдущую версию и «шили» новую.
Я
Из самиздатской книги «Фокал снаружи и изнутри» (автор — Uirh):
ПЗУ тех времён (постоянное запоминающее устройство с навечно еще на заводе прошитой информацией) делалось тоже на колечках, но размером куда как побольше и из «магнитомягкого» материала. Колечек брали столько, скольки разрядное слово в этом ПЗУ; устанавливали их в один ряд; выходная катушка на каждое из них наматывалась многовитковая, а входных было столько, сколько слов информации надо было записать. Причем информация в буквальном смысле прошивалась: брали иголку, вдевали в неё проволочку для очередного слова и протягивали через весь ряд.
Причем если в данном разряде должна была быть единица — проволочку пропускали через кольцо, а если ноль — мимо. Потом, когда через эту проволочку пропускался импульс тока — в тех кольцах, сквозь которые она была продета, в выходной катушке наводится импульс напряжения. А в тех через которые нет, — импульса не было.
С появлением микросхем постоянных и полупостоянных запоминающих устройств больше так не делают. Но термин «прошить» по отношении к ПЗУ — так с тех времён и остался.
Я такие устройства в рабочем состоянии уже не застал, только их следы, — как сейчас я иногда в городе на земле нахожу советские монеты, которые тридцать лет как не в ходу, так и тогда, на свалках, где радиолюбители того времени нередко искали
Десятичная запятая
Затронули тут в одном из айтишных чатов тему десятичной запятой — я считаю, что в русском тексте должна использоваться десятичная запятая, хотя и понимаю, что программистам привычнее точка, так как во всех языках программирования используется именно она.
На этом месте я вдруг задумался — видел ли я хоть раз язык, где используется десятичная запятая?
Так и не вспомнил, зато на память пришёл забавный баг, который был
<?php
var_dump('0,4' + '0,7');
setlocale(LC_ALL, 'ru_RU.koi8r');
var_dump('0,4' + '0,7');Как можно убедиться онлайн, результат будет следующим:
int(0)
float(1,1)Работает в версиях 4.0—4.2, в 4.3 уже не повторяется. Я не помню каким образом на него наткнулся, но чувство недоумения от этого эффекта до сих пор живо в моей памяти.
Неблокирующий режим файлов в PHP
Позже я внимательно прочитал документацию на теме. По всей видимости, это ничего не даст в отношении проверки системы хранения на зависание.
Смотрел недавно исходники языка ПХП, хотел изучить подробнее как реализованы там стримы. Пока читал, случайно натолкнулся на недокументированную возможность — у функции fopen, как оказалось, есть режим открытия в неблокирующем режиме (буква n).
#if defined(O_NONBLOCK)
if (strchr(mode, 'n')) {
flags |= O_NONBLOCK;
}
#endifДоступно не на всех системах, только там, где есть константа O_NONBLOCK в fcntl.h, то есть в Линуксах, например.
Для меня это довольно полезное знание, так как я давно ищу возможность проверить из ПХП не зависла ли подмонтированная через NFS система хранения.
В общем, надо будет попробовать при следующем таком инциденте — не спасает ли открытие в неблокирующем режиме. Код, кажется, должен выглядеть примерно так:
function tryToOpen(string $path, float $timeout): ?bool
{
$dio_fd = dio_open($path, O_RDONLY | O_NONBLOCK);
if ($dio_fd === false) {
return null;
}
foreach (glob('/proc/self/fd/*') as $file) {
if (is_link($file) && readlink($file) === $path) {
$fd = basename($file);
$read = [fopen("php://fd/$fd", 'rn')];
$write = $except = null;
$timeout_us = (int) ($timeout * 1e6);
return stream_select($read, $write, $except, 0, $timeout_us) === 1;
}
}
return null;
}Тут используется модуль ПХП Direct IO, чтобы обратиться к системной функции open мимо ПХП. Приходится так делать, потому что функция fopen, которая есть в языке, сначала попытается проверить существование файла и в моей задаче сразу зависнет.
После этого я, пользуясь /proc, ищу среди своих файловых дескрипторов тот, который отвечает за только что открытый файл и переоткрываю его через fopen (в этом месте делается дубликат файлового дескриптора, а прежний закрывается — вызывается dup), так как мы тут работаем с номером файлового дескриптора, то проверка на существование файла не используется.
Переоткрытие файла нужно, так как в вызов stream_select, который я использую для ожидания данных из файла с таймаутом, можно передать результат fopen, но не dio_open.
Надеюсь случай испытать этот код представится нескоро.
Алгол и Баш
Удивительно, я и не догадывался, что иногда употребляю ключевые слова родом
Оказывается автор командной оболочки bsh Стивен Борн притащил ключевые слова IF/FI и CASE/ESAC прямиком из «Алгола». Позже оттуда они попали в более известный сейчас bash, на котором я нередко пишу.
Более того, Стивен Борн по всей видимости насколько крепко фанател по этому языку, что специальными макросами превратил Си, на котором написал bsh, в некое подобие «Алгола» (исходники есть в интернете).
Если скачать себе пару заголовочных файлов (и выкинуть из них всё то, что мешает компиляции), то можно и сейчас попробовать этот макроязык в деле (это я пузырьковую сортировку написал):
#pragma GCC diagnostic ignored "-Wimplicit-int"
#pragma GCC diagnostic ignored "-Wreturn-type"
#include "mac.h"
#include "mode.h"
#include <stdio.h>
bubbleSort(arr, n)
INT arr[], n;
BEGIN
INT i, j;
FOR i = 0; i < n - 1; i++
DO FOR j = 0; j < n - i - 1; j++
DO IF arr[j] > arr[j + 1]
THEN arr[j] = arr[j] - arr[j+1];
arr[j+1] = arr[j] + arr[j+1];
arr[j] = arr[j+1] - arr[j];
FI
OD
OD
END
printArray(arr, n)
INT arr[], n;
BEGIN
INT i;
FOR i = 0; i < n; i++
DO
printf("%d ", arr[i]);
OD
printf("\n");
END
main()
BEGIN
INT arr[] = { 0xFE, 0xED, 0xBE, 0xEF };
INT n = 1[&arr] - arr;
printArray(arr, n);
bubbleSort(arr, n);
printArray(arr, n);
ENDПримерно так выглядят исходники bsh. Единственное моё добавление — BEGIN и END, которые Борн для описания тела функций не использует, но зато в «Алголе» принято делать именно так.
Кстати (век живи — век учись), такое неординарное объявление функций, как у меня в листинге (а я подсмотрел его в исходниках bsh), — как оказалось, устаревший синтаксис Си, причём он до сих пор поддерживается компиляторами.
Ушла эпоха
Примерно в начале двухтысячных много программировал на Перле. Сейчас его заслуженно не любят, а в те времена на нём делалось много веба. С того времени прошла пара десятилетий, но меня
Вообще, Перл я любил, но была одна вещь, от которой меня некисло бомбило — это синтаксис передачи параметров в функции. Полюбуйтесь:
print sub {
my ($a, $b) = @_;
return $a + $b;
}->(1, 2);Язык помещает параметры в специальную переменную @_ (с символа «собачки» в Перле начинаются массивы), откуда их можно достать любыми способами, которые подходят для массива. Всратейший синтаксис с кучей граблей. Самые очевидные — в случае копирования единственного параметра забыть скобки вокруг переменной и получить вместо параметра длину массива аргументов. Так устроен Перл — при присваивании массива скаляру мы получаем его длину.
Самые аккуратные ребята могли бы задавать так называемые «прототипы», где в специальных значках выражалось что функции хотелось бы получить на вход, например вот так: $$$;$. Но такое в чужом коде я видел только в учебниках, так как этот птичий язык мало кто помнил.
И вот недавно я читаю, что в версии 5.36 зарелизили синтаксис сигнатур, позволяющий явно определить параметры функции, полюбуйтесь какая красота:
use v5.36;
sub range($from, $to, $step = 1) {
my $current;
return sub {
($from, $current) = ($from + $step, $from);
return undef if $current > $to;
return $current;
}
}
my $iter = range(0, 256);
while (defined (my $v = $iter->())) {
print $v, "\n"
}Теперь всё как у всех — аргументы сразу имеют нормальные имена, а чтобы понять что у нас опциональное, не надо считать количество знаков в прототипе.
Не знаю зачем Перл до сих пор ковыряют, но времена у нас нынче самые неожиданные, вдруг он