Записи в рубрике 'PHP Статьи'

Интеграция журнала LiveJournal в персональный сайт

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

Автоматическое построение форм различной сложности и отправка их письмом с аттачами произвольного количества

Все сталкивались с тривиальной задачей - создание формы для отправки по e-mail. Обычно не возникает никаких проблемм. Но и работа эта не столь интересна и увлекательна. Простая рутина. Возникает идея создать программу, которая автоматизировала бы этот процесс. Для начала определим задачу. Предположим, нам нужно создать формы на сайте.

В формах может присутствовать:
заголовок раздела формы
текстовое поле (text)
текстовый блок (textarea)
поле пароля (password)
поле выбора из списка (select)
поле checkbox
поле радио буттона (radio)
невидимое поле (hidden)
поле загрузки файла (file)

Отсылать письма предполагается в текстовом виде с аттачами. Письма в формате html не пользуются популярностью у народа. Должна быть проверка на заполненность полей, обязательных к заполнению. Вот все пункты задачи готовы.

Решено сделать 3 файла:
файл с формой
файл отправки формы
файл инициализации формы

Забегая вперед, могу предположить, что кто-то захочет положить файлы программы (первые два) в отдельный каталог, например forms, и будет просто инклюдить файл с формой на нужных страницах сайта, передавая ему параметром путь к файлу инициализации данной формы. Так как формы могут все-таки отличаться друг от друга в оформлении, я не стану городить огромный файл с бесконечным количеством вариантов и приведу полностью рабочий пример, работающий на сайте «Седьмого континента» в разделе «поставщики», а также на сайте gipragor.ru в разделе «задать вопрос».

Добавлю, что в наших случаях в формах были вариации вида полей text, textarea (в форме «Седьмого континента» 3 вида поля text). В вашем случае, возможно, понядобится еще несколько вариантов для полей. Все делается аналогично тому, что будет рассмотрено ниже.

Начнем с описания файла инициализации формы.

Ниже приведен текст файла ini.php

Выбор адресата^head^0
Выберите из списка^select^1^mail консультант|info@gipragor.ru|selected админ|totoeval@mtu-net.ru
Ваши координаты^head^0
Имя^text^1
Телефон^text^0
Факс^text^0
<nobr>Е-mail</nobr>^text^0
Я хочу получить ответ по телефону^checkbox^0^checked
Вопрос^head^0
Тема^textarea^1
Вопрос^textarea^1^long
Присоединить файл^file^0^attach
Присоединить файл 2^file^0^attach2
Предыдущая страница^hidden^0^refer

Как видим, каждое поле формы описывается отдельной строкой.
Как я ни старался сделать универсальным оформление всех полей форм, — не получилось.
Вследствие этого, предлагаю такое оформление:
Первым везде идет название поля, которое выводится на экран.
Вторым — тип поля формы:
text
password
textarea
checkbox
radio
hidden
file

Третий — указатель обязательного заполнения поля. Если стоит 1 — поле обязательно. Если параметр пустой или любой отличающийся от 1, то поле не обязательное.
Четвертым указываем дополнительный параметр, если он необходим. У каждого вида поля свои дополнительные параметры:
text — long указывает на то, что поле-строка будет длинной и размещена под названием; обычное поле, без параметра, размещается справа от названия
textarea — то же самое, что и у text
checkbox — checked указывает на то, что чекбокс будет выбран по-умолчанию
radio — четвертым параметром указывается имя группы радио-буттонов, а пятым — checked, как и у checkbox
file — указываем имя указателя массива загружаемых файлов
hidden — указываем параметр, в соответствии с которым в значение этого поля будет подставлено определенное значение, либо параметр будет передан как есть

Ну вот покончили с инициализацией формы.

Теперь попробуем написать программу, выводящую форму пользователю.

Создаем файл index.php с нижеприведенным содержимым.
<!– начало –>

<h1>Задать вопрос</h1>

<?

if ($is_send == “send_query”)
{
echo “<p>Вопрос был отправлен.</p>”;
}
?>

<!–
Выводим форму типа multipart/form-data
для отправки через нее текстовых полей и файлов
–>
<form method=”post” action=”send.php” ENCTYPE=”multipart/form-data”
onsubmit=”return Validate(this);”>
<table border=”0″ cellspacing=”0″ cellpadding=”5″ width=”100%”>
<tr><td class=”text” colspan=”2″ align=”center”><b></b></td></tr>
<?
// читаем файл инициализации в массив $texts
$texts=file(”ini.php”);

// перебираем все строки в файле и определяем пустые
for ($j=0; $j<(sizeof($texts)); $j++)
{
// оператором trim удаляем у строки слева и справа пробелы и переносы
$texts[$j]=trim($texts[$j]);
// если есть пустые строки, то в новый массив $proposal_text они не записываются
if ($texts[$j] != “”) {$proposal_text[]=$texts[$j];}
}

// обнуляем переменную, в которую будут занесены все обязательные для заполнения поля
$fields=”"; // имена полей формы
$fieldnames=”"; // названия полей формы

// перебираем все строки инициализации в массиве $proposal_text
// имена полей будут называться form[0], form[1], form[2]…
// Таким образом, мы передадим всю форму в одном массиве.
// Индекс элемента массива будет указателем строки описания поля в файле инициализации
// для дальнейшей обработки полученной формы.
for ($i=0; $i<(sizeof($proposal_text)); $i++)
{
// разобьем строки специальным разделительным символом ^
// тогда $proposal[0] - текстовое название поля
// тогда $proposal[1] - указатель типа поля формы:
// text - текстовое поле-строка
// textarea - текстовое поле-блок
// hidden - невидимое поле
// password - поле ввода пароля
// file - форма для загрузки файла
// checkbox - чекбокс
// radio - радио буттон
// head - заголовки разделов форм, не имеют никаких полей,
// лишь текст выводится полужирным шрифтом, либо выделяется иным способом
// тогда $proposal[2] - указатель обязательного заполнения поля посетителем.
//Если он равен 1, то поле обязательно, если любое другое значение - нет
// тогда $proposal[3] - дополнительный параметр.
// например, у нас это:
// long в поле text и поле textarea означает, что поле бОльшей ширины
// и расположено под названием поля
// refer в поле hidden говорит о том, что передается в невидимом поле
// адрес предыдущей страницы, посещенной пользователем
// attach в поле file - имя поля загружаемого пользователем файла

// все поля оформляются соответственно указанному типу ниже в блоке switch
$proposal=explode(’^',$proposal_text[$i]);
// переменной type присвоем тип поля
$type=trim($proposal[1]);

// определяем, обязательно ли к заполнению текущее поле
if (isset($proposal[2]))
{
if (trim($proposal[2]) == ‘1′)
// если в поле указателя содержится 1, то добавляем имя поля к
{
// если в переменную fields уже были записаны данные,
// то ставим запятую
if ($fields != “”) {$fields.=’, ‘;}
$fields.=”‘form[$i]’”;
if ($fieldnames != “”) {$fieldnames.=’, ‘;}
$fieldnames.=”‘”.$proposal[0].”‘”;
$imperative=” *”;
}
else {$imperative=”";}
}
// если в строке есть дополнительный параметр, то записываем его в пtременную param
if (isset($proposal[3])) {$param=trim($proposal[3]);}

// стравниваем тип поля с возможными вариантами и соответственно оформляем его
switch ($type) {
case “head”: // поле заголовка
echo “<tr>\n\t”.
“<td class=\”text\” colspan=\”2\”><br>”.
“<p><b>$proposal[0]</b></p>”.
“</td>\n</tr>\n”;
break;
case “text”: // текстовое поле
if (isset($proposal[3])) {
if ($param == “long”) {
// если поле длинное, то располагаем его под названием
// и увеличиваем длину
echo “<tr>\n\t”.
“<td colspan=\”2\” class=\”text\”>”.
$proposal[0].”$imperative<div align=\”right\”>\n\t”.
“<input type=\”text\” name=\”form[$i]\” size=\”102\”>”.
“</div></td>\n</tr>\n”;
}
}
else {
// иначе выводим стандартное поле-строку справа от названия поля
echo “<tr>\n\t”.
“<td class=\”text\”>”.$proposal[0].”$imperative</td>\n\t”.
“<td align=\”right\” valign=\”top\”>”.
“<input type=\”text\” name=\”form[$i]\” size=\”50\”>”.
“</td>\n</tr>\n”;
}
break;
case “password”: // поле пароля
echo “<tr>\n\t”.
“<td class=\”text\”>”.$proposal[0].”$imperative</td>\n\t”.
“<td align=\”right\” valign=\”top\”>”.
“<input type=\”password\” name=\”form[$i]\” size=\”50\”>”.
“</td>\n</tr>\n”;
break;
case “textarea”: // поле текстового блока оформляем аналогично текстовому полю
if (isset($proposal[3])) {
if ($param == “long”) {
echo “<tr>\n\t”.
“<td colspan=\”2\” class=\”text\”>”.
$proposal[0].”$imperative”.
“<div align=\”right\”>\n\t”.
“<textarea name=\”form[$i]\” rows=\”6\” cols=\”102\”>”.
“</textarea></div></td>\n</tr>\n”;
}
}
else {
echo “<tr>\n\t”.
“<td class=\”text\” valign=\”top\”>”.
$proposal[0].”$imperative</td>\n\t”.
“<td align=\”right\” valign=\”top\”>”.
“<textarea name=\”form[$i]\” rows=\”4\” cols=\”50\”>”.
“</textarea></td>\n</tr>\n”;
}
break;
case “radio”: // радио буттон.
//Его дополнительный параметр - имя переменной-группы радио-буттонов.
if (!isset($proposal[3])) {$param = “form[$i]”;}
if (!isset($proposal[4])) {$checked = “”;}
// если не задан параметр выбора буттона по-умолчанию
else {$checked = ” checked”;}
// если выбран по-умолчанию
echo “<tr>\n\t”.
“<td colspan=\”2\” class=\”text\”>”.
“<input type=\”radio\” name=\”$param\” id=\”id$i\”$checked>”.
“<label for=\”id$i\”> $proposal[0]</label></td>\n</tr>\n”;
break;
case “checkbox”: // чекбокс
if (!isset($proposal[3])) {$checked = “”;}
// если не задан параметр выбора чекбокса по-умолчанию
else {$checked = ” checked”;}
// если выбран по-умолчанию
echo “<tr>\n\t”.
“<td colspan=\”2\” class=\”text\”>”.
“<input type=\”checkbox\” name=\”form[$i]\” id=\”id$i\”$checked>”.
“<label for=\”id$i\”> $proposal[0]</label></td>\n</tr>\n”;
break;
case “hidden”: // невидимое поле.
// От его параметра зависит, что в нем будет передаваться.
// Если параметр не описан, то он будет передан по-умолчанию как есть
if (!isset($proposal[3])) {$param = “form[$i]”;}
echo “<input type=\”hidden\” name=\”form[$i]\”";
if ($param==”refer”) {echo ” value=\”".urlencode($HTTP_REFERER).”\”>”;}
else {echo ” value=\”$param\”>\n”;}
break;
case “file”: // поле загружаемого пользователем файла
if (!isset($proposal[3])) {$param = “form[$i]”;}
echo “<tr>\n\t”.
“<td align=\”right\” valign=\”bottom\”>”.
“<p align=\”left\”>$proposal[0]$imperative<br>”.
“<input type=\”file\” name=\”file_att[$param]\” size=\”35\”></p>”.
“</td></tr>\n”;
break;
case “select”: // поле выбора селект
if (isset($proposal[3])) { // если заданы параметры селекта
$options = explode(”\t”, $proposal[3]);
// разделяем параметры каждой строки селекта
$option_text=explode(”|”,$option[0]);
// разбиваем первый подпараметр селекта
// на имя селекта и вид (multiselect и обычный)
// получаем в $option_text[1] - вид селекта
if ($option_text[1]==”multiselect”)
{
if (isset($option_text[2]))
{
$multiselect=”size=$option_text[2]”;
}
$multiselect.=” multiselect”;
}
else {$multiselect=” size=\”1\”";}
echo “<tr>\n\t”.
“<td class=\”text\”>$proposal[0]$imperative</td>\n\t”.
“<td align=\”right\” valign=\”top\”>”.
“<select name=\”form[$i]\” style=\”width: 317\”$multiselect>\n”;
// выводим тег селекта
for ($z=1; $z<sizeof($options); $z++)
// в 0 строке селекта у нас параметр, указывающий отправщику,
// как обрабатывать текущий селект
{
// выводим строки селекта
$option_text=explode(”|”, $options[$z]);
// в первой части - текст строки,
// во второй - передаваемое значение
if (!isset($option_text[2])) {$option_text[2]=”";}
// если параметр “выбранная строка” не установлен
echo “\t<option value=\”$option_text[1]\” $option_text[2]>”.
“$option_text[0]</option>\n”;
// вывели строку селекта
}
echo “</select></td>\n</tr>\n”;
}
break;
default:
// если тип не определен, то ничего не выводится.
// И, следовательно, стоит подумать, что еще не учтено.
}
}
?>
</table>
<!–
Выведена таблица с формой.
Осталось вывести на экран кнопки “отправить” и “очистить”,
как это делают умные дядьки на других сайтах.
–>
<table border=”0″ cellspacing=”5″ cellpadding=”0″ width=”100%”>
<tr>
<td align=”right” valign=”bottom”><input type=”submit” value=”Отправить”>
<img src=”/images/1×1.gif” width=”10″ height=”50″>
<input type=”reset” value=”Очистить”>
</td>
</tr>
</table>
<!–
Конечно, здесь могло не быть этого кода,
а кнопки отправки формы и очищения можно задать в файле инициализации,
добавив и их обработку в программе.
–>
</form>
<p>Вы можете задать вопрос.
С вопросом можно отправить файлы.<br>
Ответ вы получите на адрес электронной почты,
указанный в координатах,
либо по телефону, если поставите галочку у соответствующего пункта.</p>
<!–
Яваскрипт, которому мы передали список полей формы, обязательных к заполнению
Он определит после попытки отправки формы, заполненны ли эти поля.
Если не заполнены, то скрипт ругнется и укажет какое поле не заполненно,
установив в него курсор.
–>
<script language=”JavaScript”>
fields = new Array(<? echo $fields; ?>);
fieldnames = new Array(<? echo $fieldnames; ?>);
function Validate(forma)
{
for(i=0;i<fields.length;i++)
{
field = fields[i];
if (forma.elements[field].value == “”)
{
alert(”Вы должны заполнить поле \”"+fieldnames[i]+”\”");
forma.elements[field].focus();
return false;
}
}
return true;
}
</script>

</td>
</tr>
</table>
<!– конец –>

Ну вот, наша форма выводится на экран пользователя, и он старательно, прикусив язык, заполняет все её поля.

Но мы-то знаем, что вывести форму и заполнить её — половина дела. Важно получить форму, обработать её и отправить по выбранному или указанному по-умолчанию адресу.

Ниже приведен текст файла отправки письма с аттачами send.php, который мы кладем в папку с index.php.

<?
// определяем, с какой страницы пришел посетитель на страницу отправки
if (strpos($HTTP_REFERER, “gipragor.ru/feedback”) === false) {
// если не со страницы отправки формы, то кидаем его в форму
header(”location: .”);
}

// если посетитель прошел проверку, читаем файл инициализации
$texts=file(”ini.php”);

// перебираем все строки в файле и сохраняем в новый массив только не пустые
for ($j=0; $j<(sizeof($texts)); $j++)
{
$texts[$j]=trim($texts[$j]);
if ($texts[$j] != “”) {$proposal_text[]=$texts[$j];}
}

// Объявляем пустую строковую переменную, в которой будет храниться сообщение
$mailtext=”";

// Перебираем все строки массива формы
for ($i=0; $i<(sizeof($proposal_text)); $i++)
{
// Разбиваем строки по разделительному символу ^
// получаем подстроки, в которых хранится:
// 0 - текст названия поля формы
// 1 - тип поля формы
// 2 - указатель обязательности заполнения поля формы
// 3 - дополнительные параметры поля формы
$proposal=explode(’^',$proposal_text[$i]);
$type=trim($proposal[1]);
if (isset($proposal[3])) {$proposal[3]=trim($proposal[3]);}
if (!isset($form[$i])) {$form[$i]=”нет данных”;}
// перебираем варианты типов полей формы
switch ($type)
{
case “head”: // если заголовок раздела формы
if ($mailtext != “”)
{
// если это не первый заголовок в форме,
// то ставим перед ним 2 пустые строки
$mailtext.=”\n\n”;
}
$mailtext.=”\t$proposal[0]\n”;
break;
case “text”: // если поле текствое - строка
if (isset($proposal[3]))
{
if ($proposal[3] == “long”)
{ // если строка длинная, то выводим ее под названием поля
$mailtext.=”$proposal[0]:\n$form[$i]\n\n”;
}
}
else
{ // если строка не длинная, то выводим ее справа от названия поля
$mailtext.=”$proposal[0]: $form[$i]\n”;
}
break;
case “textarea”: // поле текстового блока
$mailtext.=”$proposal[0]:\n$form[$i]\n\n”;
break;
case “radio”: // радио буттон
$group == “$proposal[2]”;
$mailtext.=”$proposal[0]: $group\n”;
break;
case “checkbox”: // чекбокс
if (trim($form[$i]) == “on”)
{ // если чекбокс выделили, то его значение - on
$mailtext.=”$proposal[0]\n”;
}
break;
case “hidden”: // скрытое поле. Обрабатываем его взависимости от параметра
if (!isset($proposal[3])) {$param = “form[$i]”;}
if ($param=”refer”) {$form[$i]=urldecode($form[$i]);}
$mailtext.=”$proposal[0]: $form[$i]\n”;
break;
case “file”: // поле файла отправляемого пользователем в виде аттача к письму
if (!isset($proposal[3])) {$param = “form[$i]”;}
else {$param=$proposal[3];}
// создаем массив из файлов-аттачей
$att_arr[]=$file_att[$param];
$att_arr_type[]=$file_att_type[$param];
$att_arr_name[]=$file_att_name[$param];
break;
case “select”: // поле селекта
if (isset($proposal[3])) { // если есть параметры селекта
$options = explode(”\t”, $proposal[3]);
// разбиваем параметры и перебираем каждую строку
$option_text=explode(”|”,$options[0]);
// разбиваем первый подпараметр селекта
//на имя селекта и вид (multiselect и обычный)
if ($option_text[0] == “mail”) {$mailto=$form[$i];}
// если 0 параметр равен mail -
// значит это варианты e-mail адресата (в нашем случае)
$mailtext.=”$proposal[0]: $form[$i]\n”;
}
break;
default:
}
}

// удалим из текста формы теги html
$mailtext=strip_tags($mailtext);

// удалим специальные символы из текста формы
// используя стандартный способ из руководства php
$search = array (”‘&(amp|#38);’i”,
“‘&(lt|#60);’i”,
“‘&(gt|#62);’i”,
“‘&(nbsp|#160);’i”,
“‘&(iexcl|#161);’i”,
“‘&(cent|#162);’i”,
“‘&(pound|#163);’i”,
“‘&(copy|#169);’i”,
“‘�(\d+);’e”,
“‘—’i”,
“‘–’i”);
$replace = array (”&”,
“<”,
“>”,
” “,
chr(161),
chr(162),
chr(163),
chr(169),
“chr(\\1)”,
” - “,
“-”);
$mailtext = preg_replace ($search, $replace, $mailtext);

// Параметры отправляемого сообщения
if ($mailto == “”)
{ // если адресат не был выбран в форме, то указываем его по-умолчанию
$to = “totoeval@mtu-net.ru”;
}
else
{ // если адресат был выбран посетителем в форме
$to = $mailto;
}
$from = “webmaster@$SERVER_NAME”;
$subject = “New Providers”;
$message = $mailtext;

// объявление в заголовке письма параметр From - от кого.
$headers = “From: $from”;

// Оформляем boundary string - строку-разделитель
$semi_rand = md5(time());
$mime_boundary = “==Multipart_Boundary_x{$semi_rand}x”;

// определяем, был ли отправлен файл с письмом
if (sizeof($att_arr)>0)
{ // если файл отправлен
// Добавляем к заголовку письма тип передаваемых данных
$headers .= “\nMIME-Version: 1.0\n” .
“Content-Type: multipart/mixed;\n” .
” boundary=\”{$mime_boundary}\”";
// Добавляем к сообщению multipart boundary и тип передаваемых данных,
// а затем присоединяем текст письма
$message = “This is a multi-part message in MIME format.\n\n” .
“–{$mime_boundary}\n” .
“Content-Type: text/plain; charset=\”windows-1251\”\n” .
“Content-Transfer-Encoding: 7bit\n\n” .
$message . “\n\n”;
}
else
{ // если письмо без приаттаченных файлов
// Добавляем к заголовку письма тип передаваемых данных
$headers .= “\nMIME-Version: 1.0\n” .
“Content-Type: text/plain; charset=\”windows-1251\”\n” .
” boundary=\”{$mime_boundary}\”";
// Добавляем к сообщению boundary и тип передаваемых данных (текст),
// а затем присоединяем текст письма
$message = “Content-Type: text/plain; charset=\”windows-1251\”\n” .
“Content-Transfer-Encoding: 7bit\n\n” .
$message . “\n\n”;
}

// перебираем имеющиеся приаттаченные файлы
// если их нет, то аттач производиться не будет
for ($files=0; $files<sizeof($att_arr); $files++)
{

$fileatt=$att_arr[$files];
$fileatt_type=$att_arr_type[$files];
$fileatt_name=$att_arr_name[$files];

if (is_uploaded_file($fileatt))
{ // проверяем, верно ли заапплоаден файл
// Читаем файл аттача (’rb’ = читаем в двоичном виде)
$file = fopen($fileatt,’rb’); // открываем поток
$data = fread($file,filesize($fileatt));

fclose($file); // закрываем поток

// Кодируем Base64 содержимое файла
$data = chunk_split(base64_encode($data));

// Добавляем содержимое файла к сообщению
// с соответствующими заголовком и описанием типа данных
$message .= “–{$mime_boundary}\n”.
“Content-Type: {$fileatt_type};\n”.
” name=\”{$fileatt_name}\”\n”.
“Content-Transfer-Encoding: base64\n\n”.
$data.”\n\n”;
}// так перебираем все отправляемые файлы

}
$message .= “–{$mime_boundary}–\n”;
// в конец сообщения добавляем разделительную строку с окончанием сообщения

// Отправляем сообщение
@mail($to, $subject, $message, $headers);

// сообщаем в куки, что письмо отправлено
setcookie (”is_send”, “send_query”, time()+120);
// переводим пользователя к странице формы
// где ему сообщат, что письмо его отправлено
header(”location: .”);

?>

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

Все вышенаписанное предназначено для начинающих программировать на php, и для полета мысли уже опытных.

Повторять программу можно в варианте моем, своем, друга.

По вопросам всвязи с возникающими проблемами обращаться ко мне по адресу totoeval[@]mtu-net.ru
Автор: Тотоев Александр

www.nova-wings.ru
gipragor.ru
www.v6.ru
«Седьмой континент»

Tags: , ?>, mail, форма

Добавить комментарий Март 10, 2008

Использование шаблонов в PHP

Многие программеры сталкивались с проблемой, когда дизайнеру “нужна свобода” в написании html, а программеру “чистота” кода :) У меня такое приключилось при написании виртуального веб-магазина. В общем, недолго думая я стал искать различные PHP-классы для создания “динамических сайтов” с использованием так называемых шаблонов. И нашел один, который отвечал всем моим требованиям, но слишком уж он оказался “большим и тяжелым”. Называется этот класс FastTemplate (уж не помню, где я его скачал). Недолго думая, я решил написать свой класс, взяв за основу функциональность FastTemplate. Мои результаты в написании своего класса получились практически такими как у FastTemplate, но, как мне кажется, ни чуть не хуже (прим.: код я не копировал, а создавал сам с нуля).

Итак, для начала работы Вам необходимо скачать мой класс template.

Скачали? Теперь можно пробовать на простом примере. Сделаем пример листинга файлов текущего каталога с подсчетом кол-ва байт каждого файла, при этом динамически создав таблицу. Итак, создайте следующие файлы: main.htm
<html>
<head>
<title><!– ABOUT –></title>
<link rel=”stylesheet” type=”text/css” href=”/styles.css”>
</head>
<body>

<p align=”center” class=”b”><!–

ABOUT

–><br>
Localtime is <!– LOCALTIME –>
</p><br>

<div align=”center”><table
STYLE=”border-collapse:collapse” class=”th”>
<tr>
<td colspan=”2″ class=”th”
align=”center”
style=”background-color:#000000; color:white”>
File listing</td>
</tr>
<!–
TABLE_CONTENT
–>
<tr>
<td class=”th” align=”right”
style=”background-color:#000000; color:white”>&nbsp;</td>
<td class=”th”
style=”background-color:#000000; color:white”>&nbsp;
<!– TOTAL –>&nbsp;</td>
</tr>
</table></div>

</body>
</html>

rows.htm

<tr>
<td align=”right” class=”th”
style=”background-color:<!– COLOR –>;color:black”>&nbsp;
<!– PWD –>&nbsp;</td>
<td class=”th” style=”background-color:<!– COLOR –>; color:black”>&nbsp;
<!– FILESIZE –>&nbsp;</td>
</tr>

index.php

<?php

require (’templates.php’); // Включаем класс для работы с шаблонами

// Определяем теги
$meta = array( “ABOUT” => “:: template class example ::”,
“LOCALTIME” => date(”M-d-Y H:i:s”));

$t = new template;

// Инициализируем файлы и дескрипторы
$t->init(array( index => “main.htm”, rows => “rows.htm”));

// Устанавливаем ограничители (delimiter-ы)
$t->delimiters(”<!–”, “–>”);

// Определяем теги
$t->assign($meta);

$d = dir(”.”);

$colors = array(”#d4d4d4″, “#a0a0a0″); $i = 0;
$totalbytes = 0;
while ($entry = $d->read()) {
if (preg_match(”/^(.|..)$/”, $entry)) continue;
$color = $colors[$i];
$t->assign(”FILENAME”, $entry); // Определяем под тегом FILNAME имя файла
$t->assign(”COLOR”, $color); // под COLOR текущий цвет
$t->assign(”PWD”, realpath ($entry)); // Полный путь
if (($size = filesize($entry)) > 1024) {
$totalbytes += $size;
$size = sprintf(”%0.2f Kbytes”, ($size / 1024));
} else {
$totalbytes += $size;
$size .= ” bytes”;
}
$t->assign(”FILESIZE”, $size); // связываем с тегом FILESIZE длинну файла

// Обрабатываем файл с дескриптором rows, при этом полученные результаты
// закрепляем под тегом TABLE_CONTENT (путем добавления)
$t->parseit(rows, “TABLE_CONTENT”);
$i = (++$i >= count($colors)) ? 0 : $i;

// Под этим тегом у нас будет общее кол-во байт найденых файлов
$t->assign(”TOTAL”, ( ($totalbytes > 1024) ?
sprintf(”%0.2f Kbytes”, ($totalbytes / 1024)) : $totalbytes.” bytes”));
}

$d->close();

// Обрабатываем страницу с дескриптором index. Т.е. при обработке
// все попадающиеся теги будут заменены на определенное значение.
$t->parseit(index);

// Выводим все
$t->printit();
$t->freshall();

?>

Результаты выполнения можно увидеть здесь http://null.magelan.ru/php/templates Данный класс работает очень шустро, обработчик построен на основе регулярных выражений.
Детальное описание

В классе определенны массивы:
$filelist - ассоциативный массив дексрипторов и файлов
$assign - ассоциативный массив определенных тегов
$root - корневой каталог
$arr - массив с результатом
$delmiters - ограничители
// init - Инициализация шаблонов
// arr - ассоциативный массив (см.пример) с дескрипторами и файлами
// root - корневой каталог, где лежат файлы (по-умолчанию текущий)
// delimit - тут можно указать ограничитель из двух символов, например “{}”
function init($arr = “”, $root = “”, $delimit = “”)

// Установка корневого каталога шаблонов
function setroot($root = “”)

// Добавление дескрипторов и файлов для работы с шаблонами
// list - ассоциативный массив
function listit($list = “”)

// Обработка шаблона
// $d - дескриптор файла ИЛИ МАССИВ!
// $temp - обработка в тег $temp (если не указать, данная ф-ия обработает
// декср.файла и добавит его к массиву с результатами)
function parseit($d = “”, $temp = “”)

// Получить обработанный массив, возвращает string
// $array - массив
function getparsed($array)

// Указать ограничители
// $d1 - левый, $d2 - правый
// Например $t->delimiters(”<!–”, “–>”);
function delimiters($d1 = “”, $d2 = “”)

// Как listit, только добавить можно не ассоциативный массив, а
// простую строку
// $d - дескриптор файла
// $name - имя файла
function addtolist ($d = “”, $name = “”)

// Используется для очистки всех массивов в классе
function freshall()

// Используется для очистки результатов обработки
// Очищается $arr
function fresh()

// Вывести на экран.
// В v1.02 - если указан $tag, выводит тег $this->assign[”$tag”]
function printit([$tag])

Если возникнут какие-либо комментарии, пишите на null[@]magelan.ru. Также выслушаю замечания и поправки.

Огромное спасибо Fil (fil[@]apb.farlep.net) за некоторые дополнения и замечания :) И всем тем, кто откликнулся!

Источник http://www.codenet.ru/

Tags: template, шаблоны

Добавить комментарий Март 8, 2008

Защита e-mail адресов на веб-страницах

О защите 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

Что круче PHP или Perl

Ответить на этот вопрос однозначно нельзя. Дело в том, что 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

Определение языка и кодировки. Компонент для CakePHP

Для моего текущего проекта необходимо определять на каком языке пользователь вводит информацию. Причём это не сложный выбор между 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

Защита сайта от SQL инъекции с помощью mod_rewrite

Не секрет, что можно взломать абсолютно любой сайт, идеальной защиты не бывает. Взломать интернет сайт, намного легче, чем взломать прикладную программу, да и делать это гораздо интереснее, ведь твои труды увидят тысячи пользователей (разумеется если взломанный сайт достаточно популярен). На сегодняшний день известно множество методик взломов интернет-сайтов, одним из самых опасных является так называемая инъекция ( Injection - введение). Инъекция бывает разной можно внедрить свой код в программу, можно внедрить инородные данные в поток чужих данных, но одной из самых опасных для интернет-сайтов является SQL инъекция ( SQL Injection ). Из названия уже становится понятно, что и куда внедряется. На всякий случай поясню, при SQL инъекции мы внедряем наш код в SQL запрос, в результате чего при благополучных обстоятельствах мы можем получить данные хранящиеся в базе данных не доступные для просмотра стандартными средствами. Многие полагают, что SQL инъекцией страдает только база данных MySQL, но это конечно же не так. SQL инъекцию можно осуществить в любую базу, поддерживающую языки запросов (а таких большинство).

Рассмотрим вышесказанное на простом примере. Рассматривать все будем на примере MySQL, так как эта БД является одной из самых популярных среди web-программистов.

Пусть у нас есть таблица в которой есть 3 поля (md,password,login). Пусть у каждого пользователя есть свой секретный код (md), например он может быть такой sdf897sdsdf87sdf99sdf87. В поле password записан пароль пользователя, а в поле login имя пользователя.

Пусть у нас есть следующий код на PHP :
$result = mysql_query ( “SELECT password FROM user_table WHERE md=?$usermd?” ); // сам запрос
$pass = mysql_result ( $result , ?? , ?password? ); //записываем в переменную $pass значение поля password
echo $pass ; // выводим переменную $pass на экран

Этот код берет из таблицы user_table значение поля password у записи, md которой равен переменной $usermd, а затем выводит этот пароль. Пусть каждый пользователь знает свой собственный md, но не знает md других пользователей, тогда очевидно, что попытка подобрать md чужого пользователя на 99% обречена на провал.

Как же можно узнать пароль другого пользователя? Оказывается можно, достаточно поместить в переменную $usermd свой код, например такой ” ? or login=?admin?# ” (подразумевается что, администратор имеет login=”admin” ). Как будет Хакер делать подмену, это вопрос другой, но факт остается фактом, в данном случае на экран выведется пароль администратора.

Давайте проанализируем, что же произошло. В результате подмены запрос принял вид:

“SELECT password FROM user_table WHERE md=?? or login=?admin?#?”

Думаю что теперь все понятно, поясню лишь, зачем нужен символ #. Все что находится после символа # обрезается, т.е. не является частью запроса, сам символ # является спецсимволом и разумеется тоже не участвует в запросе. При таком запросе база попытается выбрать запись с пустым полем md, или у которых login=?admin?, поскольку записей с пустым md не существует, то выберется запись у которой поле login равно “admin”.

Это всего лишь пример, он не претендует на звание наиярчайшего примера SQL инъекции, он лишь демонстрирует, к чему все это может привести. Не стоит считать, что такого абсурдного кода не существует, и никто не будет открыто хранить пароль администратора. SQL инъекцией страдают большинство CMS а также крупнейших Forum ?ов, в том числе и InvisionPB, не говоря уже о PhpBB и его клонах.

Все это как ни странно было всего лишь вступлением, на самом деле я не планировал рассказывать о инъекции, но как говориться “нужно знать врага в лицо”. Как же можно защитить свои скрипты, от подобной уязвимости? На самом деле очень легко, нужно всего лишь проверять входные данные. Реализовать это можно различными способами, лучший из них, это просто проверить наличие спецсимволов в переменной, и если таковые имеются занести в лог информацию о попытке взлома, вместе с IP взломщика, чтобы в последствии можно было его забанить. Второй способ более простой, можно сразу не проверяя вырезать из переменной все спецсимволы, например с помощью команды str_replace. Этот способ простой, но имеет недостаток: SQL запрос все равно будет выполнен, причем переменная будет заведомо содержать ошибочные данные, поэтому на экране может отобразиться целая гора ошибок (в зависимости от сложности скрипта).

Я хочу предложить свой способ защиты скриптов. Этот способ не является лучшим, но на мой взгляд, он очень изящен и имеет несколько плюсов, о которых я конечно же расскажу. Способ заключается в использование модуля для сервера Apache, который носит название mod_rewrite. Этот модуль установлен практически на всех платных хостингах. Задача этого модуля - изменять введенную информацию пользователя в строке URL на другую, нужную вам. Чтобы было понятнее приведу простой пример.

Допустим, пользователь введет в строке адрес http://site.ru/download.html. Этот адрес получает сервер, но только после того, как его обработает mod_rewrite (если он включен конечно). Модуль mod_rewrite анализирует введенный адрес, и согласно своим настройкам изменяет или не изменяет его. Изменить он может на что угодно, например он может отдать серверу вместо введенного адреса другой адрес например http://site.ru/article.html, в результате чего у пользователя отобразиться на экране страница article.html, хотя в строке адреса по прежнему будет отображаться http://site.ru/download.html, и пользователь даже не будет подразумевать, что его “обманули”.

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

Давайте вернемся к нашему примеру с паролем администратора и попробуем защитить наш скрипт с помощью mod_rewrite. Пусть переменная $usermd передается скрипту GET запросом: http://site.ru/script.php?usermd=4k2jhk34jhkjh6kh7kh33. Как видите в переменной $usermd будет храниться значение “4k2jhk34jhkjh6kh7kh33″, мы знаем, что эта переменная может содержать цифры от 0 до 9 и латинские буквы от a до z. Теперь давайте с помощью mod_rewrite изменим наш адрес на более красивый, а заодно и реализуем проверку на входные данные.

Для того чтобы воспользоваться модулем mod_rewrite вам необходимо создать файл .htaccess в корневом каталоге (обратите внимание на точку в начале файла). Запишите в этот файл следующие строки:

RewriteEngine on
Options +FollowSymlinks
RewriteBase /
RewriteRule ^.htaccess$ - [F]

Эти команды сообщают серверу, что необходимо использовать модуль mod_rewrite. Далее за этим кодом будут следовать правила замена адресов, общий вид которых:

RewriteRule входной_адрес на_что_заменить

Например, чтобы нам подменить адрес http://site.ru/download.html на http://site.ru/article.html необходимо написать:

RewriteRule http://site.ru/download.html http://site.ru/article.html

Как видите все просто. Но на самом деле все чуточку сложнее. Дело в том, что в данном примере мы имеем дело со статическими страницами, адрес которых никогда не измениться, если же мы имеем дело со скриптами, переменные в которых принимают разные значения, такой способ не подойдет. Для этого mod_rewrite позволяет использовать регулярные выражения. Если вы с ними не знакомы, то вам будет сложно разобраться с модулем, но все же можете попытаться. Советую вам почитать дополнительную информацию о регулярных выражениях, так как она никогда лишней не бывает.

Снова вернемся к нашему примеру. Что же нам необходимо сделать? Нам нужно заменить адрес http://site.ru/script_4k2jhk34jhkjh6kh7kh33.html на адрес http://site.ru/script.php?usermd=4k2jhk34jhkjh6kh7kh33.

Делается это следующей строкой:

RewriteRule http://site.ru/script_([a-z0-9]*).html http://site.ru/script.php?usermd=$1

Думаю нужно сделать немного пояснений. Выражение ( [a-z0-9]* ) означает, что в данном месте может находиться последовательность из цифр и букв любой длины. [a-z0-9] - перечисление допустимых символов (в данном примере заданы диапазоны), знак * означает что таких символов может быть несколько. Выражение которое соответствует маске находящейся в скобках присваивается переменной $1 (цифра обозначает номер скобок) и вставляется в адрес на который будем заменять входящий адрес.

В результате этой манипуляции после того как пользователь введет в строке адреса http://site.ru/script_4k2jhk34jhkjh6kh7kh33.html он автоматически попадет на страницу http://site.ru/script.php?usermd=4k2jhk34jhkjh6kh7kh33. И конечно же переменная $usermd будет содержать значение “4k2jhk34jhkjh6kh7kh33″.

Теперь давайте рассмотрим, что же произойдет если пользователь попытается ввести http://site.ru/script_?%20or%20login=?admin?#.html ( %20 - тоже самое что и пробел). В результате этого запроса адрес попадет модулю mod_rewrite, который проанализирует его, т.к. выражение ?%20or%20login=?admin?# не подходит под маску ( [a-z0-9]* ), т.к. содержит недопустимые символы, то mod_rewrite ничего не сделает, так как будто его вообще нет, очевидно, что в этом случае пользователю будет возвращена ошибка 404 Page not found (404 страница не найдена).

Это и есть защита. Она не идеальна и имеет минусы, но есть и плюсы. Главный минус - это то, что в скрипты, все же можно передать вредоносную информацию, но для этого придется воспользоваться методом POST, а не GET. Второй минус - mod_rewrite немного нагружает сервер, как и любые регулярные выражения. Главный плюс - пользователь не видит, как называются ваши переменные.

Ну вот собственно и все, о чем я хотел рассказать. Я показал всего лишь пример, на самом деле модуль mod_rewrite намного мощнее, собственно как SQl инъекция. И помните, идеальной защиты не существует.

Данная информация дана в ознакомительных целях, автор не несет ответственности за возможно взломанные скрипты.

Нгуен Павел http://excode.ru

Tags: htaccess, mod_rewrite, MySQL, Options, SQL

Добавить комментарий Март 4, 2008

PHP отправка писем с аттачами

Как слать письма в 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

Защита Web-форм от автоматической обработки

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

Требования: 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

Новые записи Старые записи


Календарь

Май 2012
Пн Вт Ср Чт Пт Сб Вс
« Апр    
 123456
78910111213
14151617181920
21222324252627
28293031  

Записи по месяцам

Записи по рубрикам

Бегун