Подписка на блог

В Телеграме помимо ссылок на заметки делюсь околодизайнерскими наблюдениями.

В Твиттере помимо ссылок на заметки пишу всякую чушь.

В Тумблере и Же-же есть автоматические трансляции. Если не работает, напишите мне: ilyabirman@ilyabirman.ru.

По РСС и Джейсон-фиду трансляции для автоматических читалок

ПХП

Проверка непустоты текста в ПХП: программисты пишут

Недавно я писал о проверке непустоты текста в ПХП. Конечно, суровые программисты подняли меня на смех: да если у тебя код так написан, что ты не знаешь, какого типа у тебя переменная, то чего от тебя вообще ждать. Стоит отметить, что среди моих знакомых есть несколько очень сильных программистов. Разумеется, никому из них не пришло в голову высказываться в подобном ключе. Крутые программисты знают, что нет смысла критиковать решение, пока не знаешь подробности задачи.

Напомню, я предложил проверять так:

if ((string) @$text !== '') { ... }

Мне понесли варианты. «Специально для этого есть функция empty ()»:

1

if (!empty ($text)) { ... }

Это код с ошибкой: для (int) 0 проверка будет пройдена.

2

if (trim ($text) !== '') { ... }

Это код с двумя ошибками: если переменная $text не определена, выведется предупреждение; если текст состоит из пробелов, проверка не будет пройдена.

3

if (isset ($text) && trim ($text)) { ... }

В этом коде исправлена только первая ошибка предыдущего.

Кстати, этот код полагается на «ленивое» вычисление логических выражений в ПХП: если просто поменять операнды конъюнкции местами, код снова станет с двумя ошибками. Само по себе это окей, но странно, когда такое предлагают программисты, борющиеся за чистоту кода.

4

if (isset ($text) && ($text !== "")) { ... }

Это длинный и мусорный код, и снова с ошибкой: и для false, и для NULL проверка будет пройдена.

5

if (isset ($text) && (string) $text !== '') { ... }

Этот код — длинная и мусорная версия моего. В нём просто соблюдён религиозный ритуал «не использовать оператор @», ну и он дополнительно полагается на ленивое вычисление логики.

Интересно, что никто из советчиков не указал на ошибку в моём исходном варианте (я её увидел сам, пока смотрел на предложенные альтернативы): если в переменную попадёт массив, проверка сработает, потому, что (string) от любого массива — это "Array". В зависимости от того, что дальше делать с этой переменной, это может привести к проблемам. В этом удлинённом варианте ошибка та же.

6

Все варианты кода с оператором ?? — с фатальной ошибкой: такого оператора нет в ПХП 5.4, на котором должна работать Эгея.

7

if (@strlen ($text)) { ... }

Этот код работает правильно.

Неприятность в том, что глядя на него нельзя этого сказать с уверенностью. Нужно читать документацию. Оказывается, у strlen () есть полезная особенность: если передать ему массив, он вернёт 0, а вовсе не 5 ("Array"). Правда, это только начиная с ПХП 5.2, хе-хе. В документации не сказано, как он ведёт с себя с переменными других типов, поэтому нужно идти дальше и читать документацию по неявному преобразованию типов, чтобы узнать, что strlen (0) вернёт 1, как мне и требуется. Полагаться на такие штуки, когда ожидаешь какого-то чёткого и тонкого поведения, как-то некомфортно.

Поэтому я пока оставил свой вариант, забив на потенциальную проблему с "Array".

Проверка непустоты текста в ПХП

Только не бомбите, я для себя.

В ПХП оператор == проверяет равенство значений безотносительно типа. Поэтому строка «42» и число 42 с его точки зрения равны.

Это удобно: в большинстве случаев не нужно вообще думать о типах, просто сорок два равно сорока двум. Аналогично равны, например и логическая истина и число 1. Когда важно, чтобы значения были не просто равны, но ещё и совпадали по типу, используется оператор строгого равенства === .

Часто важно убедиться, что переменная не пуста. Можно проверить так:

if (@$text) { ... }

Как бы «если текст есть, то». Любая непустая строка приведётся к логически истине, при этом и пустая строка, и null, и даже неопределённая переменная приведутся ко лжи, и условие не выполнится.

Но есть нюанс. Число 0 тоже приводится к логической лжи. С этим был связан баг в Эгее: если текст заметки состоят целиком из нуля, заметку не получалось сохранить, потому что до одной из таких строчек этот текст доходил в виде числа, и условие не выполнялось. А это вполне легальный текст заметки.

У меня рука не поднимется исправить так:

if (@$text or $text === 0) { ... }

Поэтому теперь я проверяю наличие текста так:

if ((string) @$text !== '') { ... }

Не так элегантно, как было, но вроде жить можно. Или нет?

Продолжение

Вопрос про фоновую работу ПХП

Эгея написана на ПХП как обычное веб-приложение, отвечающее на запросы браузера к серверу. Браузер просит страницу — Эгея её генерирует и отдаёт.

Часть работы, которую делает Эгея — медленная по своей природе, например создание бекапа или индексация большого блога для поиска. Но Эгея отдаёт страницы быстро.

Это потому что в Эгее реализован механизм фоновой работы через запрос к себе. Когда нужно сделать что-то долгое, Эгея устанавливает ХТТП-соединение сама с собой, как бы делая вид, что она браузер, отправляет запрос по специальному урлу, который означает «сделай бекап» или «поиндексируй поиск», и тут же обрывает соединение. В результате выполнение скрипта заканчивается быстро, а вся долгая работа делается незаметно, как раз в ответ на этот запрос к себе. О таком методе я узнал лет двенадцать назад от Романа Иванова, и с тех пор пользуюсь.

Асинхронное выполнение на ПХП

К сожалению, с этим методом возникла проблема, когда я стал поддерживать ХТТПС. Если просто отправлять запрос и сразу закрывать соединение, как я всегда делал, не происходит вообще ничего — с точки зрения сервера всё выглядит так, как будто к нему и не обращались. Если же попробовать прочитать ответ, то приходит 400 Bad Request, потому что я как бы пытаюсь говорить с ХТТПС-сервером на простом ХТТП. К сожалению, мне почему-то так и не удалось отправить самому себе запрос по ХТТПС с помощью функции fsockopen () и её родственников, хотя я вроде бы исчитал документацию со всех сторон.

Вместо запроса к себе можно использовать register_shutdown_function (). Но я когда-то пользовался ей, и у меня осталось ощущение ненадёжности — кажется, она выполнялась не всегда, и там были какие-то особенности внутри странные, например, что все пути к файлам должны быть указаны абсолютно. Короче, чтобы ей воспользоваться, нужно переструктурировать код. Женя Степанищев ещё рассказал про fastcgi_finish_request () — похожий вариант.

Эти схемы мне не нравятся непрозрачностью: часть скрипта будет выполняться в каком-то мире, который не виден никому. В варианте с запросом к себе мне ничто не мешает зайти браузером по моему служебному урлу и посмотреть, что происходит. А тут придётся что-то специальное изобретать для отладки. Кроме того, запрос к себе, разумеется, запускает новую копию ПХП, в которой заново начинается отсчёт времени. Выполняя такой запрос, я могу теоретически захотеть сделать что-то ещё так же, и инициировать запрос третьго уровня. И все эти запросы — одинаковой природы, просто заход по урлу. Мне нравится эта однородность.

Есть радикальный вариант — переделать всё так, чтобы по служебным урлам ходил аджаксом клиент, пока ни о чём не подозревающий пользователь смотрит на уже загрузившуюся страницу. Такая конфигурация мне не нравится уже эстетически. Что это за беспомощный сервер, что ему надо, чтобы его клиент приводил в чувство? Возможно, это бред, но я бы хотел, чтобы сервер мог работать самостоятельно.

Ну и пока умники в твиттере не начали писать «расскажите кто-нибудь Бирману про крон», объясню, что моя задача — сделать максимально автономное решение, не требующее от пользователя никакой специальной настройки сервера. Закачал на сервер папку — всё работает. Если вы матёрый программист в свитере, вам такое не понять.

Короче, в идеале я бы хотел заставить работать свою исходную конфигурацию. Для этого мне нужно научиться делать запрос к себе по ХТТПС. Причём мне нужно просто «потрогать» нужный урл и отвалиться — мне не нужно по нему передавать никаких данных, не нужно читать ответ. Как это сделать?

Ну и если вы знаете какой-то ещё способ добиться нужного результата, лишённый всех описанных недостатков, тоже расскажите.

 66   2017   ПХП   скриншоты

Что почитать на выходных — 19

Например:

  1. Как интерфейс без обратной агрессии вызывает в людях ярость. Очередной кайфовый Людвиг. Подписывайтесь на него уже, если ещё не, нельзя же в каждый список его включать.
  2. Concluding the Great MP3 Bitrate Experiment. Джефф Этвуд провёл эксперимент и установил, что нет никакого смысла в мп3 более высокого битрейта, чем 160.
  3. The PHP Singularity. Он же в очередной раз про ПХП.
  4. The iPhone Is 5 Years Old. Статья странного чувака Генри Блоджета о том, как Айфон изменил мир за 5 лет. Ну и сюда же Джим Далримпл с лучшими цитатами скептиков.
  5. История создания БЭМ. После того, как я опубликовал прошлую ссылку на статью про БЭМ, мне написал Виталий Харисов и сказал, что на самом деле начинать надо не оттуда, а отсюда вот. Чтобы въехать, откуда вообще растут ноги.

Что почитать на выходных — 11

Понравилось недавно:

  1. Hammerforum.com. Если бы плотники на форумах общались как фотографы. «That’s because you have absolutely no skills. A good hammer user can drive nails with a 20 pound sledghammer with no problem.»
  2. Startups, this is how design works. Видео-текстовое эссе о том, зачем стартапу дизайнер. Клёво и интересно сделано.
  3. A Passenger’s Lot Then... And Now. Сравнение условий на Титанике в 1912 году и на современном корабле. Я и не знал, что Титаник, оказывается, такой был крохотный по современным меркам.
  4. The Beauty of Silence. Это я в английском блоге написал про тупость искусственного шума в электромобилях. Ничё, что я сам на себя ссылаюсь? Если вы меня там не читаете, самое время подписаться и, главное, рассказать всем своим англоговорящим друзьям.
  5. PHP: a fractal of bad design. Чувак-питонист рассказывает о том, о чём просто не могут молчать Питонисты: о том, как плох и ужасен ПХП. Всё это, конечно, не имеет значения, но работа, которую провёл автор по классификации всех корявостей ПХП, вызывает уважение. Я вот тоже высказываюсь по теме.
  6. Love Hotels and Unicode. О том, откуда в Уникоде взялись символы U+1f3e9 Love Hotel и U+1f4a9 Pile of Poo.

Что почитать в выходные — 4

Понравилось на этой неделе:

  1. Developing for old browsers is (almost) a thing of the past. 37сигналов рассказывают, как они отпиливают левые браузеры. Горячо поддерживаю.
  2. To My Old Master. Письмо бывшего раба своему бывшему владельцу (США, 1865 год). Обалдеть.
  3. Adjust Your Mirrors to Avoid Blind Spots. Совет по настройке боковых зеркал машины. Контринтуитивно, но по утверждению авторов статьи — более безопасно. Езжу так уже три дня, пока немного разрывает мозг.
  4. What the Vaio Z says about Sony’s little design problem. Про разницу в подходах к дизайну у Эпла и Сони.
  5. Как добавить ПХП в список шеллов в Автоматоре. Женя Степанищев объясняет, как прикрутить ПХП-сервис иначе, чем я показывал — возможно, кому-то так больше понравится.
  6. Still Fucking Hate Email. Эм-Джи Сиглер предлагает изменить отношение к почте, смотреть на неё как на ленту твиттера, которая проносится мимо тебя, а не как на инбокс, который нужно непременно разобрать. Радикально.

Как написать сервис на ПХП

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

Разные приложения могут добавлять свои сервисы, например после установки Твиттера любой выделенный текст можно твитнуть. Но текст — это не обязательно. Сервис может уметь что-то делать с файлами или другими объектам. Девон-технолоджис много всяких сервисов дают. Сервисами никто не пользуется, потому что фиг поймёшь, как с ними совладать и какую пользу они могут принести. Не пользовался особо и я.

Но недавно назрела необходимость сделать себе макросы, которые бы заменяли простые куски текста на большие простыни ХТМЛ-кода. Чтобы писать pic image.jpg, а оно превратилось в целую кучу вёрстки для правильного размещения изображения, да ещё и размер файла само вписывало. Причём не в редакторе кода, где таких фич завались, а в любом месте. Долго объяснять, зачем, но надо было. Программерский мозг не терпит неавтоматизированной работы.

Оказалось, что всё очень просто. Запускаем Автоматор (это ещё одна недооценённая фича Мака) — он спросит, что мы хотим создать. Выбираем сервис и делаем в нём одно-единственное действие — выполнить скрипт:

Настраиваем, чтобы выделенный текст шёл скрипту в stdin и заменялся результатом выполнения (всё в правой части скриншота). Осталось написать сам скрипт на ПХП, а это может даже ребёнок (no pun intended). Саша Карпинский научил, что в начале скрипта нужно заклинание, чтобы он заработал, то есть всё вместе получится как-то так:

#!/usr/bin/php
<?
$input = trim (file_get_contents ('php://stdin'));
if ($input == 'SOPA') die ('PIPA');
echo $input;
?>

Ну, например. А дальше идём в «Систем преференсес», в настройку клавиатуры, и вешаем наш волшебный сервис на любую кнопку. Да, ПХП на Маке встроенный, то есть ничё про это думать не надо.

 56   2012   Мак   программирование   ПХП

Нотация для названий функций

В Какао мне очень нравится, как используется возможность «Объектного Си» вставлять параметры прямо в название методов («селекторов»):

[someObject doStuffWithThing:x andThing:y usingParameter:z];

Получаются нормальные предложения. В большинстве языков так делать нельзя, однако формулировать названия функций так, чтобы параметры продолжали предложение, никто не запрещает. Если параметр всего один, или параметры образуют очевидную последовательность, то это добавит удобочитаемости. Я принял нотацию, согласно которой название функции завершается подчёркиванием, если оно составлено по такому принципу. В этом случае открывающая скобка не отбивается пробелом (вообще, я всегда отбиваю; меня страшно бесит, что в ЦССе так делать нельзя).

Пример перевода:
e2_note_by_id ($id)
e2_note_with_id_($id).

А так могли бы выглядеть некоторые функции с несколькими параметрами:
do_stuff_at_($x, $y),
do_stuff_with_date_($year, $month, $day).

Если параметры не образуют очевидной последовательности или нормально переформулировать смысл функции не удаётся, то функция пишется «по старинке», без подчёркивания в конце:
e2_store_cache ($path, $data, $secure).

А какие вы используете хитрости для повышения удобочитаемости кода?

Боремся с 77 февраля: мой вариант

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

Впрочем, придумывать сложной математики мне не пришлось. Моё решение было однострочным и работало так:

return (gmdate (’Y/n/d’, gmmktime (0, 0, 0, $m, $d, $y)) == $y .’/’. $m .’/’. $d);

Функция mktime умеет делать нормальный «тайм-штамп» из любых входных параметров. Например, если дать ей 25 часов 99 минут 32 декабря 2008 когда, то она вернёт штамп для 2 часов 39 минут 2 января 2009 не поперхнувшись.

Это свойство функции mktime () страшно удобно, потому, что если нужно посчитать, какими будут дата и время через 55 часов, то можно тупо прибавить 55 к часам и забыть про суточные, месячные и годовые переходы, забыть про учёт високосности — функция всё сделает сама.

Короче, критерий правильности даты — то, что функции не пришлось ничего «исправлять». (На самом деле, я уверен, что функция ничего и не исправляет, просто формулы в ней написаны так, чтобы всё работало независимо от корректности входных параметров.) А GMT-версии функций я использую «для надёжности», чтобы нигде часовые пояса не могли что-нибудь испортить.

Сергей Коваленко в комментариях к прошлой заметке предложил это же решение, причём совершенно справедливо не стал проверять день.
Ранее Ctrl + ↓