908 заметок с тегом

программирование

Heredoc и nowdoc в PHP

Не очень-то люблю использовать heredoc и nowdoc в ПХП из-за того, что они портят форматирование (а оно важно в больших проектах). Хотя вещь удобная — внутри можно использовать оба вида кавычек без экранирования, это бывает актуально в эскуэль-запросах.

В Перле давным-давно можно нормально смещать эту конструкцию вправо, она сама обрежет лишние пробелы, а теперь и в ПХП сделали то же — с версии 7.3 можно будет делать вот так:

$values = [<<<END
      a
     b
    c
    END];

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

26 апреля   php   php7   программирование

Zabbix и sudo

Несколько дней назад админы у нас настраивали звонилку в системе мониторинга «Заббикс», чтобы получать уведомления о различных проблемах. Раньше всё было через ботов, а теперь, когда телеграм прищучили, понадобилась более традиционная сигнализация.

Дело не сложное — вешаешь триггер, который должен запускать команду. Вроде всё прописали, но не работает. При этом в логах тишь и гладь. Я сел поразбираться.

Подцепился через strace к нужному процессу, понаблюдал как он запускает команду. Оказывается для запуска команды делается дочерний процесс, ему передаётся строка для запуска строка, а дальше дочерний процесс жалуется «sudo: sorry, you must have a tty to run sudo» во второй поток (stderr), но это никуда не попадает — ни в логи, ни на экран. Странная архитектура.

Ошибка обозначает, что пользователю, из-под которого работает «Заббикс», запрещён sudo без терминала. Без терминала sudo не сможет отключить показ пароля на экране, поэтому по-умолчанию проверка включена. Так как у нас работает не человек и пароль нигде не набирается, то проверку надо выключить опцией !requiretty.

18 апреля   программирование

JavaScript и иврит

Выше прекрасный способ сломать мозг стыком трёх языков — английского, иврита и ДжаваСкрипта.

Как многим известно, в семитских языках текст записывается справа налево. Когда я лет 20 назад участвовал в разработке сайтов для некой арабской страны, это был тот ещё вызов моему внутреннему набору аксиом. Прокрутка слева, выделение текста в другую сторону, клавиша Backspace стирающая в противоположную сторону от стрелки, которая на ней нарисована, и прочие прелести.

Тогда кажется только Эксплорер умел поддерживать такое и переключался он атрибутом dir целиком, всем окном. Сейчас у нас есть Юникод и специальные коды смены направления текста. В одной строке можно сколько угодно его менять.

В ДжаваСкрипте выше именно это и сделано — английское const написано в привычном нам направлении, а дальше направление текста изменено и буквам иврита значения присваиваются справа налево. Выглядит психоделически, конечно.

Дополнение: в комментариях меня поправили. Оказывается коды смены направления тут не нужны — программы автоматически меняют направление, основываясь на том символы какого языка используются, просто в этот момент захватываются ещё и небуквенные символы — пунктуация, цифры и так далее.

13 апреля   javascript   программирование

Негативное кеширование NFS

В реализациях сетевой файловой системы NFS есть так называемое «негативное кеширование». Эта штука портит много крови программистам, а суть её проста, как чихание — если вы обратились за файлом и его на момент обращения не было, то какое-то время этот результат будет закеширован.

Например, вы обращаетесь через NFS за файлом и если его нет, даёте серверу очередей задание на генерацию. Дожидаетесь выполнения задания и снова пытаетесь найти файл по тому же пути. Если обработка задания заняла секунды, то из-за негативного кеширования файл вы не увидите.

Теоретически этот кеш можно отключить (опцией lookupcache), практически же не всегда на оборудовании заказчика удаётся это сделать.

Чтобы решить эту проблему, мы придумывали разные сигнальные схемы и в ходе экспериментов обнаружилось, что если что-то записать в папку, где мы ожидаем файл, то кеш сбрасывается. В ПХП это выглядит так:

function clear_nfs_cache(string $filename): bool
{
    $tmpname = tempnam(dirname($filename), 'clear-cache'));
    return $tmpname === false ? false : unlink($tmpname);
}
12 апреля   nfs   php   программирование

Структуры в Си и CGO

По горячим следам хочу написать какая забытая мною особенность языка Си вчера ввела меня в заблуждение и почему несмотря на то, что я работал с сишной структурой, CGO так как будто не считало.

Для начала посмотрим как определяются и передаются структуры в Си. Полную спецификацию я приводить не буду, посмотрим на два распространённых способа. Первый используется в этом небольшом примере:

#include <stdio.h>
#include <math.h>

struct point {
	double x, y;
};

double point(struct point p) {
	return sqrt(pow(p.x, 2) + pow(p.y, 2));
}

int main() {
	printf("%g\n", point((struct point){1, 2}));
}

Тут можно обратить внимание на две особенности — во-первых, везде, где мы используем структуру, приходится использовать и ключевое слово struct, во-вторых, в коде существует и структура point, и функция с таким названием.

Это происходит потому, что имена структур обитают в своём собственном пространстве имён, на его использование и указывает struct. Там же живут имена объединений (union) и перечислений (enum). Имена функций находятся в общем пространстве, так что два таких определения с одним именем друг другу не мешают.

Структуры, объявленные таким образом видны через CGO компилятора Гоу как C.struct_name (C.struct_point в данном случае). К сожалению, тут есть особенность, о которую я споткнулся — если запрашиваемая структура не определена, то Гоу не выдаст ошибки, мы просто получим пустую структуру:

package main

import ("C"; . "fmt")

func main() {
	Println(C.struct_undefined{}) // ошибки не будет
}

Но программисты — люди ленивые, писать всюду struct неудобно. К счастью есть способ этого не делать — можно завести структуру, а потом дать ей имя в пространстве всех остальных типов. Это делается при помощи ключевого слова typedef позволяющего создавать алиасы типов:

struct point {
	double x, y;
};

typedef struct point point;

Теперь у нас есть два имени для одного типа — один в пространстве структур и прочего, второе — в общем пространстве. Можно даже сделать их одинаковыми, что очень удобно. Если второе имя нам ни к чему (бывают случаи, когда оно необходимо, но я не буду их тут рассматривать), то можно определить анонимную структуру и сразу дать имя этому типу в общем пространстве, так тоже будет работать.

Это нас подводит ко второму способу работы со структурами:

#include <stdio.h>
#include <math.h>

typedef struct {
	double x, y;
} point;

double veclen(point p) {
	return sqrt(pow(p.x, 2) + pow(p.y, 2));
}

int main() {
	printf("%g\n", veclen((point){1, 2}));
}

Ключевое слово struct перед типом уже не нужно, раз все имена в общем пространстве. Это же касается и CGO.

Как раз на эти грабли я и наступил — позабыл про эти все особенности. Когда я ещё писал на Си, всегда пользовался вторым способом, с именем в общем пространстве и привык к тому, что это и есть структуры.

31 марта   cgo   программирование   си   язык гоу

Структуры и CGO

Обновление: в комментариях в Фейсбуке мне бывший коллега попенял, что я забыл Си и что поведение правильное — тут объявляется анонимная структура, а потом к ней определяется тип. Верно, практики на Си у меня за последние годы почти не было, вымылось всё из памяти. Так что текст ниже читайте с учётом этого обновления заметки.

Каждый раз расстраиваюсь, когда сталкиваюсь с CGO. Идея-то прекрасная — встраивать в программы на Гоу сишные библиотеки почти без программирования на Си. Но частности всё убивают. То нельзя функцию по указателю передать, то вот убил час с утра сегодня, пытаясь починить ошибку в своей библиотеке go-gd.

Документация говорит нам, что сишные структуры доступны в Гоу через конструкцию C.struct_name, а её размер — через C.sizeof_struct_name.

Я так и пытался сделать и получал ошибки:

package main

/*
typedef struct {
    int x, y;
} test;
*/
import "C"
import . "fmt"

func main() {
    Println(C.struct_test{}) // «{}»? Что за…
    Println(C.sizeof_struct_test) // could not determine kind of name for C.sizeof_struct_test
}

Упрощённый код выше кратко иллюстрирует проблему. В первом случае никакая ошибка не выводится (что ещё больше сбивает с толку), но структура почему-то пустая. Во втором случае компилятор выдаёт ошибку, но от этого не легче — при конфликте моих знаний с документацией как правило думаю, что права документация, потому около часа пытался понять где я тут умудрился накосячить.

Оказывается, и я набрёл на это случайно, в ходе экспериментов, struct просто не надо писать, тогда всё будет работать:

Println(C.test{}) // «{0 0}»
Println(C.sizeof_test) // 8

Если будет время, ещё поизучаю этот вопрос — посмотрю историю коммитов и всё такое, возможно была какая-то движуха, которую не отразили в документации, либо просто что-то сломали. Надеюсь кому-нибудь сэкономлю время этой заметкой.

30 марта   cgo   программирование   язык гоу

Баг в PHP 7.2 с output_add_rewrite_var

Для того, чтобы не подставлять в каждую ссылку и форму глобальные для проекта параметры запроса (идентификатор сессии, например), в ПХП используется техника, называемая Url Rewriting. У нас в проекте так передаётся токен против XSRF — благо есть возможность указать собственные параметры, которые надо передать и даже задать список тегов с атрибутами в которых это будет работать.

И при переходе на ПХП 7.2 словили неприятный баг, который уже поправлен в 7.1.9, но ещё почему-то не влит дальше: если урл не содержит ничего, кроме якоря, то параметры добавляются неправильно. Код для повторения бага такой:

<?php
output_add_rewrite_var('foo', 'bar');
?>
<a href="index.php">This is link</a>
<a href="#place">This is anchor</a>

В первую ссылку «foo=bar» добавится нормально, а во второй это будет не к месту, якорь будет испорчен:

<a href="index.php?foo=bar">This is link</a>
<a href="#place/?foo=bar">This is anchor</a>

Наверняка со стандартными сессиями ПХП, передаваемыми в урле, будет тот же результат, но я не пробовал.

Либо надо ждать обновления, либо поставлять вместо якоря текущий полный урл.

26 марта   php   php7   программирование

99 бутылок: SCSS

63. SCSS — язык шаблонизации для файлов CSS. Я написал реализацию «99 бутылок» на нём ещё в 2012 году, но по неизвестной причине так никуда и не выложил. Возможно к лучшему — посмотрел свежим взглядом и исправил несколько неточностей.

Язык полностью процедурный, с примитивными типами (есть массивы, включая ассоциативные и собственный тип — «цвет»), поддержкой интерполяции в строках и с массой своих особенностей, диктуемых областью применения.

Шаблонизатор не имеет обособляющих элементов (типа двойных фигурных скобок), его конструкции «живут» прямо в коде, который генерируется, поэтому иногда нужно ставить немного уродливые подпорочки — например, символ деления используется и в математической операции, и в синтаксисе таблиц стилей, так что иногда приходится указывать что имеется ввиду.

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

@function bottles($beer) {
    @if $beer == 0 { @return "no bottles" }
    @if $beer == 1 { @return "1 bottle" }

    @return $beer + " bottles"
}

@function beer($beer) {
    $b: bottles($beer);

    @return "#{ $b } on the wall, #{ $b }.
Take one down and pass it around, " + bottles($beer - 1) + " of beer on the wall.

"
}

$result: "
";

@for $i from 99 through 1 {
    $result: $result + beer($i)
}

@debug $result +
"No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall."
23 марта   99   программирование

Банан велик, а кожура еще больше

Много когда-то программировал на ДжаваСкрипте, поэтому многие его «особенности» просто заучил наизусть, даже не утруждая себя детальным пониманием почему и как это работает. В свою защиту хочу сказать, что многие вещи там просто «исторически сложились»

А тут братишка прислал в «Телеграм» кусок кода, который всплывает на «Стек Оверфлоу» не первый год:

console.log(0 >= null); // выведет true
console.log(0 > null || 0 == null); // выведет false

Странное поведение, как ни крути, ведь даже название операции «больше или равно» раскладывается на две — либо «больше», либо «равно». Казалось бы оба варианта должны давать одинаковый результат.

Решил я полюбопытствовать есть ли объяснение этому феномену.

Во всех браузерах работает одинаково странно, а это значит, что где-то в стандарте должно быть детально описано это поведение. И действительно, вот это место:

Let r be the result of performing abstract relational comparison lval < rval
If r is true or undefined, return false. Otherwise, return true

Что означает примерно следующее: если нам надо выполнить операцию «lval >= rval», то мы должны её преобразовать в «!(lval < rval)». То есть «0 >= null» преобразовывается в «!(0 < null)», результат операции внутри скобок — false (так как «ноль» не меньше, чем null), что после отрицания даёт нам true. Всё просто.

22 марта   javascript   программирование

PHP8

Понятно, раз пишу про ПХП, нужна какая-то фотография со слоном, поэтому держите

Пока всё прогрессивное человечество переходит на ПХП7, а менее прогрессивное до сих пор чахнет над каким-нибудь 5.4, я решил разведать что там обещается в восьмой версии и когда её обещают.

В официальной вики есть страница, где можно ознакомиться с довольно пока скудным списком предложений на восьмую версию. Меня больше всего заинтересовали два изменения, серьёзно ломающие обратную совместимость: во-первых, предлагается запретить создавать свойства, объекты и константы с одинаковыми именами в пределах одного класса, во-вторых, запретить создавать функции, константы, классы, интерфейсы и трейты с одинаковыми именами в одном пространстве имён.

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

Или вот похожий пример с константой:

class Sample
{
    const test = 'const';
    public static $test = 'variable';
}

$test = 'test';
// что ты такое?
var_dump(Sample::$test);
// на самом деле вернётся «variable», очевидно; константу придётся получить так:
var_dump(constant("Sample::$test"));

В действительности можно было бы выкрутиться скобками (сейчас так можно вызывать анонимную функцию, записанную в свойство класса), не знаю рассматривали ли такой вариант, но возможно он менее интуитивен.

// так можно было бы получать константу по имени в переменной (не работает!)
var_dump(Sample::($text));

Самым серьёзным внутренним изменением нового релиза, вероятно будет добавление JIT, Дмитрий Стогов, человек, которому мы обязаны фантастическим ускорением в «семёрке», ещё в позапрошлом году написал в рассылку php.internals, что работа над динамической компиляцией начата. Получившийся к тому времени прототип показал 20% прироста на синтетических тестах и снижение производительности на реальных приложениях.

Кстати, это не первая попытка совместить динамическую компиляцию и ПХП, в языке HHVM, диалекте ПХП от «Фейсбука», такая компиляция даёт хороший прирост в скорости. Да и Дмитрий сотоварищи ещё в 14-м году делали попытку прикрутить JIT тогда ещё пятой версии ПХП. Тогда ускорение составило всего около пары процентов и эта неудача заставила Дмитрия исследовать интерпретатор на предмет «бутылочных горлышек», который и были устранены в седьмой версии.

Результат сравнения разных версий, JIT убедительно выигрывает на фоне PHP7 и PHP7.1 на синтетических тестах

Более свежий тест того, что будет в ПХП8 показывает, что на трёх тестах (два стандартных зендовских теста и построение множества Мандельброта) динамическая компиляция уделывает версию 7.1.0 примерно вдвое. Это отличный результат! Но понятно, что в реальной жизни всё будет скромнее.

Когда же выйдет «восьмёрка»? Дата, увы, неизвестна, но есть предположение, что новую мажорную версию вряд ли можно ожидать раньше сентября 2021 года.

21 марта   php   php8   программирование
Ранее Ctrl + ↓