<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Insight IT &#187; PHP</title>
	<atom:link href="http://www.insight-it.ru/category/programmirovanie/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.insight-it.ru</link>
	<description>Информационные технологии</description>
	<lastBuildDate>Tue, 31 Jan 2012 09:34:08 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Модификация алгоритма хэширования</title>
		<link>http://www.insight-it.ru/programmirovanie/php/modifikaciya-algoritma-khehshirovaniya/</link>
		<comments>http://www.insight-it.ru/programmirovanie/php/modifikaciya-algoritma-khehshirovaniya/#comments</comments>
		<pubDate>Fri, 15 Feb 2008 10:17:20 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[hash]]></category>
		<category><![CDATA[md5]]></category>
		<category><![CDATA[sha1]]></category>
		<category><![CDATA[код]]></category>
		<category><![CDATA[кодинг]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[технология]]></category>
		<category><![CDATA[хранение данных]]></category>
		<category><![CDATA[хэш]]></category>
		<category><![CDATA[хэширование]]></category>
		<category><![CDATA[шифрование]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/programming/php/modifikaciya-algoritma-khehshirovaniya/</guid>
		<description><![CDATA[Если Вы уже успели прочитать одну из моих предыдущих записей о хэшировании, то Вы уже имеете базовое представление о теме сегодняшнего разговора. Одним из возможных способов применения хэшей является хранение аутентификационных данных пользователей интернет-приложения, об особенностях реализации формирования и проверки хэшей при регистрации и авторизации пользователей средствами PHP я и хотел бы с Вами поговорить. [...]]]></description>
			<content:encoded><![CDATA[<p>Если Вы уже успели прочитать <a href="/science/kriptografiya/obratnogo-puti-net/" target="_blank">одну из моих предыдущих записей о хэшировании</a>, то Вы уже имеете базовое представление о теме сегодняшнего разговора.<br />
Одним из возможных способов применения хэшей является хранение аутентификационных данных пользователей интернет-приложения, об особенностях реализации формирования и проверки хэшей при регистрации и авторизации пользователей средствами <a href="/tag/php" target="_blank">PHP</a> я и хотел бы с Вами поговорить.<br />
<span id="more-42"></span><br />
Сомневаюсь, что Вы услышите что-то новое, если я скажу, что в <a href="/tag/php" target="_blank">PHP</a> даже в &#171;стандартной комплектации&#187; реализована масса алгоритмов хэширования, начиная с широкораспространенных <strong>md5();</strong> и <strong>sha1();</strong> и заканчивая модулями <strong>hash</strong> и <strong>mhash</strong>, в которых реализована еще целая масса алгоритмов. Все они давно уже стандартизованы и доступны для изучения всем желающим получить о них какую-либо информацию.</p>
<p>Допустим мы храним пароли пользователей в виде какого-то стандартного хэша, для примера &#8212; <strong>md5</strong>, в базе данных. Все было отлично, но в один прекрасный момент нашелся подлый злоумышленник, который неким хитрым способом получил доступ к базе данных логинов и паролей. Перед ним стоит цель &#8212; узнать изначальный пароль у максимального числа пользователей. Посмотрим на ситуацию с его стороны:</p>
<ul>
<li>Первым делом он бы попытался определить, какой именно хэш перед ним находится &#8212; чаще всего это делается либо просто взглянув на длину хэша, либо если приложение широко распространено (популярная CMS скажем) &#8212; покопавшись в ее исходниках, еще есть вариант найти свой собственный аккаунт &#8212; и зная пароль попробовать на нем разные алгоритмы, способов можно придумать множество &#8212; все ограничивается лишь воображением. Узнав ответ на свой вопрос ему лишь останется набрать в <a href="/tag/google" target="_blank">Google</a> фразу вроде <em>&#171;md5 decrypt&#187;</em>, а дальше уже дело техники.</li>
<li>Еще один вариант решения задачи &#8212; взглянуть на список хэшей на предмет наличия совпадений. С очень высокой степенью вероятности за значительной группой одинаковых хэшей будет скрываться какой-либо банальный пароль вроде <em>123456</em>.</li>
</ul>
<p>Задача же разработчика приложения максимально обезопасить систему от подобных ситуаций. Конечно же можно просто стараться минимизировать возможности реализации методов получения информации из базы данных, но предугадать все варианты невозможно: в любом из используемых компонентов системы может оказаться уязвимость в коде, на которую наверняка найдется умник, который напишет <em>exploit</em>, а значит полностью исключить такую вероятность не получится, в лучшем случае выйдет просто ее минимизировать.</p>
<p>Именно по этим причинам и стоит задуматься об усложнении задачи злоумышленника в случае возникновения описанной выше ситуации. Для исключения возможности просто расшифровывания хэшей по словарю (то есть первый случай, когда определяется тип хэша и соответствующий ему словарь <em>хэш =&gt; исходное значение</em>) достаточно исключить возможность идентификации алгоритма хэширования или наличия к нему заранее подготовленного словаря. Для этого достаточно лишь сделать шаг в сторону от стандартного алгоритма любым пришедшим в голову способом, например:</p>
<ul>
<li>хранить хэш не от самого пароля, а от <em>пароль + какая-либо фиксированная строка</em></li>
<li>поменять местами группы символов в получившемся стандартном хэше</li>
<li>сделать сдвиг символов в стандартном хэше (или можно даже не сами символы двигать, а с помощью битовых операций их значения)</li>
<li>комбинировать два стандартных алгоритма хэширования, или алгоритм хэширования с алгоритмом обратимого шифрования, которых доступно также множество</li>
</ul>
<p>Список этот можно было бы продолжать достаточно долго, это было лишь первое, что пришло мне в голову. Но ни один из приведенных способов не избавит от возможности второго варианта раскрывания исходного пароля. Основывается он на однозначности стандартных алгоритмов &#8212; одним и тем же исходным данным соответствует один и тот же хэш. Для отказа от этого свойства стандартных алгоритмов придется выполнить более сложную модификацию используемой для генерации хэша функции (которая конечно же тоже поможет и для борьбы с первым вариантом). Сразу приведу пример <a href="/tag/kod" target="_blank">кода</a>, реализующего этот механизм, а дальше попытаюсь его объяснить:</p>
<pre lang="PHP">
function generateHash($input,$salt = false)
{
  if(!$salt)$salt=randomString(2);
  $hash=md5($input.$salt);
  return $salt.substr($hash,2);
}</pre>
<p>Как не трудно заметить &#8212; используется самодельная функция <strong>randomString();</strong>, которая возвращает случайную строку, состоящую из указанного количества шестнадцатеричных цифр (надеюсь Вы в состоянии написать ее своими силами). Именно этот момент и гарантирует элемент случайности при каждой новой генерации хэша. В том месте, где я прочитал про этот механизм (ссылку, к сожалению, привести не могу &#8212; в bookmark&#8217;ах не нашел), этот случайный компонент назывался словом <strong>salt</strong>, смысл его заключается в том, что он приписывается ко входным данным, передаваемым стандартной функции хэширования, а затем им же подменяется какая-либо фиксированная часть полученного хэша.<br />
Наверняка у Вас возник вопрос: а как же потом понять, что пользователь ввел верные данные, ведь для тех же исходных данных получится другой хэш и возможности их сравнить не будет? Ответ достаточно прост, его можно было увидеть даже в коде: при повторной инициализации хэша из <a href="/tag/bd" target="_blank">базы данных</a> достается заранее известная часть хранящегося там хэша, соответствующего конкретному пользователю &#8212; тот самый <strong>salt</strong>, и передается нашей функции. В этом случае в механизме будет использоваться именно он, а не новое случайное значение, и, как следствие, в случае правильности введенных данных на выходе получатся совпадающие хэши. Вот такой вот простенький, но иногда достаточно полезный трюк.</p>
<p>Если Вам понравился этот пост &#8212; возможно Вам придутся по душе и <a href="/dzhentelmenskij-nabor-php-programmista/" target="_blank">остальные записи из этой серии статей</a>, а не пропустить публикацию новых записей Вам может помочь <a href="/feed" target="_blank">RSS feed</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/php/modifikaciya-algoritma-khehshirovaniya/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>На пути к идеалу</title>
		<link>http://www.insight-it.ru/programmirovanie/php/na-puti-k-idealu/</link>
		<comments>http://www.insight-it.ru/programmirovanie/php/na-puti-k-idealu/#comments</comments>
		<pubDate>Thu, 07 Feb 2008 12:39:34 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[online]]></category>
		<category><![CDATA[интернет]]></category>
		<category><![CDATA[информационные технологии]]></category>
		<category><![CDATA[код]]></category>
		<category><![CDATA[кодинг]]></category>
		<category><![CDATA[оптимизация]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[производительность]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/programming/php/na-puti-k-idealu/</guid>
		<description><![CDATA[&#8230;или 15 привычек, которые помогут ускорить PHP-приложение Практически каждый программист стремится в своих приложениях не только максимально точно реализовать требуемый функционал, но и сделать это как можно более эффективным методом. Для этого конечно же необходимо проектирование, подходящий выбор используемых технологий, возможно некоторый опыт в предметной области, этот список можно продолжать достаточно долго, но я позволю [...]]]></description>
			<content:encoded><![CDATA[<h4 style="text-align: right">&#8230;или 15 привычек, которые помогут ускорить PHP-приложение</h4>
<p>Практически каждый программист стремится в своих приложениях не только максимально точно реализовать требуемый функционал, но и сделать это как можно более эффективным методом. Для этого конечно же необходимо проектирование, подходящий выбор используемых технологий, возможно некоторый опыт в предметной области, этот список можно продолжать достаточно долго, но я позволю себе этого не делать, так как речь сегодня пойдет не об этом. Вместо этого хочу обратить Ваше внимание на более простые и &#171;приземленные&#187; методы <a href="/tag/optimizaciya" target="_blank">оптимизации</a> PHP-кода, которые может быть и не так эффективны по сравнению с указанными выше, но зато не требуют каких-либо усилий со стороны кодера и/или программиста, достаточно лишь воспринимать их как &#171;не вредные&#187; привычки.</p>
<p><span id="more-25"></span></p>
<p>Прочитав достаточно солидный объем разного рода документации по <a href="/tag/php" target="_blank">PHP</a>, я часто натыкался на статьи и тексты, так или иначе связанные с <a href="/tag/proizvoditelnost" target="_blank">производительностью</a> PHP-скриптов. Порой в такого рода источниках информации удавалось найти достаточно интересные и неочевидные факты об этом языке программирования, которые не смотря на свою простоту могли дать вполне заметный прирост к <a href="/tag/proizvoditelnost" target="_blank">производительности</a> итогового приложения. Я почему-то очень серьезно стал относиться к производительности написанных мной скриптов, и довольно часто стал испытывать на практике спорные моменты в реализации, о которых узнавал из <a href="/net" target="_blank">Сети</a> или каких-либо других источников, с помощью самописных или <a href="/tag/opensource" target="_blank">opensource</a> benchmark&#8217;ов, хотя порой и просто внедряя в реальные приложения. Как ни странно, в большинстве случаев практика подтверждала теорию, и я стал постоянно пользоваться этими простыми правилами, о которых я и хочу Вам рассказать.</p>
<h4>Повышения значения индекса с помощью ++$i;</h4>
<p>Этот факт был наверное одним из самых удивительных для меня, когда я впервые о нем услышал, но действительно операция <strong>++<span style="color: blue">$i</span></strong>; выполняется несколько быстрее, чем <strong><span style="color: blue">$i</span>++;</strong>. или другие вариации на ту же тему вроде <strong><span style="color: blue">$i</span>+=<span style="color: blue">1</span>;</strong>. Привычка использовать в качестве индекса цикла переменную под названием i, казалось бы стара как Мир, мне она досталась в наследство от <strong>C</strong>, а в месте с ней &#171;в комплекте&#187; шла привычка писать выражение <strong>i++</strong> в заголовках циклов. Разница в скорости обработки этих выражений, насколько мне известно, обусловлена разным количеством элементарных машинных операций, которые необходимо выполнить процессору (в точных цифрах не уверен, пишу по памяти, но <strong>++<span style="color: blue">$i</span>;</strong> требует трех элементарных операций, а <strong><span style="color: blue">$i</span>++;</strong> – четырех). В справедливости этого факта не трудно убедиться, достаточно написать простенький скрипт, состоящий из цикла с достаточно большим количеством итераций, и замерить любым способом точное время его выполнения при использовании разных способов инкрементации индекса цикла.</p>
<h4>Вывод статического контента без помощи PHP</h4>
<p>Сейчас тот факт, что использование интерпретатора PHP для вывода статического контента сильно замедляет этот процесс, кажется мне очевидным, но поначалу я использовал <strong><span style="color: #17349c">echo</span></strong> там, где он был необходим, ничуть не чаще, чем там, где он лишь замедляет работу скрипта. От использования еще менее эффективного способа &#8212; <strong><span style="color: #17349c">print</span></strong>, меня избавила моя лень: писать каждый раз на одну букву больше дико не хотелось (в отличии от <strong><span style="color: #17349c">echo</span></strong>, <strong><span style="color: #17349c">print</span></strong> возвращает информацию об успешности выполнения своей работы, что в большинстве случаев просто-напросто не нужно). Проверить опять же не трудно &#8212; нужен лишь объемистый текстовый файл, который достаточно вывести в browser разными способами и засечь уходящее на это время.</p>
<h4>Вывод статического контента из отдельного файла</h4>
<p>Частенько при желании выполнить указанное в заголовке действие по привычке используют <strong><span style="color: #a3a423">include</span></strong>, <strong><span style="color: #a3a423">require</span></strong> или их <strong><span style="color: #a3a423">_once</span></strong> версии, что является далеко не самой лучшей идеей с точки зрения производительности. Самым быстрыми быстрыми и экономичными поотношению к оперативной памяти являются функции <strong><span style="color: #17349c">readfile</span></strong> и <strong><span style="color: #17349c">fpassthru</span></strong>. В качестве доказательства этого факта приведу таблицу, демонстрирующую статистику выполнения этой операции различными методами и позаимствованную с <a href="http://www.raditha.com/wiki/Readfile_vs_include" target="_blank" rel="nofollow">одного англоязычного сайта</a>:</p>
<table align="center" border="1" cellspacing="0" width="100%">
<tr>
<th rowspan="2" style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center" valign="center">Функция</th>
<th colspan="2" style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center">Время (сек.)</th>
<th colspan="2" style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center">Оперативная память (байт)</th>
</tr>
<tr>
<td style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center">32Kb файл</td>
<td style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center">1Mb файл</td>
<td style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center">32Kb файл</td>
<td style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center">1Mb файл</td>
</tr>
<tr>
<td>file_get_contents</td>
<td align="right">0.00152</td>
<td align="right">0.00564</td>
<td align="right">52480</td>
<td align="right">1067856</td>
</tr>
<tr>
<td>fpassthru</td>
<td align="right"><strong>0.00117</strong></td>
<td align="right">0.00184</td>
<td align="right">20016</td>
<td align="right">20032</td>
</tr>
<tr>
<td>fgets</td>
<td align="right">0.00195</td>
<td align="right">0.07190</td>
<td align="right">30760</td>
<td align="right">30768</td>
</tr>
<tr>
<td>file</td>
<td align="right">0.00157</td>
<td align="right">0.06464</td>
<td align="right">87344</td>
<td align="right">2185624</td>
</tr>
<tr>
<td>require_once</td>
<td align="right">0.00225</td>
<td align="right">0.08065</td>
<td align="right">67992</td>
<td align="right">2067696</td>
</tr>
<tr>
<td>readfile</td>
<td align="right"><strong>0.00117</strong></td>
<td align="right">0.00191</td>
<td align="right"><strong>19192</strong></td>
<td align="right">19208</td>
</tr>
</table>
<h4>Вывод переменных</h4>
<p>Наверняка вам известно, что переменные можно выводить с помощью конструкции вроде <strong><span style="color: #17349c">echo</span> <span style="color: red">&#171;</span><span style="color: blue">$var</span> <span style="color: red">text&#187;</span>;</strong>, что является одним из самых удобных вариантов решения этой задачи благодаря минимальному количеству символов, которые необходимо набрать, но с точки зрения быстродействия этот вариант далек от идеала, так как влечет за собой достаточно серьезные преобразования в памяти сервера, эффект которых порой бывает заметен невооруженным глазом. Частично ущерб производительности можно сгладить заменой этой конструкции на <strong><span style="color: #17349c">echo</span> <span style="color: blue">$var</span>.<span style="color: red">&#187; text&#187;</span>;</strong>, что приводит к несколькому усложнению внешнего вида кода и несколько поправляет ситуацию со скоростью выполнения. Но как известно знак . обозначает конкатенацию двух строк, что тоже требует некоторых вычислений и затрат памяти, но и от нее можно избавиться, заменив на запятую. Выражение <strong><span style="color: #17349c">echo</span> <span style="color: blue">$var</span>,<span style="color: red">&#187; text&#187;</span>;</strong> ничем по своему эффекту не отличается от предложенных ранее вариантов, за исключением максимального быстрого выполнения, обусловленного отсутствием дополнительных преобразований в процессе передачи просто последовательности из константы и переменной.</p>
<h4>Избегайте выполнения лишних действий</h4>
<p>Достаточно абстрактное утверждение, но тем не мение постоянное напоминание себе о нем может избавить Вас от совершения массы ошибок. Самой широкораспространенной является наверное вызов какой-либо функции (чаще всего <strong><span style="color: #17349c">count</span>();</strong> или <strong><span style="color: #17349c">strlen</span>();</strong>) в проверке условия выхода из цикла. Когда-нибудь доводилось писать видеть в собственном или чужом коде выражение вида <strong><span style="color: #a3a423">for</span>(<span style="color: blue">$i</span> = <span style="color: #a3a423">0</span>; <span style="color: blue">$i</span> &lt; <span style="color: #17349c">count</span>(<span style="color: blue">$array</span>); ++<span style="color: blue">$i</span>) { &#8230; }</strong>? А задумываться о последовательности выполнения действий при его обработке? Стоит только немного начать размышлять и ошибка становится очевидной: <strong><span style="color: #17349c">count</span>();</strong> выполняется при каждой итерации цикла, что приводит к подсчету количества элементов массива при каждой проверки условия выхода из цикла &#8212; почему бы не посчитать это значение заранее и сравнивать значения индекса с переменной, а не с результатом выполнения функции?</p>
<h4>@</h4>
<p>Использование этого оператора стоит избегать при каждой возможности. Казалось бы такое простое действие, как сокрытие вывода возможного сообщения об ошибке, влечет за собой достаточно трудоемкую последовательность действий: устанавливает значение параметра PHP-интерпретатора <strong>error_reporting = 0</strong>, выполняет указанное за этим оператором действие, возвращает значение <strong>error_reporting</strong> в исходное состояние.</p>
<h4>Маленькие мелочи</h4>
<p>Развивая тему предыдущего подраздела, хочется обратить внимания, что даже на еще более элементарных вещах можно сэкономить драгоценное процессорное время:</p>
<ul>
<li>Вместо условия <strong><span style="color: #a3a423">if</span>(<span style="color: blue">$variableOne</span> == <span style="color: blue">$variableTwo</span>) { &#8230; }</strong> можно написать <strong><span style="color: #a3a423">if</span>(<span style="color: blue">$variableOne</span> === <span style="color: blue">$variableTwo</span>) { &#8230; }</strong>, что избавит от проверки на соответствие типов данных и приведения их друг к другу, в некоторых случаях эти действия эти случаях эти действия конечно же и бывают необходимы, но бывает это далеко не часто.</li>
<li>Глядя на выражения вроде <strong><span style="color: #a3a423">if</span>(<span style="color: blue">$boolean</span> == true) { &#8230; }</strong>, я чаще всего вспоминаю цитату из одного малоизвестного интернет-ресурса: <strong>if (b.ToString().length &lt; 5) { &#8230; }</strong>. Хоть и не имет никакого отношения к PHP, но суть проблемы отражает очень ярко.</li>
<li>Самым очевидным способом проверить попадает ли длина строки в какой-либо диапазон является использование функции <strong><span style="color: #17349c">strlen</span>();</strong> и сравнение полученного результата с фиксированными значениями, но зачем выполнять лишний вызов функции, если можно воспользоваться услугами конструкцией языка PHP <strong><span style="color: #17349c">isset</span>();</strong> для определения наличия в строке определенных символов. <strong><span style="color: #a3a423">if</span>(<span style="color: #17349c">isset</span>(<span style="color: blue">$str</span>{<span style="color: blue">5</span>})) { &#8230; }</strong> приведет к абсолютно тем же результатам, что и <strong><span style="color: #a3a423">if</span>(<span style="color: #17349c">strlen</span>($str)&gt;4){ &#8230; }</strong></li>
<li>Битовые операции выполняются намного быстрее относительно обычных арифметических действий. Об этом факте редко вспоминают, да и работать с ними умеет далеко не каждый, но порой они бывают очень актуальны, особенно при частой работе с числами кратными двойке.</li>
<li>Угадайте, что делает интерпретатор при виде надписи <strong><span style="color: blue">1</span>/</strong><strong><span style="color: blue">2</span></strong>? Правильно: делит <strong><span style="color: blue">1</span></strong> на <strong><span style="color: blue">2</span></strong>. Зачем лишний раз утруждать его, когда можно написать просто половину &#8212; <strong><span style="color: blue">0.5</span></strong>.</li>
<li>При возвращении значения переменной из функции при помощи <strong><span style="color: #17349c">global</span></strong> выполняется на порядок больше действий, чем при классическом <strong><span style="color: #17349c">return</span></strong>.</li>
<li>Конечно же фраза <strong><span style="color: blue">$array</span>[text];</strong> интерпритируется практически точно так же, как и <strong><span style="color: blue">$array</span>[<span style="color: red">'text'</span>];</strong>, но зачем выполнять лишнее преобразование из необъявленной константы в строку, проверять, что такой константы все же не существует, выводить сообщение типа <strong>E_NOTICE</strong>, если можно всего этого не делать?</li>
<li>По возможности не используйте <strong><span style="color: #17349c">require_once</span>();</strong> или <strong><span style="color: #17349c">include_once</span>();</strong> неоднократно по отношению к одному и тому же файлу. При отсутствии какого-либо эффекта, попусту тратится время на обработку повторного запроса.</li>
<li>Даже &#171;безобидных&#187; ошибок стоит избегать, лишняя проаерка потратит не так много процессорного времени, как генерирование достаточно длинного сообщения об ошибке и вывод его в <em>stdout</em>, <em>stderr</em> или <em>лог-файл</em>, а также не стоит забывать, что даже &#171;безобидные&#187; ошибки могут стать потенциальной угрозой безопасности приложения вцелом.</li>
</ul>
<h4>В заключении&#8230;</h4>
<p>&#8230;хотелось бы упомянуть одну из первых статей по <a href="/tag/optimizaciya" target="_blank">оптимизации</a> <a href="/tag/php" target="_blank">PHP</a>, которые мне доводилось читать, до сих пор храню ссылку на нее в bookmark&#8217;ах, именно она и выступала в роли <em><a href="http://mgccl.com/php-speed-freaks" target="_blank" rel="nofollow">одного из основных источников информации</a></em> для этого текста. В качестве возможных вариантов продолжения чтения про <a href="/tag/php" target="_blank">PHP</a> хотелось бы предложить Вам соответствующие <a href="/php" target="_blank">раздел сайта</a>, <a href="/dzhentelmenskij-nabor-php-programmista/" target="_blank">серию статей</a>, <a href="/tag/php" target="_blank">тэг</a> и <a href="/feed" target="_blank">RSS-ленту</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/php/na-puti-k-idealu/feed/</wfw:commentRss>
		<slash:comments>33</slash:comments>
		</item>
		<item>
		<title>Шаблонизация</title>
		<link>http://www.insight-it.ru/programmirovanie/php/shablonizaciya/</link>
		<comments>http://www.insight-it.ru/programmirovanie/php/shablonizaciya/#comments</comments>
		<pubDate>Sat, 26 Jan 2008 12:25:36 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[blitz]]></category>
		<category><![CDATA[Smarty]]></category>
		<category><![CDATA[template]]></category>
		<category><![CDATA[template engine]]></category>
		<category><![CDATA[интернет]]></category>
		<category><![CDATA[ООП]]></category>
		<category><![CDATA[разработка]]></category>
		<category><![CDATA[шаблон]]></category>
		<category><![CDATA[шаблонизация]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/programming/php/shablonizaciya/</guid>
		<description><![CDATA[Наверняка Вы часто замечали, что в пределах одного сайта все (или покрайней мере большинство) страниц имеют много общего: структуру, расположение элементов, дизайн и так далее. Основным различием обычно является лишь содержание. Естественно, что делается это не спроста: именно общие компоненты сайта создают в голове посетителей тот самый образ, который производит общее подсознательное впечатление о сайте, [...]]]></description>
			<content:encoded><![CDATA[<p>Наверняка Вы часто замечали, что в пределах одного сайта все (или покрайней мере большинство) страниц имеют много общего: структуру, расположение элементов, дизайн и так далее. Основным различием обычно является лишь содержание. Естественно, что делается это не спроста: именно общие компоненты сайта создают в голове посетителей тот самый образ, который производит общее подсознательное впечатление о сайте, а также позволяет посетителям отличать сайт А от сайта Б.</p>
<p>Продолжая разговор, начатый еще в <a target="_blank" href="/programming/php/obshhaemsya-s-bazojj-dannykh/">одной</a> из <a target="_blank" href="/dzhentelmenskij-nabor-php-programmista">предыдущих статей</a>, рассмотрим организацию интерфейса между двумя другими составляющими практически любого интернет-проекта: скриптов (все так же на примере PHP) и страницами, отправляемыми посетителям посредством http-сервера.</p>
<p><span id="more-32"></span></p>
<p>С точки зрения веб-разработчика было бы как минимум не логично мешать в кучу постоянные части страниц с динамическими. Для этого существует множество причин, в том числе, например, экономия вычислительной мощности сервера на отсутствии необходимости каждый раз заново генерировать статичные элементы или неминуемое сокращение объемов кода, который необходимо написать, в случае если статический и динамический контент разделены. Отделенную подобным образом статическую часть страниц (слегка модифицированную с целью обозначить правила, по которым будет проводиться &quot;заполнение&quot; ее динамическим контентом) принято называть словом <em>&quot;шаблон&quot;</em>.</p>
<p>Наверняка у Вас уже возникло два вполне логичных вопроса:</p>
<ol>
<li>Как можно разделить таким образом контент?</li>
<li>Как потом восстановить страницу в исходном виде?</li>
</ol>
<p>Вариантов ответа на каждый из них можно придумать множество: начиная с банальных вариаций на тему применения <strong>echo</strong>, заканчивая применением достаточно серьезных готовых решений вроде широкораспространенного <a href="http://smarty.php.net" target="_blank" rel="nofollow"><strong>Smarty</strong></a> или существенно более эффективного <a href="http://alexeyrybak.com/blitz/blitz_ru.html" target="_blank" rel="nofollow"><strong>Blitz</strong></a>. Каждый из них имеет свои сильные и слабые стороны, но в целом любой из них можно оценить по двум критериям: производительности и удобстве организации кода.</p>
<p>Какие-либо цифры оценки производительности приводить не буду, так как, во-первых, в Сети можно найти много benchmark&#8217;ов, посвященных этой теме, а, во-вторых, просто-напросто вовсе не о цифрах я хотел с Вами поговорить. Как известно самым производительным по крайней мере с теоретической точки зрения является метод под названием <strong>$php$ mess</strong>, заключается он в следующем: вся страница размещается в рамках одного файла, при этом статическая часть документа пишется просто &quot;как есть&quot; в соответствии с необходимым стандартом, а изменяемые части организуются размещенным в необходимых местах PHP-кодом, окруженным стандартной конструкцией <strong>&lt;?php&nbsp;&nbsp;&nbsp;?&gt;</strong>. Но огромнейший недостаток очевиден &#8212; огромное количество информации расположенной в одном файле, при отсутствии какого-либо более четкого разделения PHP-кода и остального содержимого, чем указанная выше конструкция, приводит к постоянной путанице в коде, а также существенным затратам времени программиста при попытках исправить ту или иную часть документа.</p>
<p>На противоположной стороне нашей шкалы <em>удобство-производительность</em> я бы расположил уже упомянутое выше решение под названием <strong>Smarty</strong>. Представляет оно собой целую систему, реализованную также на PHP, и предоставляющую огромное количество возможностей по решению нашей задачи. Шаблоны хранятся в отдельных файлах, для определения мест расположения динамического контента используется специальный синтаксис, который прост как три копейки, так как разрабатывался с расчетом не на программистов, а по принципу &quot;чем проще, тем лучше&quot;. Именно этот факт сделал <strong>Smarty</strong> одним из самых (если не самым) распространенных движков шаблонизации (или как их принято правильно называть &quot;Template Engine&quot;). Но, к сожалению, за удобство приходится платить, в этом случае производительностью: вся система сама по себе громоздка и состоит из множества файлов, между которыми все данные так или иначе передаются, а так как написано она на PHP (который является далеко не самым производительным языком программирования, в основном в силу своей интерпритируемости и некоторых других особенностей), конкуренции в плане производительности многим другим вариантам решения нашей задачи <strong>Smarty</strong> составить не в состоянии.</p>
<p>Одним из лучших &quot;компромиссных&quot; вариантов, которые доступны на данный момент, могу назвать также упомянутый выше <strong>Blitz</strong>. Реализован он в виде модуля PHP, написанного на языке <strong>C</strong>, что является залогом его отличной производительности. При этом общая его концепция близка к <strong>Smarty</strong>: шаблоны также хранятся в отдельных файлах и подчинены незамысловатому синтаксису (который вообще можно понять и запомнить буквально за 15-20 минут, прочитав статью, <a href="http://alexeyrybak.com/blitz/blitz_ru.html" target="_blank" rel="nofollow">ссылку на которую</a> я уже приводил выше), а в PHP-скриптах после установки становится доступен специальный класс для управления модулем. Но основное достоинство этого решения является одновременно и его основным недостатком &#8212; редкий хостинг имеет этот модуль в списке предустановленных (видимо в силу своей не очень обширной известности, обусловленной ), а доступ к http-серверу и PHP-интерпретатору, который необходим для установки PHP-модулей, предоставляется чаще всего только на дорогих тарифах виртуального хостинга или на различных вариантах VPS или арендуемых серверов.</p>
<p>Помимо этого некоторые энтузиасты берутся на написание &quot;собственных&quot; <em>Template Engine</em>, базирующихся на различных вариантов использования PHP-функций вроде <strong>preg_replace</strong>. Если честно такие попытки редко заканчиваются успехом: в лучшем случае удается добиться удобства использования самим разработчиком, но чаще всего в ущерб производительности. Заниматься подобными экспериментами я Вам не советую, вместо этого я предлагаю написать <em>&quot;обертку&quot;</em> к приглянувшемуся распространенному <em>Template Engine</em>, что позволит не только сделать его использование более удобным конкретно для Вас, но и позволит заменить его на другой с минимальными затратами сил и времени (например в случае, если модуль <strong>Blitz</strong> недоступен).</p>
<h3>Разрабатываем &quot;обертку&quot;</h3>
<p>Сразу скажу, что цели привести конкретный пример пригодного для реального использования кода я перед собой не ставлю в этой части моего повествования. Я лишь хочу показать направление, в котором можно провести разработку с целью облегчения собственной же жизни, т.е. предоставить Вам альтернативу простому использованию тех или иных решений в том виде, в котором они предоставлены разработчиками.</p>
<p>Если Ваш выбор всетаки пал на написание &quot;оболочки&quot;, не смотря на принесение в жертву несущественной части производительности, то стоит для начала определиться: а что же мы будем &quot;заворачивать&quot;? В качестве примера я, пожалуй, буду использовать <strong>Blitz</strong>, как самый оптимальный вариант (по крайней мере с моей точки зрения).</p>
<p>Начать стоит как обычно с пустой заготовки для класса:</p>
<pre lang="PHP">
<?php
class Template
{
  private $engine; // экземпляр оригинального класса управления модулем
  public function __construct($template)
  {
    //можно сразу указать указать путь к папке с шаблонами
    $this->engine=new Blitz('./template/'.$template.'.tpl');
  }
}
?>
</pre>
<p>Далее следует решить какие все же модификации мы будем производить для собственного удобства над стандартным решением. Попробую привести несколько примеров в отношении <strong>Blitz</strong>, для начала хочу обратить внимание, что при внимательном прочтении все той же статьи от разработчика этого шаблонизатора, можно обнаружить, что модуль показывает более высокие показатели производительности при однократном вызове метода <em>set</em>. Достичь это можно выполнением этого метода с указанием в качестве одного из входных параметров &quot;многоуровнего&quot; массива, составленного специальным образом (надеюсь Вы все же к этому моменту уже успели прочитать неоднократно упоминавшуюся статью, и представляете принцип работы модуля). Написание механизма составления такого массива позволит как сократить время разработки, так и сэкономит драгоценные миллисекунды, вычитаемые из свободного времени посетителей сайта в процессе генерации страницы.</p>
<p>В любом случае понадобится переменная для его хранения:</p>
<pre lang="PHP">
<?php
class Template
{
  private $engine; // экземпляр оригинального класса управления модулем
  private $array;  // собираемый массив
  public function __construct($template)
  {
    $this->array=array();
    //можно сразу указать указать путь к папке с шаблонами
    $this->engine=new Blitz('./template/'.$template.'.tpl');
  }
}
?>
</pre>
<p>А также метод, переопределяющий стандартный <em>set</em> на метод, добавляющий новые значения к нашему массиву (хотя можно и любое другое понравившееся название использовать):</p>
<pre lang="PHP">
function set($caption,$value)
{
  $this->array[$caption]=$value;
}
</pre>
<p>После чего оригинальный <em>set</em> можно использовать уже непосредственно перед <em>parse</em>, с указанием уже собранного массива в качестве параметра. За компанию позволю произвести себе еще одну модификацию: в подавляющем большинстве случаев <em>parse</em> используется в совокупности с <strong>echo</strong>, чтобы не указывать каждый раз это слово &#8212; можно включить его прямо в наш класс:</p>
<pre lang="PHP">
function parse()
{
  if(count($this->array))$this->engine->set($this->array);
  echo $this->engine->parse();
}
</pre>
<p>Еще одним возможным вариантом модификации может стать обработка всех (или какой-то части, если есть необходимость) динамических данных с помощью какой-либо функции, например это актуально для <strong>htmlspecialchars</strong>:</p>
<pre lang="PHP">
function set($caption,$value)
{
  $this->array[$caption]=$this->html($value);
}
function rawset($caption,$value)
{
  $this->array[$caption]=$value;
}
private function html($array)
{
  if(is_array($array))
  {
    foreach($array as $caption => $value)
    $value=$this->html($value);
    return $array;
  }
  else return htmlspecialchars($array,ENT_QUOTES);
}
</pre>
<p>Как нетрудно заметить, в методе используется рекурсия, так как структура передаваемых параметром массивов неизвестна.</p>
<p>Надеюсь написанный выше текст подтолкнет Вас к действию или хотябы заставит задуматься над имеющимся выбором, если же Вы читали его лишь &quot;для общего развития&quot;, то тем более хочется сказать Вам огромное <em>Спасибо</em><em> за то, что дочитали до конца это повествование.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/php/shablonizaciya/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>Общаемся с базой данных</title>
		<link>http://www.insight-it.ru/programmirovanie/php/obshhaemsya-s-bazojj-dannykh/</link>
		<comments>http://www.insight-it.ru/programmirovanie/php/obshhaemsya-s-bazojj-dannykh/#comments</comments>
		<pubDate>Wed, 16 Jan 2008 19:04:09 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[БД]]></category>
		<category><![CDATA[интерфейс]]></category>
		<category><![CDATA[кодинг]]></category>
		<category><![CDATA[ООП]]></category>
		<category><![CDATA[СУБД]]></category>
		<category><![CDATA[технология]]></category>
		<category><![CDATA[хранение данных]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/programming/php/obshhaemsya-s-bazojj-dannykh/</guid>
		<description><![CDATA[На этот раз хочется обсудить такой одновременно важный и несложный момент в реализации работы любого интернет-проекта, как координации работы Ваших скриптов с СУБД. Если подойти к этому вопросу &#34;в лоб&#34;, то код станет очень неудобен как для понимания, так и для использования: код станет переполнен различными функциями соединения с БД, отправки запросов, преобразования результатов запросов [...]]]></description>
			<content:encoded><![CDATA[<p>На этот раз хочется обсудить такой одновременно важный и несложный момент в реализации работы любого интернет-проекта, как координации работы Ваших скриптов с СУБД.</p>
<p>Если подойти к этому вопросу &quot;в лоб&quot;, то код станет очень неудобен как для понимания, так и для использования: код станет переполнен различными функциями соединения с БД, отправки запросов, преобразования результатов запросов в массивы PHP, подсчета строк, которые затронул запрос, а также многие и многие другие.</p>
<p>Для желающих минимизировать подобного рода издержки в процессе написания кода, хочу предложить один из, на мой взгляд, самых эффективных способов решения этой проблемы.</p>
<p><span id="more-26"></span></p>
<p>Этим способом будет являться написание класса, реализующего интерфейс между СУБД и PHP-скриптами. Для начала стоит определиться с ассортиментом функций, которые будет призван выполнять наш класс:</p>
<ul>
<li><em>установка соединения</em>, а также проверка успешности выполнения этого действия;</li>
<li><em>отправка запросов</em>, как заданных извне так и, возможно, из какого-либо ассортимента заранее написанных запросов;</li>
<li><em>обработка результатов запросов</em>, не ограничивающаяся одним SELECT, должны быть предоставлены методы обработки результатов любых видов запросов (или хотябы большинства).</li>
</ul>
<p>Вполне очевидным является тот факт, что методы этого класса будут использоваться практически повсеместно в большинстве проектов. Вследствии чего становится нецелесообразным создание объекта нашего класса и передача его по всем функциям и методам всех скриптов, в таких случае намного предпочтительнее делать владельцем методов и переменных сам класс, а не экземпляр класса, с помощью ключевого слова <strong>static</strong>. Это позволит пользоваться услугами нашего класса из любого места кода. Приступим-с собственно к кодингу, начать стоит с заготовки пустого класса:</p>
<pre lang="PHP">
<?php
class SQL
{
  private static $connection; // соединение с СУБД
  static function connect()  // установка соединения
  {

  }
  static function query($str,$bool=false) // произвольный запрос
  {

  }

?>
</pre>
<p>В зависимости от предпочитаемой Вами СУБД набор конкретных функций, используемых в реализации нашего класса, будет вариироваться. В большинстве случаев предпочитаю пользоваться PostgreSQL, на это причин у меня несколько, но это тема для отдельного разговора. Благодаря этому факту приводимый в качестве примера код будет использовать функции для работы именно с этой СУБД. Для поклонников же других этот систем вопрос в подавляющем большинстве случаев заключается лишь в замене этих функций на аналогичные из других модулей PHP, например для популярной и широкораспространенной MySQL достаточно будет всеголишь пройтись автозаменой <strong>pg_ =&gt; mysql_</strong> и слегка подредактировать параметры некоторых функций.</p>
<p>Перейдем к реализации установления соединения с СУБД, не стоит ожидать увидеть здесь ничего необычного:</p>
<pre lang="PHP">
static function connect()  // установка соединения
{
  self::$connection=pg_pconnect("host=localhost dbname=pgsql user=pgsql password=MyPassword");
  // не забываем менять указанные данные для авторизации на правильные
  if(!isset(self::$connection))
  {
    echo "Сайт не работает по техническим причинам.Просим прощения за доставленные неудобства.";
    exit;	// ни в коем случае не выводим более информативных сообщений об ошибке, чем это
  }
}
</pre>
<p>А вот с отправкой и обработкой результатов запросов ситуация далеко не так однозначна. Помимо простой передачи самого текста запроса в СУБД, необходимо правильно определить тип запроса и в соответствии с этим обработать результат. Можно конечно попытаться сделать это автоматически на основе вытаскивания первого слова из текста запроса, но мне всетаки кажется более предпочтительным определение &quot;вручную&quot; желаемого вида представление результата. Выполнение произвольных запросов может выглядеть, например, следующим образом:</p>
<pre lang="PHP">
static function query($str,$bool=false) // произвольный запрос
{
  //echo $str.""; // очень удобно на стадии разработки в процессе поиска ошибок
  $result=@pg_query(self::$connection,$str); // @ - для сокрытия теоретически возможных ошибок
  // or die('Query failed: '.pg_last_error());
  // не забываем убирать в комментарий в финальном варианте проекта
  // или совсем удалять
  if($result)  // Если получен результат, отличный от false
  {
    if($bool)  // Если выбран результат в виде boolean
    {
      return true;
    }
    else  // Если выбран результат в виде массива
    {
      $n=pg_num_rows($result);	// для создания универсального формата массива
      if($n==1)return pg_fetch_array($result,0,PGSQL_ASSOC);
      else  // даже когда результат содержит только одну строку
      {
        $j=pg_num_rows($result);
        $list=array();
        for($i=0;$i<$j;$i++)
        $list[]=pg_fetch_array($result,$i,PGSQL_ASSOC);
        return $list;
      }
    }
  }else return false;
}
</pre>
<p>Помимо базовой отправки запросов, в некоторых случаях имеет смысл написать несколько методов, отправляющих частоиспользуемые запросы, что в некоторых случаях позволяет сократить объем и уменьшить нагроможденность кода. Хоть я и предпочитаю не пользоваться такими вещами, но привести пример такого рода метода все же стоит:</p>
<pre lang="PHP">
static function selectAll($table)
{ // пример метода отправки чаcтоиспользуемых запросов
  return self::query("select * from ".$table.";");
}
</pre>
<p>Если чувствуете необходимость в подобных функциях, можно написать огромное количество, все ограничивается лишь Вашим воображением и знаниями SQL.</p>
<p>Что ж, осталось лишь собрать весь код в <a href="/wp-content/uploads/source/sql.class.phps">единый листинг</a>:</p>
<pre lang="PHP">
<?php
class SQL
{
  private static $connection; // соединение с СУБД
  static function connect()  // установка соединения
  {
    self::$connection=pg_pconnect("host=localhost dbname=pgsql user=pgsql password=MyPassword");
    // не забываем менять указанные данные для авторизации на правильные
    if(!isset(self::$connection))
    {
      echo "Сайт не работает по техническим причинам.Просим прощения за доставленные неудобства.";
      exit;	// ни в коем случае не выводим более информативных сообщений об ошибке, чем это
    }
  }
  static function query($str,$bool=false) // произвольный запрос
  {
    //echo $str.""; // очень удобно на стадии разработки в процессе поиска ошибок
    $result=@pg_query(self::$connection,$str); // @ - для сокрытия теоретически возможных ошибок
    // or die('Query failed: '.pg_last_error());
    // не забываем убирать в комментарий в финальном варианте проекта
    // или совсем удалять
    if($result)  // Если получен результат, отличный от false
    {
      if($bool)  // Если выбран результат в виде boolean
      {
        return true;
      }
      else  // Если выбран результат в виде массива
      {
         $n=pg_num_rows($result);	// для создания универсального формата массива
         if($n==1)return pg_fetch_array($result,0,PGSQL_ASSOC);
         else  // даже когда результат содержит только одну строку
         {
           $j=pg_num_rows($result);
           $list=array();
           for($i=0;$i<$j;$i++)
           $list[]=pg_fetch_array($result,$i,PGSQL_ASSOC);
           return $list;
         }
      }
    }else return false;
  }
  static function selectAll($table)
  { // пример метода отправки чаcтоиспользуемых запросов
    return self::query("select * from ".$table.";");
  }
}
?>
</pre>
<p>Вот так вот оно и выглядит в простейшем варианте, дорабатывать под собственные нужды код можно до бесконечности естественно, но в большинстве случаев даже такой реализации вполне должно хватать.</p>
<p>Эта статья является частью <a href="/dzhentelmenskij-nabor-php-programmista">серии статей "Джентельменский набор PHP программиста"</a>, если Вам понравилась эта статья то очень вероятно, что Вам придутся по душе и остальные статьи.</p>
<p>Не забываем <a href="/feed" target="_blank">подписываться на RSS блога</a>, а также на <a href="/comments/feed" target="_blank">ленту комментариев</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/php/obshhaemsya-s-bazojj-dannykh/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>Защита интернет-ресурсов в картинках</title>
		<link>http://www.insight-it.ru/programmirovanie/php/zashhita-internet-resursov-v-kartinkax/</link>
		<comments>http://www.insight-it.ru/programmirovanie/php/zashhita-internet-resursov-v-kartinkax/#comments</comments>
		<pubDate>Sun, 13 Jan 2008 19:30:00 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[captcha]]></category>
		<category><![CDATA[OCR]]></category>
		<category><![CDATA[online]]></category>
		<category><![CDATA[защита интернет-ресурсов]]></category>
		<category><![CDATA[интернет]]></category>
		<category><![CDATA[класс]]></category>
		<category><![CDATA[код]]></category>
		<category><![CDATA[кодинг]]></category>
		<category><![CDATA[ООП]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/programming/php/zashhita-internet-resursov-v-kartinkax/</guid>
		<description><![CDATA[Этой статьей мне хотелось бы открыть мою первую серию статей &#34;Джентельменский набор PHP программиста&#34;. Как и во всей остальной серии здесь пойдет речь о программировании на PHP для интернет-проектов, но в каждой статье я буду выбирать один узкий аспект и на протяжении всей статьи буду стараться показать возможные варианты его реализации и применения. Сегодня таким [...]]]></description>
			<content:encoded><![CDATA[<p>Этой статьей мне хотелось бы открыть мою первую <a href="/dzhentelmenskij-nabor-php-programmista/" target="_blank">серию статей &quot;Джентельменский набор PHP программиста&quot;</a>. Как и во всей остальной серии здесь пойдет речь о программировании на PHP для интернет-проектов, но в каждой статье я буду выбирать один узкий аспект и на протяжении всей статьи буду стараться показать возможные варианты его реализации и применения.</p>
<p>Сегодня таким аспектом станет защита интернет-ресурса от возможного возникновения нежелательного контента со стороны пользователей с помощью технологии captcha (точнее о &quot;графическом&quot; варианте ее реализации), о которой уже <a href="/net/otkuda-voznikaet-spam-i-kak-s-nim-borotsya/" target="_blank">неоднократно шла речь</a>.</p>
<p><span id="more-23"></span></p>
<p>Начать имеет смысл с небольшого напоминания о принципе работы этой технологии: перед потенциальным посетителем ставится некое препятствие, которое ему необходимо преодолеть для  продолжения работы с интернет-ресурсом. Существует множество <a target="_blank" href="/net/7-sposobov-zashhitit-svoj-internet-resurs-ot-nezhelatelnoj-informacii/">вариантов такого рода препятствий</a>. Как уже упоминалось, сегодня мы будем реализовывать только один наиболее распространенный тип &#8212; &quot;графический&quot;. В простейшем случае он представляет собой просьбу переписать с изображения некий набор символов. В процессе генерирования изображения, символы сильно искажаются с целью предотвращения возможности их распознавания любой программой с помощью технологии OCR.</p>
<h3>Подготовка</h3>
<p>Прежде чем начать писать код стоит более детально осознать какая же цель перед нами стоит: нам необходимо написать скрипт, генерирующий искаженное изображение некоторого набора символов и незаметно для пользователя передающее этот набор какому-либо другому скрипту, который нас пока мало интересует, но ясно лишь, что собственно проверкой будет заниматься именно он на основе данных полученных от пользователя и нашего скрипта. Способов исказить текст существует огромное количество, в ходе написания статьи постараюсь упомянуть несколько самых эффективных и широкоиспользуемых из них.</p>
<p>В первую очередь стоит подготовить некий каркас кода, который мы будем впоследствии заполнять. Он будет состоять из двух частей:</p>
<ol>
<li>Описание класса, генерирующего изображение</li>
<li>Файл, который будет вызываться browser&#8217;ом. В нем будет подключено описание нашего класса, выбор настроек данного конкретного изображения и выполнено создание объекта класса, в соответствии с выбранными настройками.</li>
</ol>
<p>Для начала давайте определимся со списком параметров, которые будет иметь наш класс. Во-первых, нужно решить какой текст будет генерироваться, самый простой и распространенный вариант &#8212; просто четыре цифры, я в примере на нем и остановлюсь, а реально же можно использовать абсолютно любые приходящие в голову варианты. Во-вторых, размеры изображения и текста &#8212; их лучше подобрать фиксированными так, чтобы было максимально читабельно, при минимальных размерах изображения, но при желании можно сделать и возможность изменения их извне. Последним в списке параметров будет цвет фона и текста &#8212; их как раз лучше задавать вне класса, так как основным действием, необходимым при переносе этого скрипта с одного сайт на другой &#8212; подбор используемых цветов таким образом, чтобы изображение смотрелось не очень ужасно при текущем варианте дизайна, изменения в других параметрах требуются на порядок реже.</p>
<p>Итак, создание объекта будем производить максимально простым способом, параметрами укажем белый и черный цвета.</p>
<pre lang="php">
<?php
require_once('captcha.class.php');	// указываем правильный путь к описанию класса
new Captcha(array(255,255,255),array(0,0,0));
?>
</pre>
<p style="font-size: 75%;">(для удобного чтения таких вставок исходного кода достаточно нажать на нее один раз левой кнопкой мыши и использовать стрелки &larr; и &rarr; на клавиатуре)</p>
<p>Заготовка для самого класса будет выглядеть примерно следующим образом (предположим, что он хранится в файле <u>captcha.class.php</u>):</p>
<pre lang="PHP">
<?php
class Captcha
{
   private $string;	// генерируемый текст
   private $bgcol;	// основной цвет фона
   private $fgcol;	// основной цвет текста
   private $height;	// высота изображения
   private $width;	// ширина изображения
   function __construct($bgcol,$fgcol)  // конструктор, вызывается при создании экземпляра класса
   {

   }
}
?>
</pre>
<h3>Задаем параметры</h3>
<p>Первым делом при создании объекта необходимо задать остальные параметры, размеры изображения можно указать прямо в конструкторе, а для генерации текста лучше написать отдельную функцию:</p>
<pre lang="PHP">
<?php
class Captcha
{
   private $string;	// генерируемый текст
   private $bgcol;	// основной цвет фона
   private $fgcol;	// основной цвет текста
   private $height;	// высота изображения
   private $width;	// ширина изображения
   function __construct($bgcol,$fgcol)  // конструктор, вызывается при создании экземпляра класса
   {
	$this->width=250;
	$this->height=80;
	$this->fgcol=$fgcol;
	$this->bgcol=$bgcol;
	$this->generateSymbols();
   }
   private function generateSymbols()   // генерация четырех цифр
   {
      $this->string=$this->leadingZero(rand()%10000,4);
   }
   private function leadingZero($num,$length) // дополнения числа num лидирующими нулями
   {						// до длины length
	$str=strrev($num);
	for($i=strlen($str);$i<$length;++$i)$str.="0";
	return strrev($str);
   }
}
?>
</pre>
<p>Этих данных нам должно хватить для написания функции, генерирующей изображение.</p>
<h3>Генерируем изображение</h3>
<p>Если забыть, что текст необходимо искажать, то функция, генерирующая изображение выглядела бы просто как:</p>
<pre lang="PHP">
private function generateImage()  // генерация изображения
{
   $im=@imagecreatetruecolor($this->width,$this->height);
   $bcol=imagecolorallocate($im,$this->bgcol[0],$this->bgcol[1],$this->bgcol[2]);
   $fcol=imagecolorallocate($im,$this->fgcol[0],$this->fgcol[1],$this->fgcol[2]);
   imagefill($im,0,0,$bcol);
   imagettftext($im,40,10,20,25,$fcol,"./font/font_name.ttf",$this->string));
   header('Content-Type: image/png');
   imagepng($im);
   imagedestroy($im);
}
</pre>
<p><span style="background-color: Yellow;">В данном методе используются функции модуля PHP под названием GD, основывающегося на одноименной библиотеке, убедитесь, что на Вашем хостинге этот модуль установлен.</span></p>
<p>Реально же ей пользоваться не стоит &#8212; такое изображение с легкостью поддается OCR. Полученный текст необходимо тем или иным образом исказить. Для вывода изображения используется формат PNG, но никто не мешает воспользоваться JPEG или GIF, для этого достаточно заменить везде png на название соответствующего формата.</p>
<h3>Искажаем текст</h3>
<p>Вот списочек тех, способов искажения текста, которыми я буду пользоваться в примере, пользоваться всеми сразу естественно никто не заставляет, да и включив воображение можно придумать много модификаций приведенных мной способов или абсолютно других:</p>
<ul>
<li><i>использование нестандартных шрифтов</i> &#8212; функция imagettftext позволяет использовать произвольный шрифт в формате Truetype, чем и необходимо воспользоваться. В Сети можно найти огромное количество бесплатных шрифтов в этом формате. По возможности стоит выбирать шрифты, максимально не похожие на любой стандартный, но при этом легко читающиеся.</li>
<li><i>использование нескольких шрифтов</i> &#8212; сделав подборку подходящих шрифтов, можно не останавливаться на каком-то одном, а сделать выбор текущего шрифта случайным из списка.</li>
<li><i>случайный выбор цветов</i> &#8212; усложняет работу OCR и в большинстве случаев не сильно мешает восприятию человеком.</li>
<li><i>случайное расположение символов</i> &#8212; еще один способ усложнить работу программам, пытающимся прочитать текст.</li>
<li><i>неравномерный фон</i> &#8212; изобразив на фоне какой-либо абстрактный набор любых фигур, можно заставить программу-посетителя подумать что какая-то часть из них является символом. Например, пересечение двух прямых линий часто распознается как буква T или L. Неплохим вариантом является написание на фоне других символов другим цветом, сильно отличающимся от основного и близким к цвету фона.</li>
</ul>
<p>Для начала этого вполне хватит, перейдем к реализации, в комментариях постараюсь указывать все особенности:</p>
<pre lang="PHP">
private function generateImage() // генерация изображения
{
   $im=@imagecreatetruecolor($this->width,$this->height);  // создаем пустое изображение
   $mcol=imagecolorallocate($im,$this->fgcol[0]+rand()%100+80,$this->fgcol[1]+rand()%30+150,$this->fgcol[2]-rand()%55); // выбираем случайным образом
   $kcol=imagecolorallocate($im,$this->fgcol[0]+rand()%100+80,$this->fgcol[1]+rand()%30+150,$this->fgcol[2]-rand()%20); // несколько цветов
   $lcol=imagecolorallocate($im,$this->bgcol[0]-rand()%20,$this->bgcol[1]-rand()%20,$this->bgcol[2]-rand()%20);
   $bcol=imagecolorallocate($im,$this->bgcol[0],$this->bgcol[1],$this->bgcol[2]);
   $fcol=imagecolorallocate($im,$this->fgcol[0],$this->fgcol[1],$this->fgcol[2]);
   imagefill($im,0,0,$bcol);  // заполняем изображение фоном
   $array=array(6,7,6,6,20,20,25,26,31,32,37,39,41); // список названий подходящих шрифтов
   $n=$array[rand()%count($array)];  // наугад выбираем из них один
   $m=rand()%50+1;
   $k=rand()%50+1;
   for($i=0;$i<$m;++$i)
   imageline($im,0,rand()%$this->height,$this->width,rand()%$this->height,$lcol); // создаем на фоне несколько линий
   for($i=0;$i<$k;++$i)
   imageline($im,rand()%$this->width,0,rand()%$this->width,$this->height,$lcol); // и еще несколько
   /*
   Генерируем текст: две строки на фон, а также интересующие нас символы по одному.
   */
   imagettftext($im,rand()%20+40,rand()%100-50,rand()%$this->height*0.8,rand()%50+25,$kcol,"./font/".$k.".ttf",$this->randomString(rand()%15));
   imagettftext($im,rand()%40+35,rand()%70-35,rand()%$this->height*0.8,rand()%25+25,$mcol,"./font/".$m.".ttf",$this->randomString(5+rand()%4));
   for($i=0;$i<strlen($this->string);++$i)
   imagettftext($im,rand()%10+33,rand()%70-35,15+$i*$this->width/5*1.1+rand()%5,rand()%7+$this->height*0.73,$fcol,"./font/".$n.".ttf",$this->string[$i]);
   for($i=0;$i<$m/10;++$i)
   imageline($im,0,rand()%$this->height,$this->width,rand()%$this->height,$mcol); // еще линии
   for($i=0;$i<$k/4;++$i)
   imageline($im,rand()%$this->width,0,rand()%$this->width,$this->height,$mcol);  // и еще немного
   for($i=0;$i<$k/6;++$i)
   imageline($im,rand()%$this->width,0,rand()%$this->width,$this->height,$fcol);  // и еще чуть-чуть
   header('Content-Type: image/png');
   imagepng($im);
   imagedestroy($im);
}
private function randomString($length)  // генерируем случайный набор символов заданной длины
{
  $list="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ!@#$%^&#038;**()-=_+.,<>/\|;:";
  for($i=0,$str="";$i<$length;++$i)$str.=substr($list,mt_rand(0,strlen($list)-1),1);
  return $str;
}
</pre>
<p>Стоит заметить, что конкретные цифры необходимо подбирать индивидуально, в примере они указаны абсолютно произвольно. Использование конкретно этих же цифр приведет к далеко не самым лучшим результатам.</p>
<h3>Сборка</h3>
<p>Не стоит забывать, что помимо генерации самого изображения, необходимо передать написанный текст другому скрипту, который будет сверять данные. Удобнее всего это делать через глобальный массив <span style="color: rgb(0, 0, 255);">$_SESSION</span>.</p>
<p>Собрав все написанное выше, и учтя передачу текста, можно получить следующий класс:</p>
<pre lang="PHP">
class Captcha
{
   private $string;	// генерируемый текст
   private $bgcol;	// основной цвет фона
   private $fgcol;	// основной цвет текста
   private $height;	// высота изображения
   private $width;	// ширина изображения
   function __construct($bgcol,$fgcol)  // конструктор, вызывается при создании экземпляра класса
   {
      $this->width=250;
      $this->height=80;
      $this->fgcol=$fgcol;
      $this->bgcol=$bgcol;
      $this->generateSymbols();
      $this->generateImage();
   }
   private function generateImage() // генерация изображения
   {
      $im=@imagecreatetruecolor($this->width,$this->height);  // создаем пустое изображение
      $mcol=imagecolorallocate($im,$this->fgcol[0]+rand()%100+80,$this->fgcol[1]+rand()%30+150,$this->fgcol[2]-rand()%55); // выбираем случайным образом
      $kcol=imagecolorallocate($im,$this->fgcol[0]+rand()%100+80,$this->fgcol[1]+rand()%30+150,$this->fgcol[2]-rand()%20); // несколько цветов
      $lcol=imagecolorallocate($im,$this->bgcol[0]-rand()%20,$this->bgcol[1]-rand()%20,$this->bgcol[2]-rand()%20);
      $bcol=imagecolorallocate($im,$this->bgcol[0],$this->bgcol[1],$this->bgcol[2]);
      $fcol=imagecolorallocate($im,$this->fgcol[0],$this->fgcol[1],$this->fgcol[2]);
      imagefill($im,0,0,$bcol);  // заполняем изображение фоном
      $array=array(6,7,6,6,20,20,25,26,31,32,37,39,41); // список названий подходящих шрифтов
      $n=$array[rand()%count($array)];  // наугад выбираем из них один
      $m=rand()%50+1;
      $k=rand()%50+1;
      for($i=0;$i<$m;++$i)
      imageline($im,0,rand()%$this->height,$this->width,rand()%$this->height,$lcol); // создаем на фоне несколько линий
      for($i=0;$i<$k;++$i)
      imageline($im,rand()%$this->width,0,rand()%$this->width,$this->height,$lcol); // и еще несколько
      /*
      Генерируем текст: две строки на фон, а также интересующие нас символы по одному.
      */
      imagettftext($im,rand()%20+40,rand()%100-50,rand()%$this->height*0.8,rand()%50+25,$kcol,"./font/".$k.".ttf",$this->randomString(rand()%15));
      imagettftext($im,rand()%40+35,rand()%70-35,rand()%$this->height*0.8,rand()%25+25,$mcol,"./font/".$m.".ttf",$this->randomString(5+rand()%4));
      for($i=0;$i<strlen($this->string);++$i)
      imagettftext($im,rand()%10+33,rand()%70-35,15+$i*$this->width/5*1.1+rand()%5,rand()%7+$this->height*0.73,$fcol,"./font/".$n.".ttf",$this->string[$i]);
      for($i=0;$i<$m/10;++$i)
      imageline($im,0,rand()%$this->height,$this->width,rand()%$this->height,$mcol); // еще линии
      for($i=0;$i<$k/4;++$i)
      imageline($im,rand()%$this->width,0,rand()%$this->width,$this->height,$mcol);  // и еще немного
      for($i=0;$i<$k/6;++$i)
      imageline($im,rand()%$this->width,0,rand()%$this->width,$this->height,$fcol);  // и еще чуть-чуть
      header('Content-Type: image/png');
      imagepng($im);
      imagedestroy($im);
   }
   private function generateSymbols()   // генерация четырех цифр
   {
      $this->string=$this->leadingZero(rand()%10000,4);
   }
   private function leadingZero($num,$length) // дополнения числа num лидирующими нулями
   {						// до длины length
      $str=strrev($num);
      for($i=strlen($str);$i<$length;++$i)$str.="0";
      return strrev($str);
   }
   private function randomString($length)  // генерируем случайный набор символов заданной длины
   {
      $list="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ!@#$%^&#038;**()-=_+.,<>/\|;:";
      for($i=0,$str="";$i<$length;++$i)$str.=substr($list,mt_rand(0,strlen($list)-1),1);
      return $str;
   }
}
</pre>
<p>Слегка доработав его и приведя в более подходящий вид, можно добиться генерации изображений, выглядящих например вот так:</p>
<p> <img style="margin: 6px; float: left;" title="Пример получающегося изображения" alt="CAPTCHA Sample" src="/wp-content/uploads/captcha-sample.png" />
<p>Специально не выкладываю уже доведенный до ума класс, чтобы у читателей не возникало желания просто взять и воспользоваться им, это приведет лишь к очередной серии captcha-клонов.</p>
<p>Для Вашего удобства предлагаю скачать используемые в примере <a href="/wp-content/uploads/source/captcha.class.phps">код класса</a> и <a href="/wp-content/uploads/source/captcha.phps">код вызываемого browser'ом файла</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/php/zashhita-internet-resursov-v-kartinkax/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

