Пароль на страницу. Часть 1. Скорее теоретическая.
я решил описать способы закрыть паролем часть сайта. Тема, на самом деле, большая, поэтому на первый раз ограничусь авторизацией php+mysql.
Самый первый вопрос, который обычно встаёт — как закрыть директорию со скрпитами администрирования паролем. При этом не нужно никаких изысков — один или несколько администраторов имеют одни и те же права, а персоналии меняются редко. Проще всего в данной ситуации использовать стандартную серверную авторизацию — положить файлы .htaccess и .htpasswd и прописать в них нужные параметры. Про это уже написано много, поэтому я ничего особо нового не скажу, лучше посмотрите сюда.
Добавлю две вещи. Первое — это куда класть файл .htpasswd. Экспериментальным путем я выяснил, что если, например, путь к документу с сообщением об ошибке (ErrorDocument) пишется относительно системной переменной DocumentRoot. Но путь к файлу с паролями (UserFile) пишется относительно ServerRoot. Насколько я понял, выше ServerRoot положить .htpasswd нельзя — “../” не воспринимается. Всё это сделано для того, чтобы можно было поместить файл с паролями, например, одним уровнем выше корневой директории сайта, чтобы из сети доступа к файлу не было вообще.
Второе — это то, что скрипт может узнать, кто его открывает и пароль: переменные $PHP_AUTH_USER и $PHP_AUTH_PW.
Главный недостаток этого способа — сервер не может блокировать подбор пароля (это после нескольких неудачных попыток входа пользователю предлагается подождать часок-другой, а в течение этого времени обращения с его IP-адреса игнорируются). Это написано в официальной документации по Апачу.
Ещё один недостаток — необходимость переписывать файлы с паролями при удалении пользователя или введении нового. Но если это происходит нечасто, этого способа вполне достаточно, к тому же не придётся забивать голову написанием механизма авторизации.
Автоматизация авторизации
Это нужно не только для упрощения работы с большим количеством пользователей и их большой “текучкой”. Если нужно держать дополнительную информацию о пользователях, либо необходимо гибкое разграничение прав, лучше перенести авторизацию в базу.
Каждая страница закрытой территории подключает файл с вот таким кодом:
$result = mysql_query(”
SELECT * FROM person WHERE
login=’”. preg_replace(”/[^\\w_-]/”,”",$PHP_AUTH_USER). “‘
AND pass=’”. md5($PHP_AUTH_PW). “‘”);
if (@mysql_num_rows($result)!=1) {
header(”WWW-Authenticate: Basic realm=\”User area\”");
header(”HTTP/1.0 401 Unauthorized”);
print(”Чтобы войти в пользовательскую часть сайта, надо ввести имя и пароль.”);
exit();
};
$user_row = mysql_fetch_array($result);
В первой строке из логина удаляются все символы кроме букв, цифр, тире и символа подчеркивания. Затем проверяется количество полученных строк, и только если это одна строка, дается доступ. В остальных случаях пользователь увидит в броузере окно, предлагающее ввести логин и пароль. Если же пользователь вошел успешно, в массиве $user_row мы имеем всю информацию о нем.
Конечно же, пример, который я привёл, имеет ряд существенных недостатков. Не переписывайте его один-в-один, чтобы потом не пасть жертвой попыток подбора пароля, потому что
1. защиты от подбора здесь нет
2. если таблица пользователей большая, при подборе пароля злоумышленник, скорее всего, “завалит” базу
И последний на сегодня способ — хранение зашифрованных данных в куках.
Есть скрипт для входа, остальные подключают код, позволяющий только продолжить действия в закрытой области — если куки истекут, или он выйдет оттуда, придётся возвращаться на страницу для входа.
Входной скрипт проверяет логин и пароль и выдает две куки. В первой — логин, чтобы сразу опознать пользователя (в базе поле логина, естественно, уникальное или даже ключевое). Во второй куке — хэш от времени входа и пароля (для полноты конспирации я добавляю к этим строкам букву “Ы” — тогда хэш подобрать почти невозможно :).
Все остальные программы подключают код, который делает следующее. Делает запрос в базу — выбирает строку с полученным логином. Из этой строки берет поле “log_time” и пароль и делает из них, как и описано выше, хэш. Сравнивает его с тем, что получил, и если они совпадают, выдает новую куку хэша, опять же, от пароля, времени и буквы “Ы” и делает запрос в базу данных “UPDATE user SET log_time=’…’ WHERE login=’$cookie_login’”.
if (isset($HTTP_COOKIE_VARS[$cookie_login]) && isset($HTTP_COOKIE_VARS[$cookie_code])) {
$login = $HTTP_COOKIE_VARS[$cookie_login];
$code = $HTTP_COOKIE_VARS[$cookie_code];
$result = mysql_query(”SELECT date_format(log_date,’%Y%m%d%H%i%s’) as log_date1,pass,uid
FROM user WHERE email=’$login’ AND log_date>’DATE_SUB(NOW(),INTERVAL 15 MINUTE)’”);
if (!mysql_error() && @mysql_num_rows($result)==1) {
$log_time0 = time();
$log_time1 = date(”YmdHis”, $log_time0);
$log_time2 = date(”Y-m-d H:i:s”, $log_time0);
$current_user = mysql_fetch_array($result);
if (md5($current_user[”pass”].$current_user[”log_date1″].$md5letter) == $code) {
mysql_query(”UPDATE user SET log_date=’$log_time2′ WHERE uid=”.$current_user[”uid”]);
setcookie($cookie_code, md5($current_user[”pass”].$log_time1.$md5letter),
time()+900, $site_path);
$auth = true;
}
else
unset($current_user);
};
};
Опять же, здесь нет никакой защиты от подбора и атаки на сервер (кстати, здесь можно вместо буквы “Ы” писать IP-адрес пользователя — чтобы, например, соседу по офису нельзя было взять файл с кукой и зайти со своего компьютера).
Пароль на страницу. Часть 2. Блокировка подбора
Когда я выложил этот выпуск в прошлый раз, меня запинали на месте, мол такой блокировкой можно и сервер “пустить под откос”.
Но сначала о блокировке подбора. Банальности, но всё-таки. Пароль длинной десять символов из букв латиницы и цифр - это очень много вариантов. Если подбирать пароль по 1 000 000 вариантов в секунду, понадобится несколько тысяч лет. Но поскольку такую абракадабру запомнить сложно, мы чаще делаем пароль из осмысленных слов. Несколько лет назад оказалось, что большинство паролей можно подобрать при помощи словаря из 10 000 слов. В своё время в сети появился червь (вирус такой), который лазил по юниксовым серверам, используя их дырки в защите, и подбирал пароли привелигированых пользователей при помощи… системного орфографического словаря Юникса. Ничего таскать не надо было!
Каждый пользователь, пока он не ввёл правильный логин и пароль, считается злобным хакером. С чем же мы имеем дело, когда пользователь вводит что-либо неправильно?
забывчивость (на это на приличных сайтах есть формочка “забыл пароль”, чтобы отправить на введёный в системных настройках email этот самый пароль)
баловство (”ибо нефиг”)
подбор пароля по словарю (вероятность удачного подбора велика, поэтому закрывать надо, тем более, если сайт коммерческого характера)
DoS-атака (чтобы не перегрузить сервер, надо минимизировать действия, которые будет выполнять скрипт в таком случае)
Я долго думал, как можно вызвать перегрузку на сервере, если механизм защиты стоит на файлах. Оказалось, несложно (сколько это будет стоить - другой вопрос). Итак, допустим, сервер не выдержит, если скрипт будет пытаться 1000 раз в секунду открывать файлы на запись и писать в них данные. Поскольку после 5 неудачных попыток войти в систему пользователь будет сразу получать отказ в доступе (без какой-либо записи данных в файл), надо найти 200 уникальных IP, с которых по пять раз и обратиться. Это возможно. Вешаем в баннерокрутилке html-баннер с пятью тегами:
<img src=”http://user:password@www.host.ru/secret/absent.gif” mce_src=”http://user:password@www.host.ru/secret/absent.gif” width=1 height=1>
Пользователь моментально делает пять обращений сервер пять раз пишет в файл (кстати, в некоторых броузерах, возможно, выскочит окно для ввода логина и пароля). Можно сделать html-страницу с пятью такими картинками, а саму страницу вставить через iframe на посещаемый сайт (через iframe - чтобы по полю referer не нашли. Вряд ли служба поддержки халявного хостинга будет заниматься такими вещами как копание в лог-файлах в поисках рефереров). Те примеры, которые я привёл, разумеется, натянуты, но сам факт того, что можно воспользоваться таким недостатком системы, доказан. Кстати, нечто подобное уже было.
Но всё-таки приведу этот способ - зря писал, что ли? Его, кстати, можно без особого страха применять для ограниченного количества адресов (например, для локальной сети фирмы), положив в директорию файл .htaccess такого содержания:
order deny,allow
deny from all
allow from xxx.xxx.xxx
А вот код программы:
$errors = 0;
$fn = “ignore/”. preg_replace(”[^\d\.]”, “”, $REMOTE_ADDR. “.”. $HTTP_FORWARDED_FOR);
if (is_file($fn)) {
if (filectime($fn) < time()-3600)
unlink($fn);
else
$errors = fread(fopen($fn, “r”), 2);
};
if ($errors>5) {
print (”Доступ закрыт. Зайдите через час.”);
exit();
};
// здесь происходит установка связи с сервером БД. чтобы не трогать зря, если пользователя сразу же “отлупили”.
$result = mysql_query(”SELECT * FROM user WHERE
login=’”. preg_replace(”/[^\w_\-]/”, “”, $PHP_AUTH_USER). “‘ AND
pass=’”. md5($PHP_AUTH_PW). “‘”);
if (@mysql_num_rows($result)!=1) {
header(”WWW-Authenticate: Basic realm=\”secret area\”");
header(”HTTP/1.0 401 Unauthorized”);
print (”Authorization required”);
fwrite(fopen($fn, “w”), ++$errors);
exit();
};
$current_user = mysql_fetch_array($result);
mysql_free_result($result);
Впрочем, грех работать с файлами, если есть база. Шутка. Для непрошедших авторизаций создаём таблицу:
CREATE TABLE unauth (username VARCHAR(64) NOT NULL, pass VARCHAR(64) NOT NULL, ip VARCHAR(255), logintime TIMESTAMP)
И вместо обращения к файлам работаем с базой.
$errors = @mysql_result(mysql_query(”SELECT count(username) as falses FROM unauth WHERE
logintime>DATE_SUB(NOW(),INTERVAL 1 HOUR) AND ip=’$REMOTE_ADDR’”),0);
if (mysql_error())
die(mysql_error());
if ($errors>5) {
print (”Доступ закрыт. Зайдите через час.”);
exit();
};
$result = mysql_query(”SELECT * FROM user WHERE
login=’”. preg_replace(”/[^\w_\-]/”, “”, $PHP_AUTH_USER). “‘ AND
pass=’”. md5($PHP_AUTH_PW). “‘”);
if (@mysql_num_rows($result)!=1) {
header(”WWW-Authenticate: Basic realm=\”secret area\”");
header(”HTTP/1.0 401 Unauthorized”);
print (”Authorization required”);
mysql_query(”INSERT INTO unauth (username, pass, ip) VALUES
(’$PHP_AUTH_USER’, ‘$PHP_AUTH_PW’, ‘$REMOTE_ADDR $HTTP_X_FORWARDED_FOR’)”);
exit();
};
$current_user = mysql_fetch_array($result);
mysql_free_result($result);
Хранить ли старые записи для статистики или нет - дело хозяйское. Если что, их можно удалять, выполняя перед авторизацией запрос:
DELETE FROM unauth WHERE logintime<DATE_SUB(NOW(),INTERVAL 1 HOUR)
Такой механизм при больших нагрузках будет работать быстрее и надёжнее, чем файлы - в базе часто используемые данные буферизуются и обрабатываются непосредственно в оперативной памяти.
Пароль на страницу. Часть 3. Пароль от базы
Была у меня в своё время проблема: надо закрыть администрационную часть сайта, но при этом я не могу положить файл .htpasswd выше корневой директории сайта. Врождённая подозрительность не позволяла положить файл с паролем и отдельную директорию и заблокировать доступ к ней по http. Решил попробовать сделать защиту как в phpMyAdmin: у пользователя спрашиваются логин и пароль, с которыми скрипт соединяется с базой. В своём анализаторе логов я сделал именно так. Удобство метода в том, что файл можно складывать куда угодно — никаких кук, никаких директив сервера для директории. Заодно, если поменяется пароль в базе данных, не надо ничего исправлять в скрипте.
Распишу метод на примере MySQL. Пишем функцию, например, mysql_die:
function mysql_die() {
header(”HTTP/1.0 401 Unauthorized”);
header(”WWW-authenticate: basic realm=\”Statistics\”");
print (”Access denied. User name and password required.”);
exit();
}
В начале программы указываются хост сервера БД и, если надо, имя базы:
$db_host = “localhost”;
$db_name = “somedatabase”;
А для соединения с базой берутся переменные сервера: $PHP_AUTH_USER и $PHP_AUTH_PW.
$db_connect = @mysql_connect($db_host, $PHP_AUH_USER, $PHP_AUTH_PW)
or mysql_die();
И всё. Теперь о недостатках. Разумеется, с такой защитой можно пробовать подбирать пароль (в принципе, можно приделать блокировку, но тогда потерятеся вся красота метода). Пароль, как и в случае защитой средствами сервера, пересылается в открытом виде. Но для простых задач такое вполне сгодится.
На днях в форуме разгорелась дискуссия, что лучше где применять — защиту через 401-й код, свои куки или сессии. Я не стал ввязываться, чтобы не растерять красноречия, но в ближайших выпусках опишу в общих чертах последние два метода (а пока рекомендую читать статью про сессии).
Пароль на страницу. Часть 4. Печенюшки
Способ этот применим там, где, во-первых, пользователей много, и их контингент постоянно меняется. Во-вторых, где нужно сделать удобный вход - чтобы можно было зайти в систему, введя логин и пароль в форме на странице.
Рисуем форму и делаем файл, который получает логин и пароль (защиту от подборки я уде описывал, допишите её сюда сами).
// обработка строки с логином
$login = str_repalce(”‘”, “”, $login);
$login_result = mysql_query(”SELECT id FROM user WHERE
login=’$login’ AND pass=’”. md5($pass). “‘”);
if (!mysql_error() && @mysql_num_rows($login_result)==1) {
/* выдача кук. Имена кук и путь лучше во избежание путаницы определять
в едином подключаемом файле. */
setcookie($COOKIE_LOGIN_NAME, $login, time()+3600, $COOKIE_PATH);
setcookie($COOKIE_PASSW_NAME, $pass, time()+3600, $COOKIE_PATH);
/* Сразу же после входа пользователя перенаправляют на закрытый паролем адрес. */
header(”Location: /somepath/”);
exit;
}
elseif (!mysql_error()) {
/* вывод сообщения об ошибке и формы для повторного ввода */
print (”Неправильный логин или пароль.”);
}
else
print (mysql_error());
Все закрытые страницы вызывают файл, в котором проверяется правильность пароля, полученного из куки:
$login = str_repalce(”‘”, “”, $HTTP_COOKIE_VARS[$COOKIE_LOGIN_NAME]);
$login_result = mysql_query(”SELECT id FROM user WHERE
login=’$login’ AND pass=’”. md5($HTTP_COOKIE_VARS[$COOKIE_PASSW_NAME]). “‘”);
if (!mysql_error() && @mysql_num_rows($login_result)!=1) {
/* Если такой строки в таблице нет, пользователь перенаправляется на страицу входа. */
header(”Location: /login.php”);
exit;
}
else
print (mysql_error());
Имена кук будут использоваться в нескольких местах, поэтому лучше заранее поместить их в одном месте (например, объявив константы), чтобы потом не исправлять по нескольку раз.
Как видите, пароль будет бегать по каналу и лежать в файле с куками в открытом незакодированном виде. Это очень небезопасно. В отсутствие хозяина можно подойти к компьютеру, заглянуть в файл, где броузер держит куки, и записать пароль на бумажку (а если в локальной сети всё общее, то и подходить не надо, и стащить пароль можно прямо при хозяине).
Чтобы этого не произошло, пароль нужно кодировать. Как приемлемый вариант, хэш md5. Тут уже нельзя увидеть пароль и зайти в систему, записав его на бумажку или copy-paste-нув. Кстати, именно так можно залазить под паролем и без ведома друга в web-интерфейсы, строящие авторизацию на сессиях. Поэтому последнее, что можно сделать в этом направлении - это менять куку при каждой загрузке страницы.
Сам когда-то делал такую схему: в таблице пользователей есть колонка с датой последнего обращения. Эту дату последнего обращения и пароль, закодированные через md5, пользователь получает при каждом обращении. Система берёт куку с логином, вытаскивает из базы эту строку, генерирует хэш от полей last_log и passwd и сравнивает его с полученным. Если они совпадают, значит посетителя можно впускать. Для пущей безопасности можно добавить проверку на истечение куки - кука должна истечь после получаса неактивности, и, соответсвенно, в базе дата последнего лога должна быть менее чем полчаса назад.
$login = str_repalce(”‘”, “”, $HTTP_COOKIE_VARS[$COOKIE_LOGIN_NAME]);
$login_result = mysql_query(”SELECT * FROM user WHERE
login=’$login’ AND last_log>DATE_SUB(NOW(), INTERVAL 30 MINUTE)”);
if (!mysql_error() && @mysql_num_rows($login_result)==1) {
/* Получаем строку таблицы и формируем хэш от нужных полей. */
$current_user = mysql_fetch_array($login_result);
$hash_to_check = md5($current_user[”passwd”]. ” Ы - чтоб никто не догадался “.
$current_user[log_time]);
if ($hash_to_check == $HTTP_COOKIE_VARS[$COOKIE_HASH_NAME]) {
$current_time = time();
/* Обновление поля последнего входа и выдача новой куки. */
mysql_query(”UPDATE user SET last_log=’”. date(”Y-m-d H:i:s”, $current_time). “‘
WHERE login=’$login’”);
setcookie($COOKIE_HASH_NAME, md5(date(”Y-m-d H:i:s”, $current_time).
” Ы - чтоб никто не догадался “.
$current_user[”passwd”]), $current_time + 1800, $COOKIE_PATH);
}
else {
/* В случае несовпадения хэша пользователь перенаправляется на страицу входа в систему. */
header (”Location: /login.php”);
exit;
};
}
elseif (!mysql_error() && @mysql_num_rows($log_result)!=1) {
header(”Location: /login.php”);
exit;
}
else
print (mysql_error());
Разумеется, ” Ы - чтоб никто не догадался ” лучше тоже выделить в отдельную переменную, а лучше использовать вместо этой строки ip-адрес посетителя (или, для обрывающегося диалапа, первые два/три числа ip-адреса).
Кстати, насчёт IP-адреса. Его лучше проверять, но не весь адрес, а только первые два (для ip, начинающихся на число меньше 127) или три (соответственно, больше 127) числа адреса. Это спасёт пользователей плохого и обрывающегося диалапа от необходимости заново авторизовыватсья после обрыва связи, и в то же время, не даст зайти взломщику, укравшему куку. Конечно же, он не сможет перезвонить и зайти через другого провайдера - адрес пула не тот, но это не наши проблемы (”в такую погоду свои дома сидят”). Как не наша проблема и воровство паролей внутри фирмы. Мы защитили от любопытных товарищей и неграмотных взломщиков, а против троянов и снифферов, которые можно поставить жертве, ничего сделать не можем.
На этом “навороты” закончились. Надёжнее защиту уже не сделать. Никто не будет лазить в файл кук за хэшем и подбирать его. Проще будет поместить между пользователем и веб-интерфейсом сниффер и при помощи него найти пароль. Можно поместить трояна, который будет запоминать всё, что пользователь ввёл на клавиатуре, но это уже не наши проблемы. Чтобы защититься от прослушивания канала, надо использовать соединения типа SSL или шифрование данных.
Пароль на страницу. Часть 5. Сессии
Зачем я писал заметку про куки? “Не понимаю, зачем писать про куки, когда в php есть сессии?!” Затем, чтобы у читателей не образовывалась перед глазами плоская картина. Не везде ещё стоит php 4-й версии, а в третьей они не поддерживаются. Более того, не везде сессии так необходимы — за редким исключением алгоритм авторизации проверяет правильность логина/пароля и правильность данных сессии, а затем либо отфутболивает клиента на страницу входа, либо берёт массив (или объект) с данными о пользователе.
Случаев, когда работа сессиями необходима, не так уж и часты. Например, в своей игре “Монополист” я сразу стал использовать сессии, потому что пользователь может играть в нескольких играх и одна и та же страница в одном и том же сеансе работы может содержать разные данные. Там лучше данные для одной из игр, в которых пользователь участвует, хранить в сессии и сделать страницу для перехода между играми.
В общем, я не утверждаю, что сессиями пользоваться не нужно. Нужно, только всему своё место. К вопросу применимости трёх способов авторизации — через 401-й заголовок (”realm”), куки или сессии — я вернусь позже. Сейчас поговорю о сессиях.
Сессии в php — это на самом деле не метод авторизации (само понятие неправильное, но в форумах спрашивают именно “как авторизовывать пользователя через сессии?”). Встроенный в php механизм пользовательских сессий лишь идентифицирует этих пользователей, авторизовывать — опять же, работа вашего скрипта.
Много про механизм сессий рассказывать не буду — уже рассказано. В самом простом виде (вернее в самом dafault-ном) механизм этот работает так: система держит на сервере файл сессии, в котором содержатся её переменные. Пользователь при запуске сессии получает уникальный идентификатор (обычно через куку), и при обращении к другим страницам отправляет её. При запуске механизма сессий в вашем скрипте обработчик php проверяет, существует ли файл соответствующий пришедшему идентификатору сессии — если существует, то скрипт сможет прочесть данные из файла, если нет — будет запущена новая сессия и создан файл. Разумеется, имя данной переменной опеределено в установках php.
Теперь о том, какими функциями мы пользуемся.
session_start(). Запускает сам механизм сессий. От пользователя должна быть переменная и соответствующий ей файл. Если нет файла, он создаётся, и сессия запускается с нуля. Если нет ни файла, ни переменной, то генерируется переменная (например, посылается заголовок с кукой) и создаётся файл.
session_register(имя1, имя2, имя3…). Указание, какие переменные запомнить в файле по окончании работы скрипта. После того как пользователь перейдёт к другой странице, можно запустить механизм сессий, и после вызова данной функции переменные будут доступны.
session_destroy(). Удаляет файл данных сессии (при использовании кук надо удалять их вручную, выставив пустую куку: “setcookie(session_name())”).
session_end(). Если после авторизации данные о пользователе менять не надо, лучше сразу “выключить за собой свет” — закрыть файл и освободить доступ к нему.
session_set_cookie_params(жизнь, путь, домен). Установка параметров куки с идентификатором сессии (по умолчанию кука выставляется на корень сервера и на 0 секунд — до закрытия браузера).
Пока всё. Подробно про сессии будут отдельные выпуски. Пока опишу механизм авторизации и идентификации пользователя при помощи сессий.
Итак, имеем три файла — вход (login), проверка (auth) и выход (logout).
// вырезка всех нежелательных символов
$login = preg_replace(”/[^\w_\.\-]/”, “”, $HTTP_POST_VARS[”login”]);
$pass = trim($HTTP_POST_VARS[”pass”]);
// проверка переменных
if (strlen($login)==0 || strlen($pass)==0)
$error = “Введите логин и пароль”;
else {
// проверка логина и пароля
$user_result = mysql_query(”SELECT * FROM user WHERE
login=’$login’ AND pass=’”. md5($pass). “‘”);
/* если возникла ошибка в базе (например, пользователь всунул в сессию дли-и-инную
переменную, которую база переваривать не захотела) или получилась не одна строка,
отфутболиваем пользователя */
if (mysql_error())
die(mysql_error());
elseif (@mysql_num_rows($user_result) != 1)
$error = “Неверное имя пользователя или пароль.”;
// если всё нормально, выбираем данные, запускаем сессию
else {
$user = mysql_fetch_assoc($user_result);
session_set_cookie_params(1800, “/”);
session_start();
// запоминаем данные о пользователе
session_register(”user”);
// и дальше отправляем его куда-нибудь
if (isset($HTTP_POST_VARS[”return”]))
header(”Location: {$HTTP_POST_VARS[’return’]}”);
else
header(”Location: /”);
exit();
};
};
/* здесь пользователь уже не прошёл авторизацию, но может отправить куку из
закрытой сессии. очистим её. */
if (isset($HTTP_COOKIE_VARS[session_name()]))
setcookie(session_name());
// дальше рисуем форму, это неинтересно.
Данный скрипт является и обработчиком и формой для ввода данных. При получении логина и пароля он обрабатывает их и, если они правильные, прекращает работу, отправив пользователя на нужную страницу. Если данные неправильные или вообще отсутствуют, рисует форму.
/* убиваем переменную user, чтобы нельзя было, нарисовав форму, отправить данные
в post-запросе. */
unset($user);
// флаг “ошибка сессии” — если он включён, работа прекратится.
$session_error = false;
// если не существует куки с идентификатором сессии, поднять флаг
if (!isset($HTTP_COOKIE_VARS[session_name()]))
$session_error = true;
// если существует, запускаем механизм сессий и регистрируем переменную $user.
else {
session_start();
session_register(”user”);
/* если случайно в массиве нет логина и пароля, работа тоже прекращается (”ничего
не знаем, мы вам их давали”) */
if (!isset($user[”login”]) || !isset($user[”pass”]))
$session_error = true;
};
/* если пользователю до сих пор удалось геройски избежать ошибок, делается проверка
через базу так же, как и на входе. */
if (!$session_error) {
$check_result = mysql_query(”SELECT uid FROM user WHERE
login=’{$user[login]}’ AND pass=’{$user[pass]}’”);
if (mysql_error() || @mysql_num_rows($user_result) != 1)
$session_error = true;
};
// если была какая-то ошибка, то
if ($session_error) {
// уничтожаем данные сессии
session_destroy();
// уничтожаем куку, если она была
if (!isset($HTTP_COOKIE_VARS[session_name()]))
setcookie(session_name(),”",”/”);
/* отправляем пользователя на вход, с возможностью вернуться на запрошенный адрес */
header(”Location: /login.php?return=$REQUEST_URI”);
// прекращаем работу
exit();
};
mysql_free_result($check_result);
Пользователь проверен и в массиве $user — все данные о нём, можно, например, поприветствовать его по имени-отчеству:
<?
include(”auth.inc”);
?><html>
<head><title><? print (”Здравствуйте, {$user[fname]} {$user[sname]}!”); ?></title></head>
<body>
[skip]
И выход:
if(isset($HTTP_COOKIE_VARS[session_name()])) {
// запуск механизма сессий
session_start();
// удаление файла
session_destroy();
// удаление куки
setcookie(session_name());
};
// выход со страницы
header(”Location: /login.php”);
Пара замечаний: закрываемая паролем часть в данном примере - весь сервер (например, service.firm.ru), для закрытия директории нужно исправить пути. Вместо PHPSESSID используется session_name(), чтобы можно было свободно менять имя идентификатора. Кстати, на одном физическом сервере можно делать разные имена идентификаторов сессий - достаточно в нужную часть положить файл .htaccess со строкой php_value session.name “ABRACADABRA”
Оставить комментарий
Источник http://phpclub.ru
Tags:
AUTH,
htpasswd,
php,
авторизация,
куки,
логин,
подбор
Март 14, 2008
Многие обладатели домашних страничек рано или поздно начинают интересоваться устройством крупных новостных порталов и контент-проектов - не вручную же они ве эти страницы делают! А как тогда? Данная статья поможет начинающему веб-мастеру начать создание проектов с применением PHP-MySQL.
Один из самых часто задаваемых вопросов начинающих веб-мастеров: как начать работать с базами данных MySQL, используюя скрипты на PHP? Это неудивительно, потому что эра статических HTML-сайтов давно прошла (на “Народе” это любимое народом дело процветает
и теперь более-менее грамотные начинающие веб-мастера осознали удобство и комфорт создания и поддержания контент проектов на основе PHP-MySQL. Скептики и консерваторы будут утверждать, что работать с файлами проще, что легче наверстать страничку вручную, чем тратить время на отладку и написание скриптов. Не слушайте их - это абсурд! Как говорится “Лучше день потерять потом за 5 минут долететь!”. Ну допустим, на подготовку и создание динамической версии сайта на основе PHP-MySQL уходит раза в два-три больше времени, а то и больше, но зато поддержка проекта в будущем покажется вам приятным и простым занятием (конечно, настолько, насколько серьёзно вы к этому подойдёте). А почему загибаются казалось бы хорошие статические сайты? Причина всему - рутинные операци по обновлению сайта. Если на PHP-MySQL сайте есть возможность добавлять статьи через форму (копировать, вставить, отправить), то для добавления статьи на статический сайт нужно намного больше более сложных операций:
Сверстать новую страничку на основе существующего шаблона.
Проверить форматирование, ссылки, картинки, всё ли на месте.
Добавить ссылку на новую страницу где-то ещё (а то и на несколько!), например в раздел “Статьи”.
Проверить, как всё это работает целиком.
Соединиться с сервером FTP.
Закачать все обновлённые страницы на сервер.
Проверить, всё ли работает в онлайне.
Вот почему многие бросают любимое занятие. Ну есть конечно некоторые усердные личности, которые годами поддерживают такие сайты. А есть и умельцы, которые за пару месяцев разработают БД и PHP-движок и будут жить методом “копировать, вставить, отправить”!
Надеюсь, я привёл достаточные аргументы в пользу динамического сайта. И вот Вася Пупкин решил опробовать себя в этом нелёгком деле, но тут возникает несколько резонных вопросов:
Есть ли у меня PHP?
Есть ли у меня MySQL?
Есть ли у меня вообще сайт? (шутка
Итак, для создания динамического сайта нам понадобится хостинг с поддержкой PHP и MySQL. Ну здесь я вам не советчик - если вы создаёте серьёзный проект, то лучше немного заплатить, но получить всё и сразу - тогда вам сюда http://hcenter.info. Из бесплатных хостингов PHP и MySQL доступны кажется на Агаве, но я могу ошибаться - тогда поищите в Яндексе “бесплатный хостинг с поддержкой PHP MySQL” или что-то в этом роде.
Пропускаем момент регистрации на хостинге. Теперь вам должны выслать по email письмо с логинами и паролями. Внимательно прочитайте инструкции, посмотрите FAQ на сайте хостинга, если что-то непонятно.
Во-первых, вам необходимо создать новую базу данных на MySQL-сервере. Это очень легко делается при помощи инструментов типа phpMyAdmin или любого графического клиента MySQL - SQLyog, MySQL-Front. На большинстве хостингов это делается при помощи панели управления хостингом - тут я вам не помощник, смотрите сами. Выполните функцию создания новой базы, назвав её например “test”.
При помощи одной из указанных выше программ выполните представленный ниже SQL-дамп:
#
# Table structure for table `links`
#
# Creation: Aug 12, 2003 at 05:11 PM
# Last update: Aug 12, 2003 at 05:28 PM
#
CREATE TABLE `links` (
`id` int(10) unsigned NOT NULL auto_increment,
`url` varchar(100) NOT NULL default ”,
`description` varchar(100) NOT NULL default ”,
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=6 ;
#
# Dumping data for table `links`
#
INSERT INTO `links` VALUES (1,
‘http://www.programmingsite.co.uk’,
‘programming directory’);
INSERT INTO `links` VALUES (2,
‘http://www.beginnersphp.co.uk’,
‘PHP tutorials and code’);
INSERT INTO `links` VALUES (3,
‘http://asp.programmershelp.co.uk’,
‘ASP site with code and tutorials’);
INSERT INTO `links` VALUES (4,
‘http://javascript.programmershelp.co.uk’,
‘javascript site’);
INSERT INTO `links` VALUES (5,
‘http://software.programmingsite.co.uk’,
’software directory’);
Теперь вы можете написать и выполнить свои PHP-скрипты для операций с только что созданной базой данных. Следующий PHP-скрипт подключается с серверу MySQL, производит выборку данных и выводит содержимое таблицы “links” в виде обычной HTML-таблицы.
<?php
//соединение с базой данных при помощи функции mysql_connect()
//в аргументах функции укажите имя сервера, логин и пароль.
$db = mysql_connect(”сервер”,”логин”,”пароль”);
//функция mysql_select_db() выбирает текущую
//базу данных с именем “test”
mysql_select_db(”test” ,$db);
//функция mysql_query() выполняет запрос на выборку данных
//результирующий набор данных хранится в переменной $sql
$sql = mysql_query(”SELECT * FROM links” ,$db);
//после получения данных начнём формирование HTML-таблицы
echo (”<table border =’1′>”);
//выводим строку заголовков
echo (”<tr><td>Адрес</td><td>Описание</td></tr>”);
//функция mysql_fetch_row() извлекает одну строку из результата
//и сохраняет её в массиве $tablerows
while ($tablerows = mysql_fetch_row($sql))
{
//теперь в цикле для каждой полученной строки сделаем вывод
//$tablerows[1] соответствует полю “url”
//$tablerows[2] соответствует полю “description”
echo(”<tr><td><a href=’$tablerows[1]’>$tablerows[1]</a></td><td>$tablerows[2]</td></tr> “);
}
echo “</table>”;
//закрытие соединение (рекомендуется)
mysql_close($db);
?>
Сохраните скрипт в файл под именем, например test.php и закачайте на FTP-сервер вашего сайта. Скрипт будет доступен например так: http://vasya.server.ru/test.php (ну или смотря куда вы его закачали). Если всё сделано правильно, скрипт отработает и выведет на экран содержимое БД в виде обычной таблицы.
Буду искренне рад, если эта статья стала для вас отправной точкой для создания динамического сайта!
источник http://www.codenet.ru
Перевод: fox++
на основе статьи и скриптов: http://tutorials.programmingsite.co.uk.
Tags:
MySQL,
php
Март 13, 2008
1 Вступление
Все большую популярность среди пользователей интернета приобретает ведение личных онлайновых дневников. Существует множество сайтов, которые позволяют публиковать свои откровения в интернете легко и непринужденно, без какого-либо знания специальных языков программирования и разметки гипертекста. Один из таких сайтов - LiveJournal (www.livejournal.com), интернет-сервис, где каждый желающий может завести себе личный дневник.
2 Задача
Создать программный комплекс, который позволит интегрировать журнал из Livejournal в персональный сайт, предусмотреть возможность отображения постов и комментариев с использованием предустановленного шаблона.
3 Существующие решения
Существует несколько методов решения данной задачи, которые описаны непосредственно на сайте LiveJournal (http://www.livejournal.com/developer/embedding.bml?method=all):
1. Самый простой путь встроить ваш журнал в персональный сайт - это вставить JavaScript в HTML-код страницы. В браузерах, где JavaScript отключен, возможно отображение ссылки на журнал:
<script language=”JavaScript”
src=”http://www.livejournal.com/customview.cgi?username=username&styleid=101&enc=js” mce_src=”http://www.livejournal.com/customview.cgi?username=username&styleid=101&enc=js”>
<noscript><a href=”http://username.livejournal.com/” mce_href=”http://username.livejournal.com/”>View
my LiveJournal</a></noscript>
</script>
2. Встраивание Livejournal при помощи фреймов. Данный метод будет работать только в том случае, если браузер поддерживает отображение фрейм-структур:
<center>
<iframe name=”livejournal”
src=”http://www.livejournal.com/users/username/” mce_src=”http://www.livejournal.com/users/username/”
frameborder=”0″
scrolling=”auto” height=”400″ width=”460″>
<a href=”http://username.livejournal.com/” mce_href=”http://username.livejournal.com/”>View my LiveJournal</a>
</iframe>
</center>
3. Интеграция при помощи CGI-скриптов или PHP. Средствами языка программирования производится чтение данных с удаленного ресурса и затем последующий вывод в браузер без обработки информации:
<?php
$fp = fsockopen(”www.livejournal.com”, 80, &$errno, &$errstr, 30);
if($fp) {
fputs($fp,”GET /customview.cgi?”.
“username=username&styleid=101 HTTP/1.0\n\n”);
while(!feof($fp)) {
echo fgets($fp,128);
}
fclose($fp);
}
?>
Очевидное преимущество всех существующих решений - простота реализации. Подобная интеграция займет не более 10 минут с минимальным увеличением скорости загрузки страницы.
Недостатки: все вышеописанные способы интеграции доступны только для пользователей с платными экаунтами. В случае же их модификации под бесплатные журналы на вашей странице появится реклама которую необходимо будет дополнительно фильтровать. Так же появляется проблема несоответствия дизайна станицы и встраиваемого журнала. Вывод: ни один из способов внедрения от Livejournal не предоставляет гибкости в настройках отображения записей и комментариев журнала.
4 Решение
Суть предлагаемого метода - парсинг записей и комментариев журнала Livejournal и последующий вывод результатов с использованием предустановленного шаблона.
5 Используемые технологии
PHP, MYSQL, RSS, Sockets
6 Кросс-браузерность
Internet Explorer 5.0,6.0,7.0; FireFox 1.0,2.0; Opera 9.0; Safari 2.0;
7 Входные и выходные параметры
Входные параметры:
$user - имя пользователя Livejournal, журнал которого будет интегрироватся в персональный сайт;
$postCount - количество записей, отображаемых на странице;
Выходные параметры:
Структура данных, содержащая в себе записи и комментарии журнала Livejournal.
Фрагмент HTML-кода, отображающий записи и комментарии журнала Livejournal с использованием предустановленного шаблона.
8 Реализация
Для начала нам необходимо получить ленту записей (постов) пользователя. К счастью Livejournal выдает по RSS-каналу всю необходимую информацию о дневнике и последних 25 записях. Ниже приведен фрагмент RSS-потока одного из журналов
<?xml version=’1.0′ encoding=’utf-8′ ?>
<rss version=’2.0′ xmlns:lj=’http://www.livejournal.org/rss/lj/1.0/’>
<channel>
<title>второе, что пришло в го…</title>
<link>http://shtepka.livejournal.com/</link>
<description>второе, что пришло в го… - LiveJournal.com</description>
<lastBuildDate>Thu, 24 May 2007 12:26:15 GMT</lastBuildDate>
<generator>LiveJournal / LiveJournal.com</generator>
<image>
<url>http://userpic.livejournal.com/61827888/6441793</url>
<title>второе, что пришло в го…</title>
<link>http://shtepka.livejournal.com/</link>
<width>100</width>
<height>100</height>
</image>
<item>
<guid isPermaLink=’true’>http://shtepka.livejournal.com/196544.html</guid>
<pubDate>Thu, 24 May 2007 12:26:15 GMT</pubDate>
<title>Вопрос</title>
<link>http://shtepka.livejournal.com/196544.html</link>
<description>В Windows нельзя создать файл или папку под названием
“Con”, ибо у Билла Гейтса в детстве была прозвище “Con”
то есть “ботаник”. И он постарался чтобы в его системе отсутствовали
такие файлы и папки.<br /><br />есть ли этому РАУМНОЕ объяснение?</description>
<comments>http://shtepka.livejournal.com/196544.html</comments>
<lj:security>public</lj:security>
</item>
<item>
<guid isPermaLink=’true’>http://shtepka.livejournal.com/196104.html</guid>
<pubDate>Wed, 23 May 2007 13:12:18 GMT</pubDate>
<title>Ветер знает, где меня искать!</title>
<link>http://shtepka.livejournal.com/196104.html</link>
<description>На обеденном перерыве пускали змея с мальчиком Феем или мальчиком
Тимой, ну<br />вообщем с Тимофеем из Мира Детства!!! Мы шли на речку,
несли змея в руках с катушкой, очень похож он на удочки, а у прохожих возникал
вопрос при виде змея и нас: “Что ловить будете?”,- а мы хором
думали: “Ветер”.<br />Тима поймал ветер, а ветер поймал змея,
а змей поймал Тиму и мы понеслись по мосту над рекой вниз, расправив руки и
крылья, а под нами летали чайки, именно под нами и именно ЧАЙКИ, оказывается
эти птицы бывают и в Москве, только надо очень хотеть их увидеть и услышать.</description>
<comments>http://shtepka.livejournal.com/196104.html</comments>
<lj:security>public</lj:security>
</item>
<item>
<guid isPermaLink=’true’>http://shtepka.livejournal.com/195985.html</guid>
<pubDate>Tue, 22 May 2007 12:52:17 GMT</pubDate>
<title>игра слов</title>
<link>http://shtepka.livejournal.com/195985.html</link>
<description>Пойду домой… дойду - помой!<br /><span class=’ljuser’
lj:user=’zero_result’ style=’white-space: nowrap;’><a
href=’http://zero-result.livejournal.com/profile’><img
src=’http://stat.livejournal.com/img/userinfo.gif’ alt=’[info]’
width=’17′ height=’17′ style=’vertical-align: bottom;
border: 0;’ /></a><a href=’http://zero-result.livejournal.com/
‘><b>zero_result<
/b></a></span></description>
<comments>http://shtepka.livejournal.com/195985.html</comments>
<category>слова</category>
<lj:security>public</lj:security>
</item>
:
</channel>
</rss>
Поскольку одним из самых распространенных языков написания скриптов в сети является PHP, мы воспользуемся именно этим интерпретатором для интегрирования дневника в сайт.
Широко распространенных способов обработки XML-документов существует два - Event-based APIs и Document Object Model (DOM) APIs. В PHP стандартная поддержка XML организована с помощью Event-based API (основана на событиях).
Сперва создадим класс RSSParser, внутри которого будет выполняться вся работа по разбору XML. После создания класса, получим RSS-данные от сервиса LiveJournal и инициализируем обработчик XML, который будет использовать для событийной обработки (Event-based API) класс RSSParser. Код класса RSSParser приведен ниже:
//RSSParser class
class RSSParser
{
var $postCurrentNumb = 0;
var $postCount = 0;
var $insideItem = false;
var $tag = “”;
var $maxPostSize = 999999;
var $imageURL = “”;
var $lastBuildDate = “”;
var $lbd = “”;
var $userPic = “”;
var $title = “”;
var $dt = “”;
var $text = “”;
var $category = “”;
var $comments = “”;
var $lj = array();
function startElement($parser, $tagName, $attrs)
{
if($this->insideItem)
{
$this->tag = $tagName;
}
elseif($tagName == “ITEM”)
{
$this->insideItem = true;
}
elseif($tagName == “IMAGE”)
{
$this->insideItem = true;
}
elseif($tagName == “LASTBUILDDATE”)
{
$this->tag = $tagName;
$this->insideItem = false;
}
}
function endElement($parser, $tagName)
{
if($tagName == “IMAGE”)
{
$this->userPic=$this->imageURL;
$this->title = “”;
$this->insideItem = false;
}
if($tagName == “ITEM”)
{
if ($this->postCurrentNumb<$this->postCount)
{
$this->lj[$this->postCurrentNumb][title]=UtoW($this->title);
$this->text=UtoW($this->text);
if (strlen($this->text)>$this->maxPostSize)
$this->text=substr($this->text,0,$this->maxPostSize);
$this->lj[$this->postCurrentNumb][text]=$this->text;
$this->lj[$this->postCurrentNumb][comments]=trim($this->comments);
$this->lj[$this->postCurrentNumb][tag]=UtoW($this->category);
$this->lj[$this->postCurrentNumb][dt]=dateConvert($this->dt);
}
$this->title = “”;
$this->text= “”;
$this->comments = “”;
$this->category = “”;
$this->dt = “”;
$this->postCurrentNumb++;
$this->insideItem = false;
}
if($tagName == “LASTBUILDDATE”)
{
$this->lbd=dateConvert($this->lastBuildDate);
$this->insideItem = false;
}
}
function characterData($parser, $data)
{
if($this->insideItem)
{
switch($this->tag)
{
case “TITLE”: $this->title .= $data; break;
case “DESCRIPTION”: $this->text .= $data; break;
case “COMMENTS”: $this->comments .= $data; break;
case “CATEGORY”: $this->category .= $data; break;
case “PUBDATE”: $this->dt .= $data; break;
case “URL”: $this->imageURL .= $data; break;
}
}
else
{
switch($this->tag)
{
case “LASTBUILDDATE”: $this->lastBuildDate .= $data; break;
}
}
}
}
Стоит учитывать, что чаще всего XML-документы хранятся в Unicode кодировке UTF-8, а в рунете наиболее часто используемой кодировкой является Windows-1251, т.е. приходится решать проблему перекодировки.
Для перекодировки используется функция UtoW()
//convert Unicode to Windows-1251
function UtoW($str)
{
return (mb_convert_encoding($str,”windows-1251″,”UTF-8″));
}
Кроме того, для переформатирования даты используется функция dateConvert()
//convert date
function dateConvert($str)
{
return (date(”Y-m-d H:i:s”, strtotime($str)));
}
Теперь создадим функцию, которая будет выполнять инициализацию обработчика RSS и его запуск.
//generate posts
function generatePostData($postCount)
{
$this->postsCount=$postCount;
$xml_parser=xml_parser_create(”UTF-8″);
$rss_parser=new RSSParser();
$rss_parser->postCount=$this->postsCount;
$rss_parser->maxPostSize=$this->maxPostSize;
xml_set_object($xml_parser, &$rss_parser);
xml_set_element_handler($xml_parser, “startElement”, “endElement”);
xml_set_character_data_handler($xml_parser, “characterData”);
$fp = fopen(”http://”.$this->user.”.livejournal.com/data/rss”, “r”)
or die(”Error reading RSS data from http://”.$this->user.”.livejournal.com/data/rss !”);
while(($data = fread($fp, 4096)) && ($rss_parser->postCurrentNumb<$rss_parser->postCount))
{
xml_parse($xml_parser, $data, feof($fp))
or die(”Error parsing RSS data from http://”.$this->user.”.
livejournal.com/data/rss !”);
}
fclose($fp);
xml_parser_free($xml_parser);
$this->posts=$rss_parser->lj;
$this->userPic=$rss_parser->userPic;
$this->lastBuildDate=$rss_parser->lbd;
if ($this->delcuts)
$this->clearLivejournalCuts();
}
Данная функция является одним из методов класса Livejournal. Параметр $this->postsCount определяет количество записей, которые будут считаны, $this->maxPostSize - максимальный размер поста в байтах. Как результат выполнения функции generatePostData() мы получаем двухмерный массив $this->posts, в котором находятся записи журнала разбитые на элементы: Заголовок, Дата, Текст, Ссылка на комментарии.
Следующий шаг - получение комментариев для каждой из записей. Все было бы так же просто, если Livejournal выдавал ленту комментариев по RSS. К сожалению, такой услуги нет ни для платных, ни для бесплатных экаунтов. Комментарии придется получать при помощи HTML-парсера.
Существует две системы стилей (http://www.livejournal.com/customize/) Livejournal при помощи которых происходит отображение журналов - S1 (для пользователей, знакомых с CSS и HTML) и S2 (для остальных). Кроме того, существует несколько стилей для просмотра журналов Livejournal (http://www.livejournal.com/manage/settings/): Horizon, XCalibur, Dystopia, Lynx. Самым “легким” из них является Lynx. Именно в этом стиле мы будем считывать страницы с комментариями пользователей. Кроме того, это позволит нам написать универсальный парсер для всех стилевых отображений журналов.
Метод getSocketData() считывает HTML-данные со страницы с комментариями. Для того, чтоб использовать стиль Lynx необходимо в конце адресной строки дописать параметр “?format=light”.
//get socket data
function getSocketData($host,$request)
{
if ($fp = fsockopen($host, 80, $errno, $errstr, 5))
{
fputs($fp,$request);
$data=”";
while(!feof($fp))
$data.=fgets($fp,2048);
fclose($fp);
}
return ($data);
}
//get livejournal comments
function getLJCommentsSock($ljUser, $commentsId)
{
$commentsPath=$commentsId.”.html?format=light”;
$host=”livejournal.com”;
$commonRequest= “Accept: */*”.”\r\n”.
“Accept-Language: ru”.”\r\n”.
“User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT)”.”\r\n”.
“Connection: Keep-Alive”.”\r\n”;
// Get request
$request= “GET /”.$commentsPath.” HTTP/1.0″.”\r\n”.
$commonRequest.
“Cookie: “.$cookie.”\r\n”.
“Host: “.$ljUser.”.livejournal.com\r\n”.”\r\n”.
“Pragma: no-cache”.”\r\n”;
$data=$this->getSocketData($host, $request);
$temp1=strpos($data,”/><a name=’”)+2;
$temp2=strpos($data,”<hr />”,$temp1);
if ($temp1 && $temp2)
$data = substr($data, $temp1, $temp2-$temp1);
else
$data=”";
$this->commentsData=UtoW($data);
}
Необходимый для нас блок информации находится между фрагментами текста “/><a name=’” и “<hr />”
Следующий шаг - передаем текстовый блок с комментариями для парсинга методу parseComments(). Обработку HTML выполняет экземпляр класса HtmlParser.
//parse livejournal comments
function parseComments()
{
$j=0;
$prevName=”";
$parser = new HtmlParser($this->commentsData);
while ($parser->parse())
{
if ($parser->iNodeName==”span” && $parser->iNodeType == NODE_TYPE_ELEMENT)
{
$attrValues = $parser->iNodeAttributes;
$attrNames = array_keys($attrValues);
$size = count($attrNames);
for ($i = 0; $i < $size; $i++)
{
$name = $attrNames[$i];
if ($attrNames[$i]==”lj:user”)
$this->comments[$j][user]=UtoW($attrValues[$name]);
}
};
if ($parser->iNodeName==”Text” && ($parser->iNodeType ==
NODE_TYPE_TEXT || $parser->iNodeType == NODE_TYPE_COMMENT))
{
if (strpos($parser->iNodeValue,”UTC”))
$this->comments[$j][dt]=dateConvert($parser->iNodeValue);
if ($prevName==”td” && trim($parser->iNodeValue))
$this->comments[$j][text].=$parser->iNodeValue;
if ($prevName==”br”)
$this->comments[$j][text].=”<BR>”.$parser->iNodeValue;
if ($prevName==”b” && !$this->comments[$j][user] && $parser->iNodeValue!=”(”)
$this->comments[$j][subj]=$parser->iNodeValue;
};
if ($parser->iNodeName==”img” && $parser->iNodeType == NODE_TYPE_ELEMENT)
{
$attrValues = $parser->iNodeAttributes;
$attrNames = array_keys($attrValues);
$size = count($attrNames);
for ($i = 0; $i < $size; $i++)
{
$name = $attrNames[$i];
if ($name==”src” && strpos($attrValues[$name],”userpic”))
$this->comments[$j][pic]=$attrValues[$name];
}
}
if ($parser->iNodeName==”a” && $parser->iNodeType == NODE_TYPE_ELEMENT)
{
$attrValues = $parser->iNodeAttributes;
$attrNames = array_keys($attrValues);
$size = count($attrNames);
for ($i = 0; $i < $size; $i++)
{
$name = $attrNames[$i];
if ($name==”href” && strpos($attrValues[$name],”replyto”))
{
$this->comments[$j][reply]=$attrValues[$name];
$j++;
$this->comments[$j][text]=”";
}
}
}
$prevName=$parser->iNodeName;
};
$this->commentsCount=$j;
}
Как результат выполнения функции parseComments() мы получаем двухмерный массив $this->comments, в котором находятся комментарии к текущей записи журнала разбитые на элементы: Имя пользователя, Адрес юзерпика, Заголовок, Дата, Текст, Ссылка на ответ.
В качестве дополнительных параметров можно установить максимальный размер записи (var $maxPostSize = 999999) и возможность удаления катов (var $delcuts = true);
9 Пример работы
В качестве примера работы программного комплекса создадим произвольный шаблон для отображения записей и комментариев журнала Livejournal.
<?php
require_once(’inc/function.inc.php’);
require_once(’class/rssparser.class.php’);
require_once(’class/htmlparser.class.php’);
require_once(’class/lj.class.php’);
connect_to_db($host,$login,$pass,$db_name);
$user=”shtepka”;
$postCount=10;
$lj=new Livejournal($user);
if ($_GET[comment])
{
$lj->generateCommentsData($lj->user, $_GET[comment]);
$query=”SELECT value FROM info WHERE name=’userpic’ OR name=’lastbuilddate’ ORDER BY name ASC”;
$result=mysql_query($query);
$row=mysql_fetch_array($result);
$lbd=$row[value];
$row=mysql_fetch_array($result);
$userpic=$row[value];
$postOut=’<table width=”600px” align=”center” cellpadding=”10″>’;
$query=”SELECT text, textfull FROM posts WHERE commentid=’$_GET[comment]’”;
$result=mysql_query($query);
$row=mysql_fetch_array($result);
if ($row[textfull])
$row[text]=$row[textfull];
$postOut.=’<tr><td><table width=”100%” style=”border: 1px dashed #000000;
background: url(’/img/back2.jpg’);”><tr>’;
$postOut.=’<td style=”padding: 20px” valign=”top”><img src=”‘.$userpic.’” mce_src=”‘.$userpic.’”></td>’;
$postOut.=’<td width=”100%” style=”padding: 20px 20px 20px 0px”><p class=”text”>’.
str_replace(”’”,”‘”,$row[text]).’</p></td>’;
$postOut.=’</tr><tr><td colspan=”2″ align=”right” style=”padding-right: 10px”><A href=”http://’.
$lj->user.’.livejournal.com/’.$_GET[comment].’.html?mode=reply”
target=”_blank”>reply…</A></td></tr></table></td></tr>’;
for ($i=0; $i<$lj->commentsCount; $i++)
{
$imgOut = ($lj->comments[$i][pic]) ? ‘<img src=”‘.$lj->comments[$i][pic].’” mce_src=”‘.$lj->comments[$i][pic].’”><BR>
<img src=”/img/lj.gif” mce_src=”/img/lj.gif” hspace=”2″>
<A href=”http://’.$lj->comments[$i][user].’.livejournal.com” mce_href=”http://’.$lj->comments[$i][user].’.livejournal.com”
target=”_blank” style=”line-height: 0″><b>’.$lj->comments[$i][user].’</b>
</A>’ : ‘<b>anonymous</b>’;
$postOut.=’<tr><td><table width=”100%” style=”border: 1px dashed #000000;
background: url(’/img/back.gif’); height: 100%”>’;
$postOut.=’<tr><td style=”padding: 20px” align=”center”>’.$imgOut.’</td><td
width=”100%” valign=”top” style=”height: 100%”
><table width=”100%” style=”height: 100%”>’;
$dateOut=substr($lj->comments[$i][dt],8,2).”-”.
substr($lj->comments[$i][dt],5,2).”-”.substr($lj->comments[$i][dt],0,4).” |
“.substr($lj->comments[$i][dt],11,5);
$postOut.=’<tr><td align=”right” style=”padding: 4px”>
<span class=”dt”>[’.$dateOut.’]</span></td></tr>’;
$postOut.=’<tr><td colspan=”2″ align=”left”><span class=”title”>’.
$lj->comments[$i][subj].’</span></td></tr>’;
$postOut.=’<tr><td colspan=”2″ style=”padding: 10px 10px 10px 0px;
height: 100%” valign=”top”><p class=”text”>’.$lj->comments[$i][text].’</p></td></tr>’;
$postOut.=’<tr><td colspan=”2″ align=”right” style=”padding-right:
10px”><A href=”‘.$lj->comments[$i][reply].’” mce_href=”‘.$lj->comments[$i][reply].’”>reply…</A></td></tr>’;
$postOut.=’</table></td></table></td></tr>’;
}
$postOut.=’<tr><td><table width=”100%” style=”border: 1px dashed #000000;
background: url(’/img/back2.jpg’);”><tr>’;
$postOut.=’<td style=”padding: 10px” align=”center”><A href=”/’.$lj->path.’
/”><<< back</A></td></tr></table></td></tr>’;
$postOut.=’</table>’;
}
else
{
$lj->generatePostData($postCount);
$query=”UPDATE info SET value=’”.$lj->userPic.”‘ WHERE name=’userpic’”;
$result=mysql_query($query);
$query=”UPDATE info SET value=’”.$lj->lastBuildDate.”‘ WHERE name=’lastbuilddate’”;
$result=mysql_query($query);
$postOut=’<table width=”600px” align=”center” cellpadding=”10″>’;
$postOut.=’<tr><td><table width=”100%” style=”border: 1px dashed #000000;
background: url(’/img/back2.jpg’);”><tr>’;
$postOut.=’<td style=”padding: 20px”><img src=”‘.$lj->userPic.’” mce_src=”‘.$lj->userPic.’”></td>’;
$postOut.=’<td width=”100%” style=”padding: 20px”><b>’.$user.’’s livejournal</b></td>’;
$postOut.=’</tr></table></td></tr>’;
for ($i=0; $i<$lj->postsCount; $i++)
{
$commentId=$lj->getCommentId($lj->posts[$i][comments]);
$query=”SELECT id FROM posts WHERE commentid=’$commentId’”;
$result=mysql_query($query);
$row_count=mysql_num_rows($result);
if ($row_count==0)
$query=”INSERT INTO posts (commentid, text, textfull) VALUES
(’$commentId’, ‘”.str_replace(”‘”,’’’,$lj->posts[$i][text]).”‘ ,
‘”.str_replace(”‘”,’’’,$lj->posts[$i][textfull]).”‘)”;
else
$query=”UPDATE posts SET text=’”.str_replace(”‘”,’’’,
$lj->posts[$i][text]).”‘, textfull=’”.str_replace(”‘”,’’’,
$lj->posts[$i][textfull]).”‘ WHERE commentid=’$commentId’”;
$result=mysql_query($query);
$postOut.=’<tr><td><table width=”100%” style=”border: 1px dashed
#000000; background: url(’/img/back.gif’);”>’;
$dateOut=substr($lj->posts[$i][dt],8,2).”-”.substr($lj->posts[$i][dt],5,2).
“-”.substr($lj->posts[$i][dt],0,4).” | “.substr($lj->posts[$i][dt],11,5);
$postOut.=’<tr><td align=”right” style=”padding: 4px”>
<span class=”dt”>[’.$dateOut.’]</span></td></tr>’;
$postOut.=’<tr><td colspan=”2″ align=”center”><span class=”title”>’.
$lj->posts[$i][title].’</span></td></tr>’;
$postOut.=’<tr><td colspan=”2″ style=”padding: 10px”><p class=”text”>’.
$lj->posts[$i][text].’</p></td></tr>’;
$postOut.=’<tr><td colspan=”2″ align=”right” style=”padding-right: 10px”>
<A href=”/’.$lj->path.’/’.$commentId.’/” mce_href=”/’.$lj->path.’/’.$commentId.’/”>comments…</A></td></tr>’;
$postOut.=’</table></td></tr>’;
}
$postOut.=’</table>’;
}
?>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>Livejournal Integrator Sample</title>
<meta http-equiv=”Content-Type” content=”text/html; charset=windows-1251″>
<style type=”text/css”>
html, body
{
margin: 0;
padding: 0;
height: 100%;
background: url(’/img/back.jpg’);
}
.dt
{
font: 13px Arial;
}
.text
{
font: 14px Tahoma;
color: #FFF;
}
.title
{
font: 14px Tahoma;
}
a
{
color: #1C1FA7;
}
a:hover
{
color: #1C1FA7;
text-decoration: none;
}
</style>
</head>
<body>
<?=$postOut?>
</body>
</html>
Для просмотра онлайн-демонстрации работы скрипта кликните по ссылке - Демонстрация
10 Ссылки
Обработка LiveJournal RSS стандартными средствами PHP - http://bikman.ru/texts/techarticle/ljrssphp/
HTML Parser - http://php-html.sourceforge.net/
Livejournal FAQ - http://www.livejournal.com/support/faq.bml
11 Downloads
Исходный код, sql, шаблон: ljembed.zip [21Кб]
Описание: ljembed.pdf [525Кб]
Автор: research[@]zhupanenko.com Andrew Zhupanenko
http://research.zhupanenko.com/livejournal/rus/
май 2007
Source: http://codenet.ru
Tags:
CGI,
php,
rss,
Sockets,
интеграция
Март 12, 2008
Импортирование информации с чужого сайта на свой сайт в свой дизайн. (На примере импортирования прогнозов погоды с Yahoo.)
Добре, господа!
Пример предназначен для тех, кто начинает работать с php, и не только для них.
Результатом работы программы(скрипта) является прогноз погоды на 5 дней для любого, интересующего Вас города, выводимый в виде, который нравится именно Вам, а не дизайнерам сайта-донора.
Информация в таких случаях берется с известных серверов прогноза погоды (где не пишут фразу “запрещено использование информации” и т.п.). В данном случае используется сервер http://weather.yahoo.com/ , на котором есть страницы с погодой для довольно большого количества городов, и практически всегда можно найти если не интересующий Вас город, то ближайший ему и идентичный по погодным условиям.
Это законченный проект, работающий на сайте http://sim-sim.ru в разделе туризма “погода в мире”.
Единственным недостатком является лишь то, что админу приходится вводить в текстовый файл (возможен вариант с mysql, но в том случае мне было проще сделать в файле) название населенного пункта на родном языке и ссылку на страницу с прогнозом погоды на него на сервере Яхо. Но никто за Вас этого делать не будет.
Посему, скрипт состоит из 2-х частей:
1. Файл с администрированием (вводится в первую строку название города, на следующей строке - ссылка). Разбирать работу данной части, думаю, не стоит, комментариев более чем достаточно.
2. Файл с самой программой. Работа программы будет подробно описана ниже.
1. Администрирование.
Выводим на экран форму с паролем pass.
В окне вводятся:
номера
названия
ссылки
Затем, после нажатия на кнопку и проверки пароля, записываем новый список в файл.
<html>
<head>
<title>admin weather</title>
</head>
<body>
<?php
// адрес файла, в котором и будут записываться названия городов со ссылками
$adr=$DOCUMENT_ROOT.”/weather/weather.ini”;
$password=’pass’; // простенькая система авторизации
$eror=’Password eror!’;
$old=file($adr); // читаем то, что сейчас есть в файле
if ($submit) { // проверяем на нажатость кнопки
if ($pass==$password) {
$fp=fopen($adr,”w”);
fwrite ($fp, $ini); // записываем в файл измененные данные
fclose($fp);
$old=file($adr);
}
else {
echo $eror;
}
}
?>
<form method=post action=”<?php echo $PHP_SELF?>”>
// информация, введенная в форму, обрабатывается этим же файлом
password:<input type=text name=pass><br>
inicialisation:<textarea name=”ini” rows=15 cols=60>
<?
for ($i=0; $i<sizeof($old); $i++) {
echo $old[$i], “”; // выводим на экран текущий вариант файла
}
?>
</textarea>
<br>
<input type=submit name=”submit” value=”Enter”>
</form>
</body>
</html>
После ввода информации в файл в виде, получаем:
50
Ларнака
http://weather.yahoo.com/forecast/Larnaca_CY_f.html
51
Пафос
http://weather.yahoo.com/forecast/Paphos_CY_f.html
“44″ - номер города.
“Ларнака” - название города.
“http://weather.yahoo.com/forecast/Larnaca_CY_f.html” - ссылка на погоду в городе Ларнака на Яхе.
Ссылки на города организовываются по принципу:
<a href=http://www.sim-sim.ru/catalogue/weather.php?weather=50>Ларнака</a>
А можно так:
<a href=http://www.sim-sim.ru/catalogue/weather.php?city=Ларнака>Ларнака</a>
Но если город из друх слов, то в пробелах пишем “%20″
В таком случае, номера городов в списке не нужны:
Ларнака http://weather.yahoo.com/forecast/Larnaca_CY_f.html
Пафос http://weather.yahoo.com/forecast/Paphos_CY_f.html
Если у нас не текстовый файл, а mysql, то все проще.
Если кому понадобится вариант с mysql, пишите мне totoeval@mtu-net.ru
2. Программа (собственно, адаптер чужого кода к Вашему сайту).
<table width=100% border=0 cellspacing=0 cellpadding=2 bgcolor=<? echo $brdcolor; ?>>
<tr>
<td>
<br>
<!———————– FORECAST ————————->
<?php
$ini=$DOCUMENT_ROOT.’/weather/weather.ini’;
$region=file($ini); // читаем файл со списком городов-ссылок а массив $region
// определение координат искомого города
for ($i=0; $i<sizeof($region); $i++) {
if (trim($region[$i])==$weather) { // ищем номер города в списке
$city=trim($region[$i+1]); // название города
$adr=trim($region[$i+2]); // адрес страницы
}
else {
}
}
// если у нас передается информация не номером города, а названием, то:
for ($i=0; $i<sizeof($region); $i++) {
$city=str_replace($city,”%20″,” “); // заменяем “%20″ на ” ”
if (trim($region[$i])==$city) { // ищем название города в списке
$adr=trim($region[$i+1]); // адрес страницы
}
else {
}
}
// Входная информация для дальнейшего кода - $adr (адрес страницы
// прогноза погоды для города на сайте http://weather.yahoo.com)
// и $city - название города на родном языке.
// фразы для поиска полезной информации. Оригинал фраз можно обнаружить
// на странице, например, этой,
// проанализировав код. здесь заводим в переменные фразы, по которым будем
// искать нужную информацию (градусы, описания погоды, картинки). Используем
// фразы до и после необходимой информации. Этот блок в будущем придется
// изменять, когда на оригинале (weather.yahoo.com) изменится код страниц.
// На 14 авг. 2001 он работает.
$begin_screen=’FORECAST —-’; // начальная фраза таблицы с прогнозом погоды
$end_screen=’</b></td></tr></table></td></tr></table>’; // последняя фраза таблицы
$post_gradus=’</font></b><’; // после градусов
$before_gradus=’<font size=”3″ face=”arial”>’; // перед градусами
$before_image=’http://us.i1.yimg.com/us.yimg.com/i/we/fc/’; // перед картинкой
$post_image=’.gif’; // после картинки
$before_forecast=’top><font face=”arial” size=”2″>’; // перед фразой погоды
$post_forecast=’</font></td><td>’; // после фразы погоды
// массивы для переводов даты и описаний погоды с английского языка
// на родной (в данном случае использован вариант перевода с
// буржуйского на язык, которым разговаривал Ленин.
$endat=array(” “,”Mon”,”Tue”,”Wed”,”Thu”,”Fri”,”Sat”,”Sun”,
“May”,”Jul”,”Jun”,”Aug”,”Sep”,”Oct”,
“Nov”,”Dec”,”Jan”,”Feb”,”Mar”,”Apr”); // английские даты
$rudat=array(” “,”понедельник”,”вторник”,”среда”,”четверг”,
“пятница”,”суббота”,”воскресенье”,
“мая”,”июля”,”июня”,”августа”,”сентября”,”октября”,”ноября”,
“декабря”,”января”,”февраля”,”марта”,”апреля”); // родные даты
$eng=array(” “,”Thunderstorms”,”Sunny”,”Partly Cloudy”,
“Showers”,”Rain”,”Mostly Cloudy”,”Tstorms”,”Drizzle”,”Cloudy”,
“Flurries”,”Fog”,”High”,”Low”,”Clear”,”Sleet”,”Rain/snow”,
“Snow Showers”,”Mixed Snow”, “Rain”, “Snow”,
“Mixed”); // английские описания погоды
$rus=array(” “,”гроза”,”ясно”,”переменная облачность”,
“ливень”,”дожди”,”облачно”,”гроза”,”изморось”,”пасмурно”,
“снегопад”,”туман”,”день”,”ночь”,”ясно”,”дождь со снегом”,
“дождь со снегом”,”мокрый снег”,”снег с дождем”,”снег”,
“переменно”); // российские аналоги погоды
$brdcolor=’#ffffff’; // цвет рамки таблицы прогноза (белый)
$bgcolor=’#eeeeee’; //цвет фона ячеек таблицы (лучше - цвет фона страницы)
// А Здесь начинается код программы, который изменять не придется.
// занимаемся созданием даты, выводимой на экран.
Функция time() даст нам текущее время. Функцией date(d.” “.M,$t[$i]) получаем дату текущего и последующих 4 дней, а date(D,$t[$i]) дает нам дни недели всех 5 дней. А затем переводим даты с английского языка на родной.
$t=array(4);
$d=array(4);
$dn=array(4);
for ($i=0; $i<=4; $i++) {
$t[$i]=time()+$i*86400;
$d[$i]=date(d.” “.M,$t[$i]);
$dn[$i]=date(D,$t[$i]);
// переводим слова в дате и получаем даты на родном языке
for ($j=0; $j<sizeof($endat); $j++) {
$d[$i]=str_replace($endat[$j],$rudat[$j],$d[$i]);
$dn[$i]=str_replace($endat[$j],$rudat[$j],$dn[$i]);
} // и получаем даты на языке, которым разговаривает наш нонешний Вова
}
// начинаем обрабатывать страницу.
// Выясняем строки начала и конца таблицы с прогнозом погоды.
// Затем, будем работать именно с этим блоком.
$screen=file($adr); // читаем страницу с прогнозом погоды в переменную $screen
for ($i=1; $i<sizeof($screen); $i++) { // обрабатываем строки с первой по последнюю
if (strpos($screen[$i],$begin_screen)==false) { //находим начало таблицы с прогнозом
if (strpos($screen[$i],$end_screen)==false) { //находим ее конец
}
else {$m=$i; // $m - номер последней строки таблицы
}
}
else {$k=$i; // $k - номер первой строки таблицы
}
} // Получаем с $screen[$k] по $screen[$m] - нашу таблицу с погодой.
// Потом будемработать именно с ней, чтобы не было лишних совпадений, и
// быстро работала программа.
// Можно ее выводить в “родном дизайне, но нам это не нравится
// Начинаем искать полезную информацию.
$grad=array(9); // массив из 10 чисел градусов температуры окружающей среды
$zed=0; // счетчик градусов
for ($i=$k; $i<=$m; $i++) { //работаем с градусами
//если нет градусов в строке (если нет фразы, стоящей перед градусами.
if (strpos($screen[$i],$before_gradus,0)==false) {
}
else {// если есть градусы в строке
$string_grad=$screen[$i]; // сохраняем строку с градусами в переменную
// разбиваем строку на массив строк по разделителю, которым
// является фраза перед градусами, таким образом, мы получаем в
// 1 элементе массива строку, которая начинается собственно с
// самого градуса погоды.
$string_grad=explode($before_gradus,$string_grad);
// определяем позицию начала фразы, идущей после градуса.
$end_grad=strpos($string_grad[1],$post_gradus,0);
// первый градус получаем, как подстрока, с 0-го символа до начала фразы после градуса.
$grad[$zed]=substr($string_grad[1],0,$end_grad);
$zed++; // инкремент счетчика градусов
$end_grad=strpos($string_grad[2],$post_gradus,0);
$grad[$zed]=substr($string_grad[2],0,$end_grad); // второй градус
$zed++; // инкремент счетчика градусов
}
} // в результате чего, мы получили все градусы на нашей странице
// переводим фаренгейты в цельсии
for ($i=0; $i<=9; $i++) {$grad[$i]=round(5/9*($grad[$i]-32));
}
// в варианте с забиранием информации со страницы с цельсиями
// эту строку стоит просто закомментировать. Я оставил так, просто каприз.
// переводим фаренгейты в цельсии
// находим слова погоды абсолютно аналогично поиску градусов
$zed=0; // счетчик описаний погоды
for ($i=$k; $i<=$m; $i++) { //работаем с описаниями
//если нет описания погоды в строке
if (strpos($screen[$i],$before_forecast,0)==false) {
}
else {// если есть описания в строке
$string_grad=$screen[$i]; // сохраняем строку с описаниями в переменную
$string_grad=explode($before_forecast,$string_grad);
$end_grad=strpos($string_grad[1],$post_forecast,0);
$forec[$zed]=substr($string_grad[1],0,$end_grad);
// перебираем все варианты слов в описаниях
for ($j=0; $j<sizeof($eng); $j++) {
// переводим слова
$forec[$zed]=str_replace($eng[$j],$rus[$j],$forec[$zed]);
}
$zed++; // инкремент счетчика описаний
}
} // в результате чего, мы получили все описания погоды на нашей странице
$zed=0; // счетчик слов картинок аналогично
for ($i=$k; $i<$m; $i++) { //работаем с картинками
//если нет описания погоды в строке
if (strpos($screen[$i],$before_image,0)==false) {
}
else {// если есть картинки в строке
$string_grad=$screen[$i]; // сохраняем строку с картинками в переменную
$string_grad=explode($before_image,$string_grad);
$end_grad=strpos($string_grad[1],$post_image,0);
$for_img[$zed]=substr($string_grad[1],0,$end_grad);
$zed++; // инкремент счетчика картинок
}
} // в результате чего, мы получили все картинки на нашей странице
?>
// публикуем результаты:
<table width=100% cellpadding=0 cellspacing=0 border=0>
<tr><td width=20% align=center><font size=3><b
// выводим название города, для которого отображается прогноз погоды
><? echo $city;
?></b><br></font></td></tr></table
><table width=100% cellpadding=0 cellspacing=0 border=0>
<tr><?
// выводим на экран ячейки с датами и днями
for ($i=0; $i<=4; $i++) {
echo “<td width=20% align=center><font
size=2><b>$d[$i]<
br>$dn[$i]</b></font></td>”;
}
?></tr
></table
><table width=100% cellpadding=0 cellspacing=0 border=0
bgcolor=<? echo $brdcolor; ?>
><tr><td
><table width=100% cellpadding=2 cellspacing=1 border=0
><tr><?
$zed=0; // счетчик выводимых градусов
// выводим ячейки с прогнозами - картинки, градусы, описания
for ($i=0; $i<=4; $i++) {
$zed1=$zed+1;
echo “<td width=20% align=center bgcolor=$bgcolor
valign=middle><img src=../pic/”.$for_img[$i].”.gif border=0
alt=”.$forec[$i].”><br><br><font
size=2>”.$forec[$i].”</font><br><br><font
size=3><b>”.$grad[$zed].”°C<br>”.$grad[$zed1].”°C</b>
</font></td>”;
$zed=$zed+2;
}
?>
</tr></table>
</td>
</tr>
</table>
Картинки можно привязать к картинкам Яхи (названия аналогичных по погоде совпадают, как в моем случае), а можно сделать массивы соответствия слов-описаний погоды и Ваших картинок. Это по-желанию. Что долговечнее, трудно судить. И фразы-описания они могут изменить, и картинки переименовать. Можно, конечно договориться с админом Яхи, чтобы они не меняли один из этих элементов и отталкиваться от него, но у меня не было его телефона:о)
Теперь программа работает, и ее можно вставлять в свой дизайн.
Вот вариант: вышеупомянутая Ларнака
Если кого заинтересуют “вариации на тему” или возникнут вопросы, прошу писать мне totoeval[@]mtu-net.ru.
А также, пишите все, кто сможет посоветовать другие варианты. Всегда рад критике и возможности повысить свой уровень.
Tags:
import,
parce,
php
Март 11, 2008
На форуме CodeNet.Ru неоднократно задавали вопрос о том, как сделать постраничный вывод на PHP. Я объяснял, что такое LIMIT, и как его использовать в MySql.
Но все время оказывалось, что вопрос касался только навигации по страницам:
Вот универсальная процедура, выводящая такой блок ссылок:
$records - всего записей
$r_start - текущая страница
$URL - адрес, заканчивающийся на “=”
$inpage - записей на страницу
<?
function LeftRight($records,$r_start,$URL,$inpage) {
$str=”";
if ($records<=$inpage) return;
if ($r_start!=0) {
$str.=”<a href=”.$URL.”0><<</a> “;
$str.=”<a href=$URL”.($r_start-1).”><</a> “;
}
else $str.=”<< < “;
if ($r_start==0) {$sstart=$r_start-0;$send=$r_start+10;}
if ($r_start==1) {$sstart=$r_start-1;$send=$r_start+9;}
if ($r_start==2) {$sstart=$r_start-2;$send=$r_start+8;}
if ($r_start==3) {$sstart=$r_start-3;$send=$r_start+7;}
if ($r_start==4) {$sstart=$r_start-4;$send=$r_start+6;}
if ($r_start>=5) {$sstart=$r_start-5;$send=$r_start+5;}
if ($send*$inpage>$records) $send=$records/$inpage;
if ($sstart<0) $sstart=0;
if ($records%$inpage==0) $add=0; else $add=1;
for ($i=$sstart;$i<$send;$i++) {
if ($i==$r_start) $str.=” <B>”.($i+1).”/”.(intval($records/$inpage)+$add).”</B> | “;
else $str.=”<a href=$URL”.($i).”><U><B>”.($i+1).”</B></U></a> | “;
}
if ($r_start+(1-$add)<intval($records/$inpage)) {
$str.=” <a href=$URL”.($r_start+1).”>></a>”;
$str.=” <a href=$URL”.(intval($records/$inpage)-(1-$add)).”>>></a>”;
}
else $str.=” > >>”;
return($str);
}
// Пример вызова
print “<center>”.LeftRight(567,43,”index.htm?start=”,20).”</center>”;
?>
Источник / обсудить http://www.codenet.ru/
Tags:
LIMIT,
MySQL,
php,
постраничный вывод
Март 9, 2008
О защите e-mail адресов от сканирования спам-роботами в Интернете писалось немало. Скорее наоборот, очень много. Однако все способы сводятся в основном к одному – кодирование адресов таким образом, чтобы спам-роботы не смогли его распознать, а для человека это не составило бы труда. Чаще всего мне встречались такие варианты: name[at]server.ru, name(a)server.ru и даже name(Shift+2)server.ru. Вариант, конечно, тоже хороший, но спам-роботы быстро «учатся». Опытный интернетчик догадается, что [at] нужно заменить на «@», но некоторые люди копируют адрес name[at]server.ru прямо в почтовую программу, а потом удивляются – почему письма не доходят
Вначале проблема спама не так беспокоила меня, пока однажды горы рекламных писем не посыпались на мой ящик. В этот же день нашёл простой выход: «собачку» можно показывать как картинку, всё остальное – как символы. Внешне – обычный e-mail, а скопировать не получится.
Предлагаю вашему вниманию простой и доступный способ публикации e-mail адресов без опасения, что его просканирует спам-робот. Хотя, возможно, и просканирует, но для этого он должен обладать искусственным интеллектом
Требования: наличие PHP с библиотекой GD. Многие наверно догадались, что мы будем делать с адресами. Мы будем их… рисовать! Назовём наш скрипт email.php. Алгоритм достаточно прост:
Одним из способов скрипт получает на вход е-mail адрес:
Получим адрес автора из базы данных, указав например id статьи или записи в гостевой книге (каталоге файлов, ссылок и т.д.).
Передадим адрес скрипту в явном виде: email. php?adr=name@server.ru (но это чревато тем, что e-mail всё-таки «засвечен»). Такая защита равносильна её отсутсвию.
Передадим адрес скрипту в неявном виде: email. php?name=name&server=server. ru
Передадим адрес скрипту в зашифрованном виде: email.php?adr=anzr@freire.eh (воспользовавшись функцией str_rot13() , которая смещает коды всех латинских букв вверх на 13).
Скрипт расшифровывает данные, генерирует картинку с изображением адреса и возвращает её браузеру.
Для начала рассмотрим процесс «рисования», так как он впоследствии останется неизменным:
$size = 2;
$im = imagecreate(imagefontwidth($size)*strlen($adr), imagefontheight($size));
Здесь нет ничего особенного. Исходные данные: $adr – адрес, который надо «нарисовать», $size – размер шрифта на картинке (легко подбирается по размеру и «жирности», внешне похож на Tahoma, Verdana, Arial). Функция imagecreate() создаёт картинку и помещает указатель на неё в переменную $im. Однако для этого вычислим размеры будущей картинки, исходя из размера шрифта и количества символов в строке.
imagefontwidth($size) – определяет ширину одного символа при размере $size. Умножим её на длину строки strlen($adr) и получим ширину будущей картинки. Высота картинки вычисляется функцией imagefontheight($size). Добавляем в палитру изображения цвета фона и надписи (соответственно белый и красный)
$bg = imagecolorallocate($im, 255, 255, 255);
$black = imagecolorallocate($im, 0×00, 0×00, 0×00);
Если цвет фона вашего сайта не белый, сделаем так:
imagecolortransparent($im,$bg);
Цвет $bg в палитре станет прозрачным, поэтому $bg может быть любым цветом. Цвет символов задаётся в соответствии с вашими потребностями.
Делаем надпись:
imagestring($im, $size, 0, 0, $adr, $black);
И выводим картинку :
header(’Content-type: image/png’);
imagepng($im);
Теперь рассмотрим, какими способами можно получить e-mail для обработки. В таблице помещены варианты адресации и соответствующие фрагменты кода.
Извлечение e-mail из базы данных MySQL.
email.php?id=1 mysql_connect(”localhost”, “username”, “password”);
mysql_select_db(”database”);
$id = abs(intval($id));
$query = “SELECT email FROM table WHERE table_id=’$id’”;
$result = mysql_query ($query);
$f = mysql_fetch_array($result);
if (empty($f[email])) $adr = “n/a”;
else $adr = $f[email];
Прямая передача адреса
email. php?adr=name@server.ru
дополнительный код не нужен
Раздельная передача адреса
email.php?name=name&
server=server.ru $adr = $name.”@”.$server
Передача закодированного адреса
email. php?adr=anzr@freire.eh
(адрес был закодирован функцией str_ rot13(), она же раскодирует его, потому что букв в латинском алфавите всего 26, а смещение каждый раз на 13)
$adr = str_rot13($adr);
Вот весь скрипт целиком (добавьте нужный вам способ получения адреса):
<?php
$size = 2;
$im = imagecreate(imagefontwidth($size)*strlen($adr), imagefontheight($size));
$bg = imagecolorallocate($im, 255, 255, 255);
$black = imagecolorallocate($im, 0×00, 0×00, 0×00);
imagecolortransparent($im,$bg);
imagestring($im, $size, 0, 0, $adr, $black);
header(’Content-type: image/png’);
imagepng($im);
?>
Вывод картинки вместо реального e-mail делается примерно так:
Ваш e-mail: <a href=”mailto:name@server.ru” mce_href=”mailto:name@server.ru”>name@server.ru</a>
Ваш e-mail: <img src=”email.php?adr=name@server.ru” mce_src=”email.php?adr=name@server.ru”>
Теперь, когда мы знаем, как заменять e-mail адреса, можно написать функцию, которая будет производить такие замены в заданном тексте при помощи регулярных выражений. Это может потребоваться при тотальной фильтрации всего вывода, пакетной обработке HTML-файлов, выводе информации из БД.
Разделим адрес на 3 части:
(Имя)@(Домен).(Зона)
И того уже имеем паттерн типа:
(.*)@(.*).(.*)
Под эту формулу подойдут миллиарды выражений, но мы идём дальше по спецификации e-mail адреса. Имя пользователя должно состоять из “a-z”, “A-Z”, “0-9″, а также “.”, “_” и “-”. Если не ошибаюсь, строгих правил по этому поводу никогда не было, где-то разрешены и другие символы, это основа. “A-Z” добавлено на случай если пользователь любит прописывать своё мыло “покруче”. Длина имени от 2-х до 256-ти символов (см. RFC). Дальше домен, тоже самое – “a-z”, “A-Z”, “0-9″, “.”, “-” и всё! Длина доменного имени та же, единственная загвоздка – если мыло находится не в домене второго уровня, а, скажем, третьего или даже четвёртого. Тут снова в RFC кидаться надо, поскольку и сама служба DNS не лишена “ответа” типа HTTP 414 :). Длина домена не может превышать 256 символов, но вот входят ли в это число поддомены – не помню. Зона – здесь просто “a-z”, “A-Z” от 2-х до 4-х символов.
В итоге получается что-то такое:
([a-zA-Z0-9|.|-|_]{2,256})@(([a-zA-Z0-9|.|-]{2,256}).([a-z]{2,4}))
Можно проверить, не начинаются ли (или заканчиваются) подвыражения \\1, \\2 символами типа “.” или “-”, дальше – на что фантазии хватает. Используя вычисленное регулярное выражение (невольно вспоминаются лекции по матанализу), напишем функцию, которая будет заменять все адреса e-mail в строке на тэг IMG и возвращать изменённую строку. В качестве функции замены я использовал preg_replace() с синтаксисом Perl RegExp.
email_ replace. php
<?php
// функция замены e-mail адресов
function email_replace($text)
{
$exp = ‘/([a-zA-Z0-9|.|-|_]{2,256})@(([a-zA-Z0-9|.|-]{2,256}).([a-z]{2,4}))/’;
return preg_replace($exp, ‘<img src=email.php?name=\\1&server=\\2 border=0>’, $text);
}
// исходная строка со множеством e-mail адресов
$text =<<<END
По всем вопросам пишите на мои e-mail:
f_mulder@list.ru,
fox58@list.ru,
admin@foxweb.net.ru,
kurepin@sura.ru,
fox58@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com
(это реальный адрес!!!)”;
END;
echo $text; // исходная строка
echo “<br>”;
echo email_replace($text); // изменённая строка
?>
Если вы нашли в этой статье спорные или непонятные моменты, жду ваших комментариев. Протестировать и скачать все варианты скриптов можно здесь: http://foxweb.net.ru/test/email/
Источник http://foxweb.net.ru
Tags:
email,
image,
php
Март 7, 2008
Ответить на этот вопрос однозначно нельзя. Дело в том, что Perl существенно отличается от PHP. Первый - довольно продвинутый и мощный интерпретируемый язык, а второй создавался исключительно для работы в вебе. Поэтому, когда кто-то Вам скажет, что PHP круче Перла, не верьте. Эти языки сравнивают в неравных условиях.
PHP легче - это факт. При запуске демона httpd (веб-сервер Апаче) он сразу же подгружает интерпретатор. Поэтому при запросах к скриптам нет бессмысленных и ресурсоемких загрузок процессора. Скрипт сразу же идет на выполнение, поэтому и говорят, что PHP очень шустрый. И хотя при использовании PHP тратится дополнительная память (каждый потомок хватает модуль php), при большом количестве обращений мы получаем выигрыш в производительности.
Процесс выполнения Perl-скриптов в стандартном виде несколько иной. При обращении к такому скрипту, Апаче загружает интерпретатор, а тот выполняет код. При этом тратится довольно много памяти. Не менее 2 мегабайт на каждый процесс. Обычно от 4 до 6 мегабайт. Выходит, что каждый раз при обращении к скрипту сервер производит загрузку тяжелой программы - интерпретатора. Но это не значит, что Перл-скрипты выполняются медленнее. Это значит, что при работе с Perl сервер испытывает большую нагрузку.
При больших нагрузках получаем следующее: сервер, использующий PHP легче справляется с запросами и может обработать больше клиентов. В то время сервер с сайтами на Perl будет подтормаживать.
Ситуация легко разруливается следующим образом (mod_perl - убогий, я о нем говорить не буду): на сервер с Perl ставится прекрасный модуль fast_cgi (http://fastcgi.com). Он позволяет существенно снять нагрузку с сервера за счет того, что после первого запуска скрипт не выгружается из памяти, а ждет следующий запрос. При этом скрипт будет работать столько, сколько нужно, оставаясь в памяти. Никакой лишней нагрузки на ресурсы сервера. Та же идея, что лежит в основе PHP.
Простые CGI-скрипты отличаются от FastCGI-скриптов всего несколькими строчками да и структурой. Например, скрипт может при первом запуске соединиться с MySQL и постоянно держать это соединение. Для того, чтобы скрипт работал именно под управлением FastCGI вызывается библиотека FCGI. Она легко ставится под Linux или Unix.
#!/usr/bin/perl
use FCGI;
my $request = FCGI::Request();
…
Действия, выполняемые при инициализации
скрипта (например, коннект к БД)
…
while($request->Accept() >= 0)
{
…
Основные действия, которые должен выполнять скрипт
…
}
…
Действия при завершении (редко нужно)
…
При выполнении запросов скрипт как бы гоняется по циклу: ожидание - обработка - ожидание - обработка. В реале (под Linux или Unix) это спящий процесс, который находится в режиме Accept.
Если запросов очень много, то сервер может запустить еще копию процесса. Это жрет некоторое количество памяти, но в целом увеличивает производительность сервера и ведет к экономии ресурсов.
Вывод напрашивается сам собой. Кому нравится Php, пусть использует его и не обвиняют Perl. Связка Perl и FastCGI - лучший ответ на такие обвинения. При сложных задачах PHP уступает Perl на стадии выполнения кода, он выигрывает лишь в стадии загрузки. Но использование FastCGI сводит на нет это преимущество. Так что выбор между языками зависит от специфики задачи и уровня разработчика. Perl сложен для новичков, но позволяет решать огромный пласт задач помимо веб-направленных (например, работа в фоновом режиме и обеспечение работы сайта). PHP легок в освоении и удобен при создании разного рода веб-приложений.
Например, РМП ProtoPlex построен на Perl-скриптах. Некоторые из них работают как Fast-CGI, некоторые работают в обычном режиме. Крупные и часто используемые скрипты лучше перевести на FastCGI. Не каждый хостинг-провайдер поддерживает эту технологию. Мне известны только ValueHost и Ди-Нет. На Валуе я хостился раньше, на Ди-Нет - сейчас. Ускоренный Perl дает возможность держать крупные интерактивные сайты на виртуальном хостинге, поэтому все больше провайдеров ставят себе этот полезный софт.
Источник: http://docs.com.ru/
Tags:
CGI,
Perl,
php
Март 6, 2008
Для моего текущего проекта необходимо определять на каком языке пользователь вводит информацию. Причём это не сложный выбор между PHP и Perl, а, например, между английским и испанским. Сначала я хотел составить список самых распространённых слов в популярных языках - предлоги, частые глаголы и т.д. Почти сразу я понял, что точность будет небольшая, а работы - очень много, даже, если её буду делать не я :).
Поэтому пришлось думать дальше. Мне больше всего понравился способ, в котором учитывается частотность букв, двух-, трёх- и четырёхбуквенных сочетаний.
Сначала система обучается - ей скармливается много текста и указывается, какой это язык. Она разбирает их на части и запоминает наиболее часто встречаемые как эталонные для этого языка.
Потом берётся текст с неизвестным языком и определяются наиболее часто встречаемые части в нём. Эти части сравниваются с эталонными и на основе этого определяется вероятность каждого из языков.
Я взял за основу код с http://boxoffice.ch/pseudo/ng.php, удалил лишнее, немного оптимизировал алгоритм и сделал код красивый и подходящий для CakePHP.
Определим язык для текста
plain text
$language = $this->LangDetect->detect(’Учи олбанский и убей сибя ап стену.’);
Не каждый человек сможет понять, что это за язык :). А компонент понял - russian-utf8.
Кстати, сразу видно бонус - определяется кодировка. Для русского поддерживаются ISO, KOI8-R, UTF-8, Windows-1251.
Если надо определить несколько наиболее вероятных языков, то надо указать второй параметр true.
plain text
$language = $this->LangDetect->detect(’Учи олбанский и убей сибя ап стену.’, true);
Это выдаст:
plain text
Array
(
[russian-utf8] => 30366
[bulgarian-utf8] => 31619
[ukrainian-utf8] => 33273
[serbian_cyrillic-utf8] => 33878
[belarusian-utf8] => 35596
…
Чем меньше значение, тем более вероятно, что это тот язык.
Полный список поддерживаемых языков (можно получить с помощью $this->LangDetect->listLanguages()):
afrikaans, albanian, alemannic, amharic-utf8, arabic-iso8859_6, arabic-utf8, arabic-windows1256, armenian, armenian-utf8, basque, belarusian-utf8, belarusian-windows1251, bosnian, breton, bulgarian-iso8859_5, bulgarian-utf8, catalan, chinese-big5, chinese-gb2312, chinese-utf8, croatian-ascii, czech-iso8859_2, czech-utf8, danish, dutch, english, esperanto, estonian, finnish, french, frisian, georgian, georgian-utf8, german, greek-iso8859_7, greek-utf8, hawaian, hebrew-iso8859_8, hebrew-utf8, hindi, hindi-utf8, hungarian, icelandic, indonesian, irish_gaelic, italian, japanese-euc_jp, japanese-shift_jis, japanese-utf8, korean, korean-utf8, latin, latvian, lithuanian, malay, manx, marathi, marathi-utf8, middlefrisian, mingo_iroquois, nepali, nepali-utf8, norwegian, persian, persian_farsi-utf8, persian_farsi-windows1256, polish-iso8859_2, polish-utf8, portuguese_brazil, portuguese_europe, quechua, romanian, rumantsch, russian-iso8859_5, russian-koi8_r, russian-utf8, russian-windows1251, sanskrit, scots, scots_gaelic, serbian-ascii, serbian_cyrillic-utf8, slovak-ascii, slovak-utf8, slovak-windows1250, slovenian-ascii, slovenian-iso8859_2, spanish, swahili, swedish, tagalog, tamil, tamil-utf8, thai, thai-utf8, turkish, turkish-utf8, ukrainian-koi8_u, ukrainian-utf8, vietnamese, welsh, yiddish-utf8
Чтобы компонент заработал, нужно скачать скомпилированную информацию о языках и положить этот fingerprint.dat в app/vendors.
А вот и сам компонент:
<?php
/**
* Language detection component
*
* Most of code was copied from http://boxoffice.ch/pseudo/code_expl/code_class.php
* but a lot of things were simplified and beautified
*
* @link http://php.southpark.com.ua
* @author Vladimir Luchaninov
* @version 1.0 (3 Dec 2007)
*
*/
class LangDetectComponent {
protected $fingerprint = null;
protected $ngrams = array();
//reasonable defaults
public $ngramCount = 350; //default nb of ngrams created from analyzed text
public $maxDelta = 140000; //stop evaluation deviate strongly
function startup(&$controller) {
$this->fingerprint = unserialize(file_get_contents(APP . ‘vendors’ . DS . ‘fingerprint.dat’));
}
/**
* Main function
*
* @param string $text Text with unknown language
* @param bool $onlyBest
* true - detect the best language
* false - detect all languages with possibilities
* @return LangDetect
*/
function detect($text, $onlyBest = true) {
if (empty($text)) {
trigger_error(’Text should not be empty’);
return false;
}
$this->createNGrams($text);
if ($onlyBest){
return $this->compareNGramsOne();
} else {
return $this->compareNGrams();
}
}
/**
* Get list of the available languages
*
* @return array List of languages
*/
function listLanguages() {
$languages = array_keys($this->fingerprint);
sort($languages);
return $languages;
}
/**
* Create ngram-array of given string
*
* @param string $text
*
*/
protected function createNGrams($text) {
$array_words = explode(” “, $text);
$ngrams = array();
foreach($array_words as $word) {
$word = “_”. $word . “_”;
$wordLength = strlen($word);
for ($i=0; $i <$wordLength; $i++) { //start position within word
for ($s=1; $s <4+1; $s++) { //length of ngram
if (($i + $s) <$wordLength + 1) { //length depends on postion
$ngrams[] = substr($word, $i, $s);
}
}
}
}
//count-> value(frequency, int)… key(ngram, string)
$blub = array_count_values($ngrams);
//sort array by value(frequency) desc
arsort($blub);
//use only top frequent ngrams (def by $ng_number)
$top = array_slice($blub, 0, $this->ngramCount);
$this->ngrams = array();
foreach ($top as $keyvar => $valvar){
$this->ngrams[] = $keyvar;
}
}
/**
* Compare ngrams: Textinput vs lm-files.
*
* @return array of languages with lowest deviation
*/
protected function compareNGrams() {
$limit = $this->maxDelta;
foreach ($this->fingerprint as $basename => $language) {
$delta = 0;
//compare each ngram of input text to current lm-array
foreach ($this->ngrams as $key => $ngram){
//match
if(in_array($ngram, $language)) {
$delta += abs($key - array_search($ngram, $language));
//no match
} else {
$delta += 400;
}
//abort: this language already differs too much
if ($delta> $this->maxDelta) {
break;
}
} // End comparison with current language
//include only non-aborted languages in result array
if ($delta <($this->maxDelta)-400) {
$result[$basename] = $delta;
}
} //End comparison all languages
if(!isset($result)) {
$result = array(’unknown’=>0);
} else {
asort($result);
}
return $result;
}
/**
* Variation - COMPARE ng’s - Return 1 LANGUAGE only
*
* @return string Most probable language
*/
protected function compareNGramsOne() {
$limit = 160000;
foreach ($this->fingerprint as $basename => $language) {
$delta = 0;
foreach ($this->ngrams as $key => $ngram){
if (in_array($ngram, $language)) {
$delta += abs($key - array_search($ngram, $language));
} else {
$delta += 400;
}
if ($delta> $limit) {
break;
}
}
if ($delta <$limit) {
$result[$basename] = $delta;
$limit = $delta; //lower limit
}
}
if (!isset($result)) {
return ‘unknown’;
} else {
asort($result);
//basename of best matching lm file
list($result_first, $ignore) = each($result);
}
return $result_first;
}
}
?>
Источник: http://php.southpark.com.ua
Tags:
php,
кодировка
Март 5, 2008
Как слать письма в PHP с аттачами? Просто!
© Antonio
из фоpума phpclub.net
Как послать письмо в HTML виде? Присоедините к письму аттач с названием message.html и письмо превратиться в HTML-письмо!
<?
// Функции. Можно вынести в дpугой файл.
class html_mime_mail {
var $headers;
var $multipart;
var $mime;
var $html;
var $parts = array();
function html_mime_mail($headers=”") {
$this->headers=$headers;
}
function add_html($html=”") {
$this->html.=$html;
}
function build_html($orig_boundary,$kod) {
$this->multipart.=”–$orig_boundary\n”;
if ($kod==’w’ || $kod==’win’ || $kod==’windows-1251′) $kod=’windows-1251′;
else $kod=’koi8-r’;
$this->multipart.=”Content-Type: text/html; charset=$kod\n”;
$this->multipart.=”BCC: del@ipo.spb.ru\n”;
$this->multipart.=”Content-Transfer-Encoding: Quot-Printed\n\n”;
$this->multipart.=”$this->html\n\n”;
}
function add_attachment($path=”", $name = “”, $c_type=”application/octet-stream”) {
if (!file_exists($path.$name)) {
print “File $path.$name dosn’t exist.”;
return;
}
$fp=fopen($path.$name,”r”);
if (!$fp) {
print “File $path.$name coudn’t be read.”;
return;
}
$file=fread($fp, filesize($path.$name));
fclose($fp);
$this->parts[]=array(”body”=>$file, “name”=>$name,”c_type”=>$c_type);
}
function build_part($i) {
$message_part=”";
$message_part.=”Content-Type: “.$this->parts[$i][”c_type”];
if ($this->parts[$i][”name”]!=”")
$message_part.=”; name = \”".$this->parts[$i][”name”].”\”\n”;
else
$message_part.=”\n”;
$message_part.=”Content-Transfer-Encoding: base64\n”;
$message_part.=”Content-Disposition: attachment; filename = \”".
$this->parts[$i][”name”].”\”\n\n”;
$message_part.=chunk_split(base64_encode($this->parts[$i][”body”])).”\n”;
return $message_part;
}
function build_message($kod) {
$boundary=”=_”.md5(uniqid(time()));
$this->headers.=”MIME-Version: 1.0\n”;
$this->headers.=”Content-Type: multipart/mixed; boundary=\”$boundary\”\n”;
$this->multipart=”";
$this->multipart.=”This is a MIME encoded message.\n\n”;
$this->build_html($boundary,$kod);
for ($i=(count($this->parts)-1); $i>=0; $i–)
$this->multipart.=”–$boundary\n”.$this->build_part($i);
$this->mime = “$this->multipart–$boundary–\n”;
}
function send($server, $to, $from, $subject=”", $headers=”") {
$headers=”To: $to\nFrom: $from\nSubject: $subject\nX-Mailer: The Mouse!\n$headers”;
$fp = fsockopen($server, 25, &$errno, &$errstr, 30);
if (!$fp)
die(”Server $server. Connection failed: $errno, $errstr”);
fputs($fp,”HELO $server\n”);
fputs($fp,”MAIL FROM: $from\n”);
fputs($fp,”RCPT TO: $to\n”);
fputs($fp,”DATA\n”);
fputs($fp,$this->headers);
if (strlen($headers))
fputs($fp,”$headers\n”);
fputs($fp,$this->mime);
fputs($fp,”\n.\nQUIT\n”);
while(!feof($fp))
$resp.=fgets($fp,1024);
fclose($fp);
}
}
// *************************************************************************
//
// В качестве аттача пpисоединяем html-письмо (открывается автоматически).
// Второй аттач - некоторый файл из каталога.
// Вот так вызывать все то, что написано выше:
//
// *************************************************************************
$mail=new html_mime_mail();
$mail->add_html(”<html><body><center><h2>Пpивет!<br><br>”.
“<br>Посылаю двоичный файл [/bin/ls] …”.
“</h2></center></body></html>”);
$mail->add_attachment(”/bin/”,”ls”);
$mail->build_message(’win’); // если не “win”, то кодиpовка koi8
$mail->send(’ПОЧТОВЫЙ_ХОСТ_ВАШЕГО_ПРОВАЙДЕРА’,
‘КОМУ_(E-MAIL)’,
‘ОТ_КОГО_(E-MAIL)’,
‘ТЕМА ПИСЬМА’);
//
// После прихода письма качаем по ФТП оригинальный /bin/ls и сравниваем с
// импортированным из письма:
//
// X:\temp>fc /b ls ls2
// Сравнение файлов ls и LS2
// FC: различия не найдены
//
//
// Внимание! Если у вас нет файла /bin/ls, то просто закомментируйте строку
// $mail->add_attachment(”/bin/”,”ls”), чтобы программа не пыталась присоединить
// к письму неcуществующие файлы.
//
?>
>
Комментаpии по пpосьбам тpудящихся. Будут добавляться до тех поp, пока всем все не станет понятно.
> …объяснить поподробнее, то что написано
> на http://php.spb.ru/php/mail.html.
> Т.е. я не понял, вся та информация, она
> располагается все на одной странице или то,….
Пpогpамма состоит из 2х частей.
необходимые функции
как написать письмо с аттачем. Аттач — это HTML-письмо, содеpжащее слово “пpивет”
> И второе: (”<html><body><center><h2>пpивет</h2>
> </center></body></html>”) - это есть само тело послание,
> которое придет на mail ???
Да, это и есть аттач. Их может быть несколько.
> .. но у меня возникли кое-какие еще вопросы:
> “почтовый хост” - в этом случае прописывается тот адрес, где
> реально находиться почтовый ящик (т.е. в большинстве
> случаев у провайдера) или нет
Нет. Этот параметр не имеет никакого отношения к каким-либо почтовым ящикам… Если вы не можете запонить это поле, то не занимайтесь программированием вообще.
Открываем свою почтовую программу (для тех, кто не понял: Outlook, Thebat или др)
Смотрим, что указано в поле “исходящий (SMTP) сервер”
Пишем в параметре “ХОСТ” эти данные (без угловых скобок)
Пример: smtp.peterlink.ru (если я являюсь клиентом Петерлинка, что дает мне право пользоваться сервером почты). Любой человек как-то пишет письма в Инет. Это “как-то” он делает через почтовый сервер своего провайдера.
> “кому” - ???
> “от кого” - ???
> “тема” - ???
Нет, пожалуй эти поля комментировать не будем… Хотя:
Кому - пример: vasya@pupkin.ru
От кого - пример (от меня): dmitry@php…ru
Тема - пример: …
Нет, все же лучше не комментировать…
Источник http://www.codenet.ru
Tags:
attach,
mail,
php,
письмо
Март 3, 2008
Статья описывает способ реализации механизма, позволяющего бороться с автоматическим заполнением форм, ложными регистрациями и спамом через форму обратной связи.
Требования: PHP>=4.0.6, GD >=2.0.
Исходные тексты можно скачать тут.
Данная статья написана по мотивам статьи Nathan Rohler “Security Images in PHP” опубликованной на сайте #Dev Shed 9 августа 2004 года. Вообще, с начала, меня посетила мысль ее перевода, но, во первых автор выбрал интересный, но не самый тривиальный вариант решения проблемы, а во вторых, мне бы вряд ли удалость сформулировать на русском языке такое обилие мыслей.
Последнее время, в связи с распространяющейся эпидемией спама, веб-мастера, стали все чаще и чаще прятать адреса своей электронной почты (E-Mail). Многие стали использовать формы обратной связи. Но как оказалось, такую защиту можно обойти. И уже на сегодняшний день, существует огромное количество программ, предназначенных для рассылки спама, через формы обратной связи.
Наша задача - сделать так, чтобы сообщение вам смог отправить только “живой человек”. Чаще всего, для этого используют небольшие картинки, на которых выводится текст. Пользователя просят продублировать этот текст в поле ввода. Если дублирование производится неверно, то форма не обрабатывается.
На сегодняшний день мне не знакомо ни одной программы, способной обойти такую защиту. Я даже на знаю ни одной программы, вообще, хоть как-то пытающуюся распознать содержимое картинки. По этому, можно смело сказать, что сегодня нет необходимости как-то искажать изображение на картинке. Но мы смотрим в будущее.
Генерация изображения
Автор предложил следующий алгоритм: сложное изображение на картинке формируется с помощью заранее подготовленных подложек. После чего на подложки выводится текст, случайным шрифтом, случайного размер и, естественно, случайного содержания.
Основной недостаток такого алгоритма заключается в том, что существующую “подложку” можно быстро вычислить. А после этого вычесть из изображения, которое необходимо распознать и таким образом получить картинку с чистым текстом. А для распознания такого текста уже сегодня существует масса программ.
Наша задача - сделать абсолютно случайную подложку, с текстом, который не то что распознать сложно, его прочитать тяжело.
Я бы предложил следующий алгоритм:
Создаем подложку (для этого можно использовать алгоритм построения фракталов)
Добавляем помехи - несколько случайных линий, цвета основного текста.
Выводим основной текст
Самое интересное - увеличиваем изображение в неровное количество раз - например, в 1.7, в 1.6
Уменьшаем изображение до оригинальных размеров
Увеличивать и уменьшать изображение необходимо с использованием сглаживания, иначе даже человек не сможет прочитать текста.
Если вам кажется, что рисовать фрактал слишком сложно, то можно нарисовать простую сетку.
Принцип работы механизма
При заходе пользователя на страницу с формой, мы создаем сессию и записываем в зарегистрированную переменную случайный код:
session_start();
session_register(”secret_number”);
if (intval($_SESSION[”secret_number”])<1000) {
srand(doubleval(microtime()));
$_SESSION[”secret_number”]=rand(1000,9999);
}
После того как случайный текст сгенерирован, необходимо вывести форму:
<form action=”index.php” method=”post”>
Ваш E-Mail:<br>
<input type=”text” name=”email” value=”"><br>
<br>
Введите код, который вы видите на картинке:<br>
<input type=”text” name=”secretcode” value=”"><br>
<img src=’code.php?<?=doubleval(microtime());?>’
width=101 height=26 vspace=5>
<br><br>
<input type=”submit”>
</form>
Скрипт, обрабатывающий данные, отправленные при помощи формы, должен работать примерно следующим образом:
session_start();
session_register(”secret_number”);
if ($_SERVER[”REQUEST_METHOD”]==”POST”) {
$error=0;
if ($_POST[”secretcode”]!=$_SESSION[”secret_number”] ||
intval($_POST[”secretcode”])==0) $error=1;
if ($error==0) {
$_SESSION[”secret_number”]=rand(1000,9999);
// Выполняем необходимые действия с данными
// ..
print “Hello “.htmlspecialchars(StripSlashes($_POST[”email”]));
exit;
}
if ($error==1)
print “<font color=red>Число с картинки введено неверно</font>”;
}
// Выводим форму повторно
// …
Генерация изображения
<?
// Регистрируем переменную
session_start();
session_register(”secret_number”);
function mt() {
list($usec, $sec) = explode(’ ‘, microtime());
return (float) $sec + ((float) $usec * 100000);
}
header(”Content-type: image/png”);
// создаем изображение
$im=imagecreate(101, 26);
// Выделяем цвет фона (белый)
$w=imagecolorallocate($im, 255, 255, 255);
// Выделяем цвет для фона (светло-серый)
$g1=imagecolorallocate($im, 192, 192, 192);
// Выделяем цвет для более темных помех (темно-серый)
$g2=imagecolorallocate($im, 64,64,64);
// Выделяем четыре случайных темных цвета для символов
$cl1=imagecolorallocate($im,rand(0,128),rand(0,128),rand(0,128));
$cl2=imagecolorallocate($im,rand(0,128),rand(0,128),rand(0,128));
$cl3=imagecolorallocate($im,rand(0,128),rand(0,128),rand(0,128));
$cl4=imagecolorallocate($im,rand(0,128),rand(0,128),rand(0,128));
// Рисуем сетку
for ($i=0;$i<=100;$i+=5) imageline($im,$i,0,$i,25,$g1);
for ($i=0;$i<=25;$i+=5) imageline($im,0,$i,100,$i,$g1);
// Выводим каждую цифру по отдельности, немного смещая случайным образом
imagestring($im, 5, 0+rand(0,10), 5+rand(-5,5),
substr($_SESSION[”secret_number”],0,1), $cl1);
imagestring($im, 5, 25+rand(-10,10), 5+rand(-5,5),
substr($_SESSION[”secret_number”],1,1), $cl2);
imagestring($im, 5, 50+rand(-10,10), 5+rand(-5,5),
substr($_SESSION[”secret_number”],2,1), $cl3);
imagestring($im, 5, 75+rand(-10,10), 5+rand(-5,5),
substr($_SESSION[”secret_number”],3,1), $cl4);
// Выводим пару случайных линий тесного цвета, прямо поверх символов.
// Для увеличения количества линий можно увеличить,
// изменив число выделенное красным цветом
for ($i=0;$i<8;$i++)
imageline($im,rand(0,100),rand(0,25),rand(0,100),rand(0,25),$g2);
// Коэффициент увеличения/уменьшения картинки
$k=1.7;
// Создаем новое изображение, увеличенного размера
$im1=imagecreatetruecolor(101*$k,26*$k);
// Копируем изображение с изменением размеров в большую сторону
imagecopyresized($im1, $im, 0, 0, 0, 0, 101*$k, 26*$k, 101, 26);
// Создаем новое изображение, нормального размера
$im2=imagecreatetruecolor(101,26);
// Копируем изображение с изменением размеров в меньшую сторону
imagecopyresampled($im2, $im1, 0, 0, 0, 0, 101, 26, 101*$k, 26*$k);
// Генерируем изображение
imagepng($im2);
// Освобождаем память
imagedestroy($im2);
imagedestroy($im1);
imagedestroy($im);
?>
Автор: Mike
Tags:
php,
защита,
сесии
Март 2, 2008
Новые записи
Старые записи