Разработка

PHP: Самые распространенные проблемы при работе с сессиями

phpsessionsС сессиями в php существует множество непонятных проблем. С появлением виртуальных окружений проблем стало больше.

Рассмотрим наиболее часто встречающиеся проблемы сессий на файлах (мы не будем затрагивать сессии в бд и кастомные обработчики).

session_start(): open(filename, O_RDWR) failed: No such file or directory

Очевидно, что ошибка возникает, когда не существует пути, по которому пишутся данные. Но при этом вы знаете, что на диске каталог, который был указан как аргумент session_save_path(), существует.

Например.

$ mkdir /tmp/sessions
$ chmod 777 /tmp/sessions
session_save_path('/tmp/sessions');
session_start();

Вы увидите на экране или в логах ошибку из заголовка (не во всех дистрибутивах).

Можно посмотреть audit.log из selinux, но если там нет ничего подозрительного и каталог действительно есть (а вы его создали выше), то причина такого поведения достаточно неожиданна.

Происходит это потому что вы работаете в centos 7 версии (или redhat\fedora) и выше. Именно в этой версии ввели параметр PrivateTmp для сервисов. Что это означает для нас?

Сделайте второй скрипт, который перечисляет содержимое директории /tmp, а так же выводит реальный путь каталога /tmp/sessions.

var_dump(glob('/tmp/*'));

Откройте скрипт в браузере. Вы увидите, что содержимое в браузере кардинально отличается от содержимого реальной папки /tmp. На скриншоте мы видем вывод из консоли и из браузера — результат совершенно разный.

2018-06-02-12:32:03_1625x863

Как это побороть? Использовать другой каталог для хранения сессий. Или перед стартом приложения проверять и создавать в случае необходимости нужную файловую структуру.

session_start(): Session data file is not created by your uid

Не самая распространенная ошибка. Чаще всего возникает в виртуальных окружениях.

Ошибка возникает в случае, когда uid владельца файла сессии не совпадает с uid текущего пользователя под которым запущен интерпретатор.

Как проверить, что это именно наш случай? Нужно создать скрипт, которыый создает файл в каталоге, в котором вы планируете размещать сессии и сверяет uid владельца файла и uid владельца процесса.

$file = __DIR__ . DIRECTORY_SEPARATOR . 'file.name';
$fd = fopen($file, 'w');
fwrite($fd, 'test data');
fclose($fd);
if (file_exists($file)) {
    $stat = stat($file);
    var_dump($stat['uid'] == posix_getuid());
}

Если мы увидим false, то да. Проблема имеет мысто быть. И вам нужно переместить сессии в другое место.

Так же подобное поведение характерно при некоторых способах монтирования nfs.

session_start(): open(filename, O_RDWR) failed: Permission denied

Каталог недоступен для записи и достаточно дать права 777. Но тут не все так очевидно и данный трюк действует не всегда. Если вы работаете в дистрибутивах, которые используют selinux, то даже при установке полных прав система можем вам не позволить писать по выбранному пути.

Это происходит потому что контекст процесса отличается от контекста папки. Вы всегда можете посмотреть в /var/log/audit/audit.log чтобы убедиться.

Как обращаться с контекстами можно подробнее посмотреть в документации и в книге.

Литература

Разработка, HowTo

Android Studio: на устройстве не осталось свободного места

2018-05-09-13:48:05_911x394Кто из вас не сталкивался с этим сообщением при обновлении студии под linux? Пожалуй каждый, кому доводилось с ней работать эту ошибку уже видел хотя бы раз и спешно шел в гугель для поиска ответа на вопрос что же ей не нравится и как это фиксить.

java.io.IOException: На устройстве не осталось свободного места
java.io.IOException: No space left on device

И так каждый раз. А вся проблема в том, что студия использует /tmp для распаковки установочных архивов перед копированием.

И чаще всего места там катастрофически недостает.

$ df -h |grep \/tmp
tmpfs 3,9G 455M 3,4G 12% /tmp

Во многих дистрибутивах размер раздела выставляется равным половине объема озу. Решений несколько и каждый применяет то, которое ему нравится больше.

Читать далее

Разработка, HowTo

PHP: Отлаживаем скрипты командной строки на удаленной машине

01-ПревьюОтладка бекенда на PHP уже ни у кого не вызывает проблем: достаточно правильно настроить расширение xdebug (или zend debugger), поставить  расширение в свой браузер и можно отлаживать, трассировать или профилировать бекенд.

Но что делать, когда нам требуется отладить консольную утилиту на удаленном сервере? В браузере выбрать пункте enable xdebug нельзя, а если у нас и получится передать IDE_KEY, то оно не знает, где располагается среда разработки и куда делать connect_back.

Это все легко решается одним маленьким скриптом (Важно сделать замечание: это будет работать только когда мы подключены по SSH).

#!/usr/bin/env bash
IP=`echo $SSH_CLIENT | awk "{print $1}"​`
PHP='/usr/bin/php -d 'xdebug.remote_host=${IP}' -d 'xdebug.remote_autostart=1''
$PHP $@

Теперь достаточно скомандовать

$ php-debug.sh yii

И на рабочей машине мы сразу увидим запрос на подключение.

02-Запрос на настройку путей

Разработка, HowTo

Печать в Chrome без подтверждения

Снимок экрана от 2017-10-28 01-07-21Существует задача для pos-систем печатать что-либо автоматически: чеки, квитанции и т.п. Хочется, чтобы печать можно было инициировать из скриптов, но по дефолту все браузеры показывают диалог печати или предварительный просмотр.

Это неудобно при создании киосков.

Казалось бы уже неоднократно с таким сталкивались и должны были придумать подходящее решение. Но, увы, решение есть, а все советы о том, как его использовать даже на официальных форумах браузеров некорректны.

Что нам предлагают:

  • Использовать опцию —disable-print-preview (или включать аналогичную настройку в about:flags)
  • Вместе с ней включать опцию —kiosk-printing, которая в теории должна избавить нас от диалога выбора принтера.

А вот и нет. Эти опции нельзя использовать совместно. В чем суть: —disable-print-preview передает управление на диалог выбора принтера в системе, а это лишает нас контроля над выбором пользователя. Во многих (во всех) конфигурациях windows и linux нельзя избавиться от шага выбора принтера, а предварительный просмотр в хроме подменяет диалог выбора принтера.

Опция —kiosk-printing воздействует только на диалог предварительного просмотра chrome сразу печатая просматриваемый документ, а если мы его отключим, то и смысла в установке этого параметра нет.

Отсюда вывод: если вы хотите делать автоматическую печать, то используйте только —kiosk-printing.

Из минусов стоит отметить, что окошко предварительного просмотра все же на некоторое мгновение появляется на экране.

<!DOCTYPE html>
<html>
<head>
<title>autoprint</title>
</head>
<body>
data
<script>
    window.addEventListener('load', function() {
      window.print()
    })
  </script>
</body>
</html>

И это сработает — принтер начнет печатать.

UPD:

Chromium, issue 169004: баг с окном предварительного просмотра в режиме kiosk-printing (непофикшено)

Разработка

PHP: и 64х битные числа

Мне понадобилось в одном из проектов работать с 64х битными числами в качестве масок.

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

var_dump(1 & 0xffffffffffffffff);

Этот код выведет вам на экран

int(0)

Хотя другой код выводит ровно так, как и должно быть.

var_dump(1 & ~0);
int(1)

Но при этом

var_dump(1 & hexdec(dechex(~0)), 1 & ~0);
int(0)
int(1)

Такое поведение показалось немного странным и, как позже выяснилось, все упирается в константу PHP_INT_MAX, которая на 64х битных системах равна 0x7fffffffffffffff. Семерка в начале идет потому что первый бит зарезервирован под знак.

В чем же тут дело? Если мы хотим записать очень большое число (8 байт ff), то пишем мы это число как положительное целое. Вот так: 0xffffffffffffffff. Интерпретатор сравнивает число с PHP_INT_MAX и если оно его не провосходит, то все будет сконвертировано в int, а если превосходит, то во float. Убедиться в этом можно следующим кодом.

var_dump(0xffffffffffffffff);
float(1.844674407371E+19)

А все это лишь из-за того, что в php нет типа unsigned (и модификатора для данного типа тоже нет). Поэтому записывать числа нам позволено лишь от PHP_INT_MIN до PHP_INT_MAX. Все остальное будет float’ом.

Но мы же по прежнему можем работать с 8-ми байтными числами. Для примера -1 в дополнительном коде это то самое число, которое нам нужно!

var_dump(dechex(-1));
int(0xffffffffffffffff)

Очевидный ответ: либо вспоминать способы формирования чисел в дополнительном коде, либо работать с битовыми операциями.

var_dump(1 & ((0xffffffff << 32) | 0xffffffff), 1 & ~0);
int(1)
int(1)

Разумеется все вышеописанное характерно и для 32з битныз платформ с поправкой на PHP_INT_SIZE (размер инта в байтах).

Литература:

Разработка, HowTo

Часть 9: Codeception. Настройка. Unit-тесты (Тестирование ПО)

codeceptionОглавление

Продолжаем цикл статей Тестирование ПО. В этой части рассмотрим установку и настройку фреймворка codeception. Попутно перенесем все ранее написанные тесты на новую платформу.

Прежде, чем двигаться дальше нам необходимо познакомиться с таким фреймворком как codeception. На базе PHPUnit можно делать лишь блочные и модульные тесты. Его предназначение полностью расшифровывается в названии. Поскольку изначально он был заточен под применение исключительно для юнит-тестирования (и модульного конечно же).

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

Появилась потребность в инструментах более высокого уровня, которые могли бы позволить манипулировать всем этим разнообразием консольных команд, приложений и всем тем, что позволяет искать ошибки в приложениях. Одним из таких инструментов стал Codeception — надстройка над PHPUnit, которая помимо прочего умеет выполнять тесты в браузере, осуществлять сетевые запросы, заполнять формы и много чего еще.

Читать далее

Разработка, HowTo

Часть 8: DBUnit (Тестирование ПО)

dbОглавление

Продолжаем серию статей Тестирование ПО, которая посвящена разработке ПО с применением методологии TDD.

В этой части будет рассматривать полезное дополнение к PHPUnit под названием DBUnit. Оно позволяет тестировать базу данных.

Читать далее