Архив декабря 2007

Бесплатный Secondary DNS

Вторник, 25 декабря, 2007

Как известно, для делегации домена необходимо разместить DNS-зоны на двух серверах, расположенных в разных подсетях. В ситуации, когда сервер стоит на собственной площадке с одним каналом подключения, с этим возникают проблемы. Но, как выяснилось, по адресу http://freedns.rusnet.ru/ можно бесплатно разместить Secondary DNS на сервере РусНета и тем самым решить эту проблему.

Проверка загрузки расширения (extension) в PHP

Вторник, 25 декабря, 2007

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

Кроме того, в PHP есть также функция, позволяющая загружать расшрения динамически: функция dl, аргументом которой является имя расширения. Важно отметить, что имя указывается полностью, с префиксом и расширением файла (но без пути), поэтому необходимо проверять OS, установленную на сервере.

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

Пример кода, который выполняет проверку и пытается подгрузить расширение:

if (!extension_loaded('gd')) {
  $prefix = (PHP_SHLIB_SUFFIX === 'dll') ? 'php_' : '';
  if (!dl($prefix . 'sqlite.' . PHP_SHLIB_SUFFIX)) {
    echo 'Ошибка загрузки расширения GD!';
    return;
  }
}

// вызов функций GD

Настройка квот на сервере ProFTPD с использованием MySQL

Четверг, 20 декабря, 2007

Настройка поддержки дисковых квот в ProFTP выполняется достаточно просто. Для этого необходимо установить модуль поддержки квот и модуль поддержки MySQL, создать в базе данных таблицы для хранения данных о квотах и задать в файле конфигурации данные для подключения и SQL-запросы для обращения к этим данным. Выполняется это следующим образом:

1. Установка модулей. Если установка делается из портов, то нужно выполнить

make config

и включить модули MYSQL и QUOTA.
Если установка выполняется из исходников обычным способом, то необходимо выполнить

./configure --with-modules=mod_quotatab:mod_quotatab_sql

Далее компиляция и установка выполняется обычным способом:

make && make install && make clean

2. Создание таблиц в базе данных MySQL.
ProFTPd использует две таблицы с данными о квотах. В первой (назовем ее quotalimits) хранится список ограничений для пользователей, во второй (quotatallies) — статистика по использованию дискового пространства, объему скачанных файлов и т.д.

Таблицы создаются следующими запросами:

    CREATE TABLE quotalimits (

      name VARCHAR(30),

      quota_type ENUM("user", "group", "class", "all") NOT NULL,

      per_session ENUM("false", "true") NOT NULL,

      limit_type ENUM("soft", "hard") NOT NULL,

      bytes_in_avail BIGINT NOT NULL,

      bytes_out_avail BIGINT NOT NULL,

      bytes_xfer_avail BIGINT NOT NULL,

      files_in_avail INT UNSIGNED NOT NULL,

      files_out_avail INT UNSIGNED NOT NULL,

      files_xfer_avail INT UNSIGNED NOT NULL

    );    CREATE TABLE quotatallies (

      name VARCHAR(30) NOT NULL,

      quota_type ENUM("user", "group", "class", "all") NOT NULL,

      bytes_in_used BIGINT NOT NULL,

      bytes_out_used BIGINT NOT NULL,

      bytes_xfer_used BIGINT NOT NULL,

      files_in_used INT UNSIGNED NOT NULL,

      files_out_used INT UNSIGNED NOT NULL,

      files_xfer_used INT UNSIGNED NOT NULL

    );

3. Настройка данных о подключении в файле конфигурации.

Файл конфигурации находится в /usr/local/etc/proftpd.conf.

Запросы SQL для получения данных об ограничениях пользователя, получения и обновления его статистики, а также добавления новой записи в таблицу статистики в случае ее отсутствия. Запросы задаются с помощью директивы SQLNamedQuery, после которой указывается имя запроса (в одно слово) и текст запроса.

  SQLNamedQuery get-quota-limit SELECT "name, quota_type, per_session, limit_type, bytes_in_avail, \

    bytes_out_avail, bytes_xfer_avail, files_in_avail, files_out_avail, files_xfer_avail FROM quotalimits \

    WHERE name = '%{0}' AND quota_type = '%{1}'"  SQLNamedQuery get-quota-tally SELECT "name, quota_type, bytes_in_used, bytes_out_used, \

    bytes_xfer_used, files_in_used, files_out_used, files_xfer_used FROM quotatallies \

    WHERE name = '%{0}' AND quota_type = '%{1}'"

SQLNamedQuery update-quota-tally UPDATE "bytes_in_used = bytes_in_used + %{0}, \

    bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, \

    files_in_used = files_in_used + %{3}, files_out_used = files_out_used + %{4}, \

    files_xfer_used = files_xfer_used + %{5} \

    WHERE name = '%{6}' AND quota_type = '%{7}'" quotatallies

SQLNamedQuery insert-quota-tally FREEFORM \

    "INSERT INTO quotatallies VALUES ('%{0}','%{1}','%{2}','%{3}','%{4}','%{5}','%{6}','%{7}')"

Далее указываем путь файла для блокировки и имена запросов, используемых для выполнения определенных действий (префикс sql: означает, что используется модуль mod_quotatab_sql).

  QuotaLock /var/run/ftpd/tally.lock

  QuotaLimitTable sql:/get-quota-limit

  QuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally

Теперь укажем строку с данными для подключения к СУБД (примечание: если она уже указана в секции Global, и для хранения квот используется та же база, то повторно ее указывать не обязательно):

  SQLConnectInfo proftpd@localhost proftpd password

Далее осталось только включить модуль квот и пересчет статистики при заходе пользователя на сервер:

  QuotaEngine on

  QuotaOptions ScanOnLogin

В итоге в файле конфигурации получается примерно следующее:

<IfModule mod_quotatab.c>

  SQLConnectInfo proftpd@localhost proftpd password

  SQLNamedQuery get-quota-limit SELECT "name, quota_type, per_session, limit_type, bytes_in_avail, \

  bytes_out_avail, bytes_xfer_avail, files_in_avail, files_out_avail, files_xfer_avail FROM quotalimits \

  WHERE name = '%{0}' AND quota_type = '%{1}'"  SQLNamedQuery get-quota-tally SELECT "name, quota_type, bytes_in_used, bytes_out_used, \

  bytes_xfer_used, files_in_used, files_out_used, files_xfer_used FROM quotatallies \

  WHERE name = '%{0}' AND quota_type = '%{1}'"

SQLNamedQuery update-quota-tally UPDATE "bytes_in_used = bytes_in_used + %{0}, \

  bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, \

  files_in_used = files_in_used + %{3}, files_out_used = files_out_used + %{4}, \

  files_xfer_used = files_xfer_used + %{5} \

  WHERE name = '%{6}' AND quota_type = '%{7}'" quotatallies

SQLNamedQuery insert-quota-tally INSERT "%{0}, %{1}, %{2}, %{3}, %{4}, %{5}, %{6}, %{7}" quotatallies

SQLNamedQuery insert-quota-tally FREEFORM \

      "INSERT INTO quotatallies VALUES ('%{0}','%{1}','%{2}','%{3}','%{4}','%{5}','%{6}','%{7}')"

QuotaLock /var/run/ftpd/tally.lock

  QuotaLimitTable sql:/get-quota-limit

  QuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally

QuotaOptions ScanOnLogin

  QuotaEngine on

</IfModule>

4. Заполнение таблицы ограничений
Таблица ограничений заполняется следующим образом:
name — имя пользователя или группы.
quota_type — тип квоты: “user” — для пользователя, “group” — для группы, “class” — для класса соединений (см. документацию) , “all” — для всех.
per_session — если true, то ограничения распространяются на один сеанс работы с сервером, если false — то учитывается статистика и за все предыдущие обращения пользователя к серверу.
limit_type — тип ограничения. Если выбран “soft”, то при превышении объема позволяется докачать текущий файл, но запрещается скачивать/закачивать следующие.
bytes_in_avail, bytes_out_avail — ограничения на объем закачиваемых (in), скачиваемых (out) файлов в байтах.
bytes_xfer_avail — ограничения на суммарный объем закачиваемых (in) и скачиваемых (out) файлов в байтах.
files_in_avail, files_out_avail — ограничение на количество файлов, разрешенных для закачивания/скачивания
files_xfer_avail — ограничение на суммарное количество скачанных/закачанных файлов

Квоты считываются из таблицы на момент подключения пользователя к серверу и сохраняются в памяти до его отключения.
Проверить объем квоты можно с помощью команды SITE QUOTA (в клиентах командной строки команда должна вводиться как quote SITE QUOTA).

В заключение следует заметить три момента.
1. Имена и структуры таблиц могут отличаться от приведенных в данном примере. При этом необходимо учитывать эти отличия в SQL-запросах.
2. Ограничения по размерам и количеству файлов распространяются только на файлы, загруженные по FTP. Если требуется наложить ограничение на файлы, создваемые другим споосбами (например, из скриптов), необходимо воспользоваться квотами файловой системы.
3. Наличие двух записей для одного пользователя приводят к отключению квоты вообще.

Свойство clear

Вторник, 18 декабря, 2007

Часто требуется создать контейнер, внутри которого находятся только элементы со свойством float (например, горизонтальное меню или множество уменьшенных версий фотографий). В некоторых броузерах (в частности, в FireFox) высота таких контейнеров оказывается меньше высоты самих плавающих элементов, в результате чего эти элементы накладываются на текст, идущий после контейнера.

Чтобы избавиться от этого явления, следует воспользоваться свойством clear, предусмотренным в CSS. Это свойтсво помещает блочный элемент, идущий после блока со свойством float, не справа/слева от этого элемента, а ниже (иными словами, clear отменяет действие float). Если элемент со свойством clear находится в самом низу внутри контейнера, то высота этого контейнера определяется по позиции такого элемента, в результате чего проблема с наложением плавающих элементов на нижележащий текст исчезает.

Свойство clear поддерживает следующие значения: none (не используется), left (отменяет float: left), right (отменяет float: right), both (отменяет и float:left, и float:right).

Таким образом, горизонтальное меню, построенное с помощью ненумерованного списка (в соответствии с рекомендациями W3C), будет иметь такой вид:

<div class="menu">
  <ul style="display: block">
    <li style="display: block; float:left">Элемент 1</li>
    <li style="display: block; float:left">Элемент 2</li>
    <li style="display: block; float:left">Элемент 3</li>
  </ul>
  <div style="clear: both"></div>
</div>

Как избежать файлов, созданных nobody

Воскресенье, 16 декабря, 2007

Если PHP выполняется как модуль сервера Apache, то при загрузке файлов на сервер через PHP-скрипт возникает весьма неприятная ситуация: владельцем файла оказывается пользователь, от имени которого выполняется скрипт (обычно какой-нибудь nobody или www), в результате чего файл становится недоступным (или доступным только на чтение) пользователю, заходящему через FTP. Можно попытаться решить эту проблему через команды chmod/chown, но многие хостеры запрещают вызов этих функций из PHP, поэтому такой вариант будет работать не всегда.

Но есть другой способ обойти эту проблему. Реализуется он следующим образом: загружаемый файл сохраняется в какой-то временный каталог, а далее скрипт устанавливает соединение с localhost по FTP с данными пользователя-владельца аккаунта и загружает этот файл уже туда, где он будет храниться постоянно. В результате владельцем аккаунта оказывается пользователь, от имени которого осуществляется доступ по FTP и проблем с недоступностью файла не возникает. Точно такой же способ можно применять, когда на этапе инсталляции требуется создать несколько подкаталогов и выставить на них определенные права.

Код, реализующий эту идею, выглядит примерно так (здесь реализовано 3 режима: простая загрузка файла — 0, загрузка файла с chmod — 1 и загрузка файла по FTP — 2, режим определяется значением константы CONFIG_upload_mode):

   function put_file($tmpname,$newname) {
    if (!defined('CONFIG_upload_mode')) define('CONFIG_upload_mode',0);
    if (!defined('CONFIG_upload_root')) define('CONFIG_upload_root','.');
    $result=false;
    if (CONFIG_upload_mode==2 && defined('CONFIG_upload_user') && defined('CONFIG_upload_pass')) {
      $ftp=ftp_connect('localhost');
      $result=ftp_login($ftp,CONFIG_upload_user,CONFIG_upload_pass);
      $result=$result && ftp_chdir($ftp,CONFIG_upload_root);
      $result=$result && ftp_put($ftp,$newname,$tmpname);
      $result=$result && ftp_chmod($ftp,0666,$newname);
      ftp_close($ftp);
    }
    elseif (CONFIG_upload_mode==1) {
      $result=copy($tmpname,$newname);
      chmod(0666,$newname);
    }
    else $result=copy($tmpname,$newname);
    if ($result) unlink($tmpname);
    return $result;
  }

К недостаткам этого способа можно отнести следующие: а) некоторые хостеры могут запретить использование FTP-функций, б) повышается риск с точки зрения безопасности из-за необходимости хранить данные для подключения по FTP на сервере.

DIV высотой в 2 пикселя в Internet Explorer

Воскресенье, 16 декабря, 2007

Иногда бывает нужно создать пустой слой высотой 1-2 пикселя. В большинстве броузеров это не вызывает проблем, но MSIE упорно увеличивает высоту слоя примерно до 10 пикселей. Бороться с этим нужно следующим образом: указать в стиле слоя высоту шрифта размером 0%:

<div style="height: 2px; font-size: 0%"></div>

Дамп базы большого объема

Воскресенье, 16 декабря, 2007

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

Но существует скрипт под названием Sypex Dumper, который позволяет решить эту проблему. Sypex Dumper в отличии от многих подобных скриптов не загружает бекап-файл целиком в память, благодаря чему, ему безразличен размер базы данных и он одинаково быстро работает, как с маленькими, так и с большими объемами данных. Sypex Dumper Lite распространяется по лицензии GNU GPL, т. е. является абсолютно бесплатным.

Основные преимущества Sypex Dumper:

  • высокая скорость работы;
  • работа с базами любого размера;
  • простота использования;
  • удобный интерфейс;
  • многотомные бекапы;
  • мультиязычность;
  • компактность.

Скачать программу можно на ее официальном сайте: http://sypex.net

Условные комментарии и min-width в MSIE

Суббота, 15 декабря, 2007

Существенным недостатком MSIE 5.x и 6.x является то, что в нем не работают свойства CSS min-width, max-width и тому подобные. Эту проблему можно обойти с помощью применения выражений (expressions) в CSS, например, таких:

#bd { width:expression((document.documentElement.clientWidth
|| document.body.clientWidth) < 780? "780px" : "auto"); }

Но сразу возникает новая проблема: такой код не проходит валидацию. Но, к счастью, в MSIE предусмотрена возможность ее решения — так называемые “условные комментарии”, которые представляют собой часть HTML-кода, заключенную в такую конструкцию:

<!--[if lt ie 7.0]>
код
<![endif]-->

Для всех броузеров, кроме MSIE, а также для валидаторов, данный код является комментарием и никак не обрабатывается. MSIE же проверяет условие, которое написано в квадратных скобках (в приведенном примере — проверка на то, что версия младше MSIE 7) и если оно истинно, обрабатывает код обычным образом.

Теперь поместим в этот условный комментарий CSS-выражение для MSIE:

<!--[if lt ie 7.0]>
<style type="text/css">
#bd { width:expression((document.documentElement.clientWidth
 || document.body.clientWidth) < 780? "780px" : "auto"); }
</style>
<![endif]-->

Запускаем, и обнаруживаем, что и в MSIE страница стала учитывать минимальную ширину. (Правда, один неприятный момент все же останется: ширина изменяется только после того, как пользователь прекратил изменение размера окна, т.е. отпустил кнопку мыши на его границе, а не в режиме реального времени, как это бывает в остальных броузерах, но с этим уже ничего сделать не получится.)

Примечание: этот условный комментарий должен находиться после тега style с основными стилями или тега meta, подключающего внешний CSS.

Отправка данных без перезагрузки страницы или HTTP-статус 204

Суббота, 15 декабря, 2007

Иногда бывает нужно отправить данные на сервер без перезагрузки Web-страницы (такое, например, часто требуется при сохранении в черновик редактируемго текста в CMS или форуме). Обычно это организовывается через запрос с помощью AJAX, но есть более простой и надежный способ, который будет работать даже при выключенном JavaScript — использование статуса 204 (No Content), предусмотренного в HTTP-протоколе. В PHP он выдается с помощью вызова функции header:

header('HTTP/1.0 204 No Content');

Недостатком такого метода является то, что при возникновении ошибки страница все же будет перезагружена.

С точки зрения юзабилити желательно все же уведомить пользователя о том, что страница отправлена на сервер. Это уже можно сделать с помощью JavaScript следующим образом:

<div id="notyify" style="display:none">Страница отправлена на сервер</div>
<form>......
<button type="submit"
onClick="document.getElementById('notify').style.display='';
setTimeout('document.getElementById(\'notify\').style.display=\'none\'',10000);
return true">
Сохранить в черновик</button>
</form>

Ссылка на главную с точки зрения юзабилити

Суббота, 15 декабря, 2007

Очень часто требуется сделать логотип сайта ссылкой на главную страницу. Но если сайт — динамический, то получается, что на самой главной странице ссылка ведет сама на себя, что считается дурным тоном с точки зрения юзабилити. Конечно, можно отредактировать шаблон и поставить проверку вида if ($_SERVER['REQEUST_URI']!=’/') или что-то подобное, но есть более простое и красивое решение: поставить ссылку вида

<a href="/#">...</a>.

В этом случае при нажатии ссылки на главной страница перезагружаться не будет, а на других страницах ссылка будет работать точно также, как и просто <a href=”/”>.


Rambler's Top100