<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Insight IT</title><link>https://www.insight-it.ru/</link><description></description><atom:link href="https://www.insight-it.ru/tag/php/feed/index.xml" rel="self"></atom:link><lastBuildDate>Thu, 20 Sep 2012 20:16:00 +0400</lastBuildDate><item><title>Вакансии: PHP и Python разработчики в Киеве</title><link>https://www.insight-it.ru//vacancy/2012/vakansii-php-i-python-razrabotchiki-v-kieve/</link><description>&lt;div class="card orange darken-3"&gt;
&lt;p&gt;&lt;div class="card-content white-text center"&gt;
&lt;strong&gt;Вакансии более не актуальны&lt;/strong&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Киевская команда разработчиков игр для социальных и мобильных платформ
Level UP ищет опытных специалистов по
веб-разработке на Python и PHP.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id="senior-python-developer"&gt;Senior Python Developer&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Задачи:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Разработка высоконагруженных веб-приложений;&lt;/li&gt;
&lt;li&gt;Разработка внутренних и публичных API;&lt;/li&gt;
&lt;li&gt;Разработка архитектуры комплексных сервисов;&lt;/li&gt;
&lt;li&gt;Конвертирование бизнес-задач в технические решения (R&amp;amp;D).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Требования:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Опыт разработки на &lt;strong&gt;Python&lt;/strong&gt; более 3х лет;&lt;/li&gt;
&lt;li&gt;Сильные навыки применения реляционных и нереляционных баз данных;&lt;/li&gt;
&lt;li&gt;Опыт в разработке высоконагруженных веб-приложений;&lt;/li&gt;
&lt;li&gt;Дружба с &lt;strong&gt;Linux&lt;/strong&gt; и &lt;strong&gt;Git&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;Хорошее знание клиентских технологий (HTML, CSS, Javascript);&lt;/li&gt;
&lt;li&gt;Опыт работы в команде.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Плюсом будет:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Опыт разработки комплексных проектов на &lt;strong&gt;PHP&lt;/strong&gt; или серверном
    &lt;strong&gt;Javascript&lt;/strong&gt; &lt;em&gt;(node.js)&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;Опыт разработки инструментов для верстки, автоматизации верстки,
    шаблонизаторов и пр.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="senior-php-developer"&gt;Senior PHP Developer&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Задачи:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Разработка высоконагруженных социальных веб-приложений;&lt;/li&gt;
&lt;li&gt;Работа в небольшой дружной команде до 15 человек;&lt;/li&gt;
&lt;li&gt;Решение нетривиальных задач и создание инструментов для внутреннего
    использования;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Требования:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Опыт разработки на &lt;strong&gt;PHP&lt;/strong&gt; более 3х лет;&lt;/li&gt;
&lt;li&gt;Опыт работы с &lt;strong&gt;MySQL&lt;/strong&gt; и &lt;strong&gt;MongoDB&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;Опыт в разработке высоконагруженных веб-приложений;&lt;/li&gt;
&lt;li&gt;Дружба с &lt;strong&gt;Linux&lt;/strong&gt; и &lt;strong&gt;Git&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;Хорошее знание клиентских технологий (HTML, CSS, Javascript);&lt;/li&gt;
&lt;li&gt;Опыт примененения функционарного и юнит тестирования.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Плюсом будет:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Опыт разработки на &lt;strong&gt;Python&lt;/strong&gt; или серверном &lt;strong&gt;Javascript&lt;/strong&gt;
&lt;em&gt;(nodejs)&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="usloviia-dlia-oboikh-vakansii"&gt;Условия для обоих вакансий&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Зарплата в диапазоне &lt;strong&gt;\$2500-4000&lt;/strong&gt; в месяц по результатам
    собеседования;&lt;/li&gt;
&lt;li&gt;Полный рабочий день в &lt;strong&gt;Киевском офисе&lt;/strong&gt;, иногородним помощь с
    переездом;&lt;/li&gt;
&lt;li&gt;За успешную рекомендацию специалиста по данным вакансиям компания
    выплачивает&amp;nbsp;&lt;strong&gt;бонус в размере $1000&lt;/strong&gt; так что сообщите своим
    знакомым, кому-то это может быть интересно.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="zainteresovalo"&gt;Заинтересовало?&lt;/h2&gt;
&lt;div class="card orange darken-3"&gt;
&lt;p&gt;&lt;div class="card-content white-text center"&gt;
&lt;strong&gt;Вакансии более не актуальны&lt;/strong&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Не лишним будет, если кроме резюме Вы напишите - почему Вам нравится
серверная разработка на Python или PHP и почему Вам интересна данная
вакансия. Плюс упомяните, пожалуйста, что Вы узнали о данной вакансии
через &lt;strong&gt;Insight IT&lt;/strong&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Thu, 20 Sep 2012 20:16:00 +0400</pubDate><guid>tag:www.insight-it.ru,2012-09-20:vacancy/2012/vakansii-php-i-python-razrabotchiki-v-kieve/</guid><category>css</category><category>Git</category><category>html</category><category>JavaScript</category><category>Level UP</category><category>Linux</category><category>MongoDB</category><category>MySQL</category><category>node.js</category><category>PHP</category><category>Python</category><category>вакансии</category><category>Киев</category></item><item><title>Вакансии: команда IT-звезд</title><link>https://www.insight-it.ru//vacancy/2012/vakansii-komanda-it-zvezd/</link><description>&lt;div class="card orange darken-3"&gt;
&lt;p&gt;&lt;div class="card-content white-text center"&gt;
&lt;strong&gt;Вакансии более не актуальны&lt;/strong&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Благодаря сайту &lt;strong class="trebuchet"&gt;Insight IT&lt;/strong&gt;, компания
RDM-Soft нашла ОТЛИЧНОГО тимлида! Теперь, &lt;a href="https://www.insight-it.ru/vacancy/2012/vakansiya-php-polkovodec/"&gt;тимлидер&lt;/a&gt; ищет в свою команду&amp;nbsp;единомышленников и просто IT-звезд.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id="o-kompanii"&gt;О компании&lt;/h2&gt;
&lt;p&gt;История компании началась в 2003 году. С этого момента выпущено много
проектов. Некоторыми из них Вы, возможно, так или иначе пользовались.
Сейчас запускается еще один проект:&amp;nbsp;&lt;strong&gt;SEO-биржа&lt;/strong&gt;. У Вас
есть прекрасная возможность оказаться у истоков будущего хита!&lt;/p&gt;
&lt;h2 id="kto-nuzhen"&gt;Кто нужен?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mr. Backend.&lt;/strong&gt; Он же программист.&amp;nbsp;&lt;em&gt;(вакансия закрыта)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Мастер-ломастер.&lt;/strong&gt; Он же инженер по контролю качества, проще
    говоря тестер.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dr. Frontend.&lt;/strong&gt; Он же фронтендщик. &lt;em&gt;(вакансия закрыта)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mr-backend"&gt;Mr. Backend&lt;/h3&gt;
&lt;h4&gt;Требования&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Отличные знания: &lt;strong&gt;PHP&lt;/strong&gt;, &lt;strong&gt;ООП&lt;/strong&gt;, &lt;strong&gt;SQL&lt;/strong&gt;, &lt;strong&gt;MVC&lt;/strong&gt;,
    &lt;strong&gt;ZendFramework&lt;/strong&gt; (либо альтернатив), &lt;strong&gt;Linux&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Опыт работы по специальности: от 3 лет.&lt;/li&gt;
&lt;li&gt;Опыт работы в команде.&lt;/li&gt;
&lt;li&gt;Желание развиваться и изучать новое.&lt;/li&gt;
&lt;li&gt;Отсутствие желания искать работу в ближайшие 3 года.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Задачи&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Разработка серверной части проекта.&amp;nbsp;Включает в себя :&lt;ul&gt;
&lt;li&gt;бухгалтерия;&lt;/li&gt;
&lt;li&gt;бизнес-логика;&lt;/li&gt;
&lt;li&gt;статистика;&lt;/li&gt;
&lt;li&gt;различные парсеры.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="master-lomaster-sqa"&gt;Мастер-Ломастер (SQA)&lt;/h3&gt;
&lt;h4&gt;Требования&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Хорошие знания: &lt;strong&gt;PHP&lt;/strong&gt;, &lt;strong&gt;SQL&lt;/strong&gt;, &lt;strong&gt;Linux&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Отличные знания принципов whitebox и blackbox тестирования.&lt;/li&gt;
&lt;li&gt;Опыт работы по специальности: от 2 лет.&lt;/li&gt;
&lt;li&gt;Опыт работы в команде.&lt;/li&gt;
&lt;li&gt;Желание развиваться и изучать новое.&lt;/li&gt;
&lt;li&gt;Отсутствие желания искать работу в ближайшие 3 года.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Задачи&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Написание юнит-тестов, автотестов.&lt;/li&gt;
&lt;li&gt;Тестирование:&lt;ul&gt;
&lt;li&gt;бекенда;&lt;/li&gt;
&lt;li&gt;фронтенда;&lt;/li&gt;
&lt;li&gt;бизнес-логики;&lt;/li&gt;
&lt;li&gt;производительности;&lt;/li&gt;
&lt;li&gt;безопасности.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;В общем, тоже очень много интересной работы.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="dr-frontend"&gt;Dr. Frontend&lt;/h3&gt;
&lt;h4&gt;Требования&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Понимание, как сделать интерфейс удобным и приятным для пользователя&lt;/li&gt;
&lt;li&gt;Отличные знания: &lt;strong&gt;HTML&lt;/strong&gt;, &lt;strong&gt;CSS&lt;/strong&gt;, &lt;strong&gt;JavaScript&lt;/strong&gt;&amp;nbsp;(jQuery, ExtJS
    или других распространенных библиотек)&lt;/li&gt;
&lt;li&gt;Опыт проектирования и реализации пользовательского интерфейса&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Задачи&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Прототипирование UI сервиса&lt;/li&gt;
&lt;li&gt;Реализация спроектированного UI&lt;/li&gt;
&lt;li&gt;Разработка расширений&amp;nbsp;для Firefox и Chrome.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="usloviia_1"&gt;Условия&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Удаленная работа.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Работа в профессиональной команде.&lt;/li&gt;
&lt;li&gt;Полный рабочий день (5 дней в неделю по 8 часов).&lt;/li&gt;
&lt;li&gt;Карьерный и профессиональный рост.&lt;/li&gt;
&lt;li&gt;Прислушивание к Вашему мнению.&lt;/li&gt;
&lt;li&gt;Зарплата по результатам собеседования:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mr. Backend:&lt;/strong&gt;&amp;nbsp;от $1500 до $2000&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Мастер-Ломастер:&lt;/strong&gt;&amp;nbsp;от $700 до $1500&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dr. Frontend:&lt;/strong&gt; от $1000 до $2000&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="card orange darken-3"&gt;
&lt;p&gt;&lt;div class="card-content white-text center"&gt;
&lt;strong&gt;Вакансии более не актуальны&lt;/strong&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Fri, 04 May 2012 18:20:00 +0400</pubDate><guid>tag:www.insight-it.ru,2012-05-04:vacancy/2012/vakansii-komanda-it-zvezd/</guid><category>css</category><category>html</category><category>JavaScript</category><category>JQuery</category><category>PHP</category><category>RDM-soft</category><category>SQA</category><category>ZendFramework</category><category>вакансии</category></item><item><title>Вакансия закрыта: PHP-полководец</title><link>https://www.insight-it.ru//vacancy/2012/vakansiya-php-polkovodec/</link><description>&lt;div class="card orange darken-3"&gt;
&lt;p&gt;&lt;div class="card-content white-text center"&gt;
&lt;strong&gt;Вакансия более не актуальна&lt;/strong&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Компания &lt;strong&gt;RDM-Soft&lt;/strong&gt; приглашает на работу
полководца команды &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;-разработчиков &lt;em&gt;(тимлидера)&lt;/em&gt;.&lt;/p&gt;
&lt;div&gt;
&lt;!--more--&gt;
&lt;/div&gt;
&lt;h2 id="o-kompanii"&gt;О компании&lt;/h2&gt;
&lt;p&gt;История компании началась в 2003 году. С этого момента выпущено много
проектов. Некоторыми из них Вы, возможно, так или иначе пользовались.
Сейчас запускается еще один проект: &lt;strong&gt;SEO-биржа&lt;/strong&gt;. У Вас есть прекрасная возможность оказаться у истоков будущего хита в роли лидера команды разработчиков!&lt;/p&gt;
&lt;h2 id="obiazannosti"&gt;Обязанности&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Руководство группой разработчиков.&lt;/li&gt;
&lt;li&gt;Ставить задачи команде и проверять их выполнение.&lt;/li&gt;
&lt;li&gt;Бить по рукам за некачественный код, показывать как правильно
    писать.&lt;/li&gt;
&lt;li&gt;Хвалить, раздавать пряники.&lt;/li&gt;
&lt;li&gt;Иногда писать самому.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="trebovaniia"&gt;Требования&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Опыт руководства командой разработчиков.&lt;/li&gt;
&lt;li&gt;Понимание аспектов мотивации и демотивации программистов.&lt;/li&gt;
&lt;li&gt;Опыт программирования от 3-х лет.&lt;/li&gt;
&lt;li&gt;Отличное знание &lt;strong&gt;PHP5&lt;/strong&gt; &amp;amp;&amp;amp; &lt;strong&gt;MVC&lt;/strong&gt; &amp;amp;&amp;amp; &lt;strong&gt;SQL&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Базовые знания &lt;strong&gt;JavaScript&lt;/strong&gt; &amp;amp;&amp;amp; &lt;strong&gt;HTML&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Базовые знания &lt;strong&gt;unix shell&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Опыт работы с &lt;strong&gt;svn&lt;/strong&gt; || &lt;strong&gt;git&lt;/strong&gt; || &lt;strong&gt;mercurial.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Опыт работы с &lt;strong&gt;redmine&lt;/strong&gt; || &lt;strong&gt;mantis&lt;/strong&gt; || &lt;strong&gt;jira&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="pliusom-budet"&gt;Плюсом будет&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Навыки работы с NoSQL: &lt;strong&gt;MongoDB&lt;/strong&gt; || &lt;strong&gt;Redis&lt;/strong&gt; || &lt;strong&gt;Memcached&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Опыт разработки в области HA и HL.&lt;/li&gt;
&lt;li&gt;Опыт применения lean- и agile- методологий в разработке.&lt;/li&gt;
&lt;li&gt;Знания таких слов как: ДеМарко, КанБан, Таичи Оно, SCRUM.&lt;/li&gt;
&lt;li&gt;Понимание цикла Деминга и SDLС в принципе.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="usloviia"&gt;Условия&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Удаленная работа.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Гибкий график работы.&lt;/li&gt;
&lt;li&gt;Работа в слаженной профессиональной команде.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Зарплата от \$2500 до \$3000 в месяц.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="vakansiia-zakryta"&gt;Вакансия закрыта&lt;/h2&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Thu, 12 Apr 2012 20:52:00 +0400</pubDate><guid>tag:www.insight-it.ru,2012-04-12:vacancy/2012/vakansiya-php-polkovodec/</guid><category>PHP</category><category>teamlead</category><category>вакансии</category><category>менеджер</category><category>разработчик</category></item><item><title>Архитектура Tumblr</title><link>https://www.insight-it.ru//highload/2012/arkhitektura-tumblr/</link><description>&lt;p&gt;&lt;a href="https://www.insight-it.ru/goto/e90ae6ea/" rel="nofollow" target="_blank" title="http://www.tumblr.com"&gt;&lt;strong&gt;Tumblr&lt;/strong&gt;&lt;/a&gt; - одна из самых популярных в мире
платформ для блоггинга, которая делает ставку на привлекательный внешний
вид, юзабилити и дружелюбное сообщество. Хоть проект и не особо на слуху
в России, цифры говорят сами за себя: 24й по посещаемости сайт в США
с&amp;nbsp;15 миллиардами просмотров страниц в месяц. Хотите познакомиться с
историей этого проекта, выросшего из простого стартапа?
&lt;!--more--&gt;&lt;/p&gt;
&lt;h2 id="vvedenie"&gt;Введение&lt;/h2&gt;
&lt;p&gt;Как и всем успешным стартапам, Tumblr удалось преодолеть опасную пропать
между начинающим проектом и широко известной компанией. Поиск правильных
людей, эволюция инфраструктуры, поддержка старых решений, паника по
поводу значительного роста посещаемости от месяца к месяцу, при этом в
команде только 4 технических специалиста - все это заставляло
руководство Tumblr принимать тяжелые решения о том над чем стоит
работать, а над чем - нет. Сейчас же технический персонал расширился до
20 человек и у них достаточно энергии для преодоления всех текущих
проблем и разработки новых интересных технических решений.&lt;/p&gt;
&lt;p&gt;Поначалу Tumblr был вполне типичным большим &lt;a href="/tag/lamp/"&gt;LAMP&lt;/a&gt;
приложением. Сейчас же они двигаются в направлении модели распределенных
сервисов, построенных вокруг существенно менее распространенных
технологий. Основные усилия сейчас вкладываются в постепенный уход от
&lt;a href="/tag/php/"&gt;PHP&lt;/a&gt; в пользу более "правильных" и "современных" решений,
оформленных в виде сервисов. Параллельно с переходом к новым технологиям
идут изменения и в команде проекта: от небольшой группы энтузиастов к
полноценной команде разработчиков, имеющей четкую структуру и сферы
ответственности, но тем не менее жаждущей реализовывать новый функционал
и обустраивать совершенно новую инфраструктуру проекта.&lt;/p&gt;
&lt;h2 id="platforma"&gt;Платформа&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/tag/centos/"&gt;CentOS&lt;/a&gt; на серверах, &lt;a href="/tag/mac-os-x/"&gt;Mac OS X&lt;/a&gt; для
    разработки&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/apache/"&gt;Apache&lt;/a&gt; - основной веб-сервер&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;, &lt;a href="/tag/scala/"&gt;Scala&lt;/a&gt;, &lt;a href="/tag/ruby/"&gt;Ruby&lt;/a&gt; - языки
    программирования&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/finagle/"&gt;Finagle&lt;/a&gt;&amp;nbsp;- асинхронный RPC сервер и клиент&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/mysql/"&gt;MySQL&lt;/a&gt;, &lt;a href="/tag/hbase/"&gt;HBase&lt;/a&gt;&amp;nbsp;- СУБД&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/memcached/"&gt;memcached&lt;/a&gt;,&amp;nbsp;&lt;a href="/tag/redis/"&gt;Redis&lt;/a&gt;&amp;nbsp;- кэширование&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/varnish/"&gt;Varnish&lt;/a&gt;, &lt;a href="/tag/nginx/"&gt;nginx&lt;/a&gt; - отдача статики&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/haproxy/"&gt;HAProxy&lt;/a&gt; - балансировка нагрузки&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/kestrel/"&gt;kestrel&lt;/a&gt;, &lt;a href="/tag/gearman/"&gt;gearman&lt;/a&gt; - очередь задач&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/thrift/"&gt;Thrift&lt;/a&gt; - сериализация&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/kafka/"&gt;Kafka&lt;/a&gt; - распределенная шина сообщений&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/hadoop/"&gt;Hadoop&lt;/a&gt; - обработка статистики&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/zookeeper/"&gt;ZooKeeper&lt;/a&gt; - хранение конфигурации и состояний
    системы&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/git/"&gt;git&lt;/a&gt; - система контроля версий&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/jenkins/"&gt;Jenkins&lt;/a&gt; - непрерывное тестирование&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="statistika"&gt;Статистика&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Около 500 миллионов просмотров страниц в день&lt;/li&gt;
&lt;li&gt;Более 15 миллиардов просмотров страниц в месяц&lt;/li&gt;
&lt;li&gt;Посещаемость растет примерно на 30% в месяц&lt;/li&gt;
&lt;li&gt;Пиковые нагрузки порядка 40 тысяч запросов в секунду&lt;/li&gt;
&lt;li&gt;Около 20 технических специалистов в команде&lt;/li&gt;
&lt;li&gt;Каждый день создается около 50Гб новых постов и 2.7Тб обновлений списков
последователей&lt;/li&gt;
&lt;li&gt;Более 1Тб статистики обрабатывается в &lt;a href="/tag/hadoop/"&gt;Hadoop&lt;/a&gt; ежедневно&lt;/li&gt;
&lt;li&gt;Используется порядка 1000 серверов:&lt;ul&gt;
&lt;li&gt;500 веб-серверов c Apache и PHP-приложением&lt;/li&gt;
&lt;li&gt;200 серверов баз данных (существенная их часть - резервные)&lt;ul&gt;
&lt;li&gt;47 пулов&lt;/li&gt;
&lt;li&gt;30 партиций (шардов)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;30 серверов &lt;a href="/tag/memcached/"&gt;memcached&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;25 серверов Redis&lt;/li&gt;
&lt;li&gt;15 серверов Varnish&lt;/li&gt;
&lt;li&gt;25 серверов HAProxy&lt;/li&gt;
&lt;li&gt;8 серверов nginx&lt;/li&gt;
&lt;li&gt;14 серверов для очередей задач&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="tipichnoe-ispolzovanie"&gt;Типичное использование&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tumblr&lt;/strong&gt; используется несколько по-другому, чем другие социальные
сети:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;При более чем 50 миллионах постов в день, каждый из них попадает в
среднем к нескольким сотням читателей. Это и не несколько
пользователей с миллионами читателей (например, популярные личности
в Twitter) и не миллиарды личных сообщений.&lt;/li&gt;
&lt;li&gt;Ориентированность на длинные публичные сообщения, полные интересной
информацией и картинками/видео, заставляет пользователей проводить
долгие часы каждый день за чтением Tumblr.&lt;/li&gt;
&lt;li&gt;Большинство активных пользователей подписывается на сотни других
блоггеров, что практически гарантирует много страниц нового контента
при каждом заходе на сайт. В других социальных сетях поток новых
сообщений переполнен ненужным контентом и толком не читается.&lt;/li&gt;
&lt;li&gt;Как следствие, при сложившемся количестве пользователей, средней
аудиторией каждого и высокой активностью написания постов, системе
приходится обрабатывать и доставлять огромное количество информации.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Публичные блоги называют Tumblelog'ами, они не так динамичны и легко
кэшируются.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Сложнее всего масштабировать Dashboard, страницу, где пользователи в
реальном времени читают что нового у блоггеров, на которых они
подписаны:&lt;ul&gt;
&lt;li&gt;Кэширование практически бесполезно, так как для активных
пользователей запросы редко повторяются.&lt;/li&gt;
&lt;li&gt;Информация должна отображаться в реальном времени, быть целостной и
не "задерживаться".&lt;/li&gt;
&lt;li&gt;Около 70% просмотров страниц приходится именно на Dashboard, почти
все пользователи им пользуются.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="staraia-arkhitektura"&gt;Старая архитектура&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Когда проект только начинался, Tumblr размещался в Rackspace и последние выдавали каждому блогу с собственным доменом A-запись. Когда они переросли Rackspace, они не смогли полноценно мигрировать в новый
датацентр, в том числе из-за количества пользователей. Это было в 2007
году, но у них по-прежнему часть доменов ведут на Rackspace и
перенаправляются в новый датацентр с помощью HAProxy и Varnish. Подобных
"унаследованных" проблем у проекта очень много.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;С технической точки зрения проект прошел по пути типичной эволюции
&lt;strong&gt;LAMP&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Исторически разработан на &lt;strong&gt;PHP&lt;/strong&gt;, все началось с веб-сервера,
сервера баз данных и начало потихоньку развиваться.&lt;/li&gt;
&lt;li&gt;Чтобы справляться с нагрузкой они начали использовать memcache,
затем добавили кэширование целых страниц и статических файлов, потом
поставили HAProxy перед кэшами, после чего сделали партиционирование
на уровне &lt;strong&gt;MySQL&lt;/strong&gt;, что сильно облегчило им жизнь.&lt;/li&gt;
&lt;li&gt;Они делали все, чтобы выжать максимум из каждого сервера.&lt;/li&gt;
&lt;li&gt;Было разработано два сервиса на C: генератор уникальных
идентификаторов на основе HTTP и libevent, а также
&lt;a href="https://www.insight-it.ru/goto/533d5aae/" rel="nofollow" target="_blank" title="http://engineering.tumblr.com/post/7819252942/staircar-redis-powered-notifications"&gt;Staircar&lt;/a&gt;,
использующий Redis для обеспечения уведомлений в реальном времени на
Dashboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dashboard использует подход
&lt;a href="https://www.insight-it.ru/goto/f4d9020c/" rel="nofollow" target="_blank" title="http://www2.parc.com/istl/projects/ia/papers/sg-sigir92/sigir92.html"&gt;"разбрасывать-собирать"&lt;/a&gt;,
так как из-за отсортировонности данных по времени традиционные схемы
партиционирования работали не очень хорошо. По их прогнозам текущая
реализация позволит им рости еще в течении полугода.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="novaia-arkhitektura"&gt;Новая архитектура&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Приоритетным направлением стали технологии, основанные на JVM, по
причине более быстрой разработки и доступности квалифицированных
кадров. Мотивация несколько спорная, особенно если учесть, что речь
идет в первую очередь о &lt;a href="/tag/scala/"&gt;Scala&lt;/a&gt;, а не
о&amp;nbsp;&lt;a href="/tag/scala/"&gt;Java&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Основная цель - вынести все из PHP приложения в отдельные сервисы, что
сделает его лишь тонким клиентом к внутреннему API.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Почему выбор пал именно на &lt;strong&gt;Scala&lt;/strong&gt; и &lt;strong&gt;Finagle&lt;/strong&gt;?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Многие разработчики имели опыт с Ruby и PHP, так что Scala был
привлекательным (цитата, логики мало)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/d7e3c54e/" rel="nofollow" target="_blank" title="https://github.com/twitter/finagle"&gt;Finagle&lt;/a&gt; был одним из основных
факторов в пользу JVM: это библиотека, разработанная в Twitter,
которая решает большинство распределенных задач вроде маршрутизации
запросов и обнаружение/регистрацию сервисов - не пришлось
реализовывать это все с нуля.&lt;/li&gt;
&lt;li&gt;В Scala не принято использовать общие состояния, что избавляет
разработчиков от забот с потоками выполнения и блокировками.&lt;/li&gt;
&lt;li&gt;Им очень нравится Thrift в роли программного интерфейса из-за его
высокой производительности (он кроссплатформенный и к JVM никак не
относится)&lt;/li&gt;
&lt;li&gt;Нравится &lt;a href="/tag/netty/"&gt;Netty&lt;/a&gt;, но не хочется связываться с Java, еще
один аргумент в пользу Scala.&lt;/li&gt;
&lt;li&gt;Рассматривали &lt;a href="/tag/node-js/"&gt;Node.js&lt;/a&gt;, но отказались так как под
JVM проще найти разработчиков, а также из-за отсутствия стандартов,
"лучших практик" и большого количества качественно протестированного
кода.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Старые внутренние сервисы также переписываются с C + libevent на Scala + Fingle.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Был создан общий каркас для построения внутренних сервисов:&lt;ul&gt;
&lt;li&gt;Много усилий было приложено для автоматизации управления
распределенной системой.&lt;/li&gt;
&lt;li&gt;Создан аналог скаффолдинга - используется некий шаблон для создания
каждого нового сервиса.&lt;/li&gt;
&lt;li&gt;Все сервисы выглядят одинаково с точки зрения системного
администратора: получение статистики, мониторинг, запуск и остановка
реализованы одинаково для всех сервисов.&lt;/li&gt;
&lt;li&gt;Созданы простые инструменты для сборки сервисов без вникания в
детали используемых стандартных решений.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Используется 6 внутренних сервисов, над которыми работает отдельная
команд. На запуск сервиса с нуля уходит около 2-3 недель.&lt;/li&gt;
&lt;li&gt;Новые, нереляционные СУБД, такие как HBase и Redis, вводятся в
эксплуатацию, но основным хранилищем по-прежнему остается сильно
партиционированный MySQL.&lt;/li&gt;
&lt;li&gt;HBase используется для сервиса сокращенных ссылок для постов, а также
всех исторических данных и аналитики. HBase хорошо справляется с
ситуациями, где необходимы миллионы операций записи в секунду, но он не
достаточно стабилен, чтобы полностью заменить проверенное временем
решение на MySQL в критичных для бизнеса задачах.&lt;/li&gt;
&lt;li&gt;Партиционированный MySQL плохо справляется с отсортированными по времени данными, так как один из серверов всегда оказывается существенно более "горячим", чем остальными. Также сталкивались с значительными задержками в репликации из-за большого количества параллельных операций добавления данных.&lt;/li&gt;
&lt;li&gt;Используется 25 серверов Redis с 8-32 процессами на каждом, что означает
порядка 300-400 экземпляров Redis в сумме.&lt;ul&gt;
&lt;li&gt;Используется для уведомлений в реальном времени на Dashboard (о
событиях вроде "кому-то понравился Ваш пост").&lt;/li&gt;
&lt;li&gt;Высокое соотношений операций записи к операциям чтения сделало MySQL
не очень подходящим кандидатом.&lt;/li&gt;
&lt;li&gt;Уведомления не так критичны, их потеря допустима, что позволило
отключить персистентность Redis.&lt;/li&gt;
&lt;li&gt;Был создан интерфейс между Redis и отложенными задачами в Finagle.&lt;/li&gt;
&lt;li&gt;Сервис коротких ссылок также использует Redis как кэш, а HBase для
постоянного хранения.&lt;/li&gt;
&lt;li&gt;Вторичный индекс Dashboard также построен вокруг Redis.&lt;/li&gt;
&lt;li&gt;Redis также используется для хранения задач Gearman, для чего был
написан memcache proxy на основе Finale.&lt;/li&gt;
&lt;li&gt;Постепенно отказываются от memcached в пользу Redis в роли основного
кэша. Производительность у них сопоставима.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Внутренним сервисам необходим доступ к потоку всех событий в системе
(создание, редактирование и удаление постов, нравится или не нравится и
т.п.), для чего была созданна внутренняя шина сообщений &lt;em&gt;(англ.
firehose, пожарный шланг)&lt;/em&gt;:&lt;ul&gt;
&lt;li&gt;Пробовали использовать в этой роли Scribe, но так как оно по сути
свелось к пропусканию логов через grep в реальном времени - нагрузки
оно не выдержало.&lt;/li&gt;
&lt;li&gt;Текущая реализация основана на Kafka, решению аналогичной задачи от
LinkedIn на Scala.&lt;/li&gt;
&lt;li&gt;MySQL также не рассматривался из-за большой доли операций записи.&lt;/li&gt;
&lt;li&gt;Внутри сервисы используют HTTP потоки для чтения данных, хотя Thrift
интерфейс также используется.&lt;/li&gt;
&lt;li&gt;Поток сообщений хранит события за последнюю неделю с возможностью
указать момент времени с которого считывать данные при открытии
соединения.&lt;/li&gt;
&lt;li&gt;Поддерживается абстракция "группы потребителей", которая позволяет
группе клиентов вместе обрабатывать один поток данных вместе и
независимо, то есть одно и то же сообщение не попадет дважды к
клиентам из одной группы.&lt;/li&gt;
&lt;li&gt;ZooKeeper используется для периодического сохранения текущей позиции
каждого клиента в потоке.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Новая архитектура Dashboard основана на принципе ячеек или ящиков
входящих сообщений:&lt;ul&gt;
&lt;li&gt;Каждая "ячейка" отвечает за группу пользователей и читает новые события
с шины сообщений, если один из её пользователей-подопечных подписан на
автора только что опубликованного поста, то пост добавляется в "почтовый
ящик" подписанного пользователя.&lt;/li&gt;
&lt;li&gt;Когда пользователь заходит в Dashboard его запрос попадает в его ячейку,
которая возвращает ему нужную часть непрочитанных постов.&lt;/li&gt;
&lt;li&gt;Каждая ячейка состоит из трех групп серверов:&lt;ul&gt;
&lt;li&gt;HBase для постоянного хранения копий постов и почтовых ящиков;&lt;/li&gt;
&lt;li&gt;Redis для кэширование свежих данных;&lt;/li&gt;
&lt;li&gt;Сервис, читающий данные из шины и предоставляющий доступ к ящикам
посредством Thrift.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;В HBase используется две таблицы:&lt;ul&gt;
&lt;li&gt;Отсортированный &lt;strong&gt;список идентификаторов постов&lt;/strong&gt; для каждого
пользователя в ячейке, именно в том виде, как они будут отображены в
итоге.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Копии всех постов&lt;/strong&gt; по идентификаторам, что позволяет выдать все
данные для отрисовки Dashboard без обращений к серверам вне одной
ячейки.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ячейки представляют собой независимые единицы, что позволяет легко
масштабировать систему при росте числа пользователей.&lt;/li&gt;
&lt;li&gt;Платой за относительно безболезненность масштабирования является
чрезвычайная избыточность данных: при том что ежедневно создается лишь
50Гб постов, суммарный объем данных в ячейках растет на 2.7Тб в день.&lt;/li&gt;
&lt;li&gt;Альтернативой было бы использование общего кластера со всеми постами, но
тогда он бы стал единственной точкой отказа и потребовалось бы делать
дополнительные удаленные запросы. Помимо этого выигрыш по объему был бы
не велик - списки идентификаторов занимают значительно больше места, чем
сами посты.&lt;/li&gt;
&lt;li&gt;Пользователи, которые подписаны или на которых подписаны миллионы других
пользователей, обрабатываются отдельно - страницы с их постами
генерируются не заранее (как описывалось выше), а при поступлении
запроса - это позволяет не тратить впустую много ресурсов (этот подход
называется выборочная материализация).&lt;/li&gt;
&lt;li&gt;Количество пользователей в одной ячейке позволяет управлять балансом
между уровнем надежности и стоимостью содержания этой подсистемы.&lt;/li&gt;
&lt;li&gt;Параллельное чтение их шины сообщений оказывает серьезную нагрузку на
сеть, в дальнейшем из ячеек можно будет составить иерархию: только часть
будет читать напрямую из шины сообщений, а остальным сообщения будут
ретранслироваться.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tumblr географически по-прежнему находится в одном датацентре (если не
считать незначительное присутствие в Rackspace), распределение по
нескольким лишь в планах.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="razvertyvanie"&gt;Развертывание&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Начиналось как несколько rsync-скриптов для распространения
    PHP-приложения. Как только машин стало больше 200 такой подход стал
    занимать слишком много времени.&lt;/li&gt;
&lt;li&gt;Следующий вариант был основан на &lt;a href="/tag/capistrano/"&gt;Capistrano&lt;/a&gt;:
    были созданы три стадии процесса развертывания (разработка,
    тестирование, боевой). Неплохо справлялся с десятками серверов, но
    на сотнях также был слишком медленным, так как основывался на SSH.&lt;/li&gt;
&lt;li&gt;Итоговый вариант основан на &lt;strong&gt;Func&lt;/strong&gt;, решении от
    &lt;a href="/tag/redhat/"&gt;RedHat&lt;/a&gt;, позволившим заменить &lt;a href="/tag/ssh/"&gt;SSH&lt;/a&gt; на
    более легковесный протокол.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="razrabotka"&gt;Разработка&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Поначалу философия была такова, что каждый мог использовать любые
технологии, которые считал уместным. Но довольно скоро пришлось
стандартизировать стек технологий, чтобы было легче нанимать и вводить в
работу новых сотрудников, а также для более оперативного решения
технических проблем.&lt;/li&gt;
&lt;li&gt;Каждый разработчик имеет одинаковую заранее настроенную рабочую станцию,
которая обновляется посредством &lt;a href="/tag/puppet/"&gt;Puppet&lt;/a&gt;:&lt;ul&gt;
&lt;li&gt;Настроена публикация изменений, тестирование и развертывание новых
версий.&lt;/li&gt;
&lt;li&gt;Разработчики используют vim и Textmate.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Новый PHP код систематически инспектируется другими разработчиками.&lt;/li&gt;
&lt;li&gt;Внутренние сервисы подвергаются непрерывному тестированию посредством
Jenkins.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="struktura-komand"&gt;Структура команд&lt;/h2&gt;
&lt;p&gt;Проект разбит на 6 команд:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Инфраструктура:&lt;/strong&gt;&amp;nbsp;все, что ниже 5 уровня по модели OSI -
    маршрутизация, TCP/IP, DNS, оборудование и.т.п.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Платформа:&lt;/strong&gt;&amp;nbsp;разработка основного приложения, партиционирование
    SQL, взаимодействие сервисов.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Надежность (SRE):&lt;/strong&gt;&amp;nbsp;сфокусирована на текущие потребности с точки
    зрения надежности и масштабируемости.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Сервисы:&lt;/strong&gt;&amp;nbsp;занимается более стратегической разработкой того, что
    понадобится через один-два месяца.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Эксплуатация:&lt;/strong&gt;&amp;nbsp;отвечает за обнаружение и реагирование на
    проблемы, плюс тонкая настройка.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="naim"&gt;Найм&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;На интервью они обычно избегают математики и головоломок, основной
    упор идет в основном именно на те вещи, которым придется заниматься
    кандидату.&lt;/li&gt;
&lt;li&gt;Основной вопрос: будет ли он успешно решать поставленные задачи?
    Цель в том, чтобы найти отличных людей, а не в том, чтобы никого не
    брать.&lt;/li&gt;
&lt;li&gt;Разработчиков обязательно просят привести пример своего кода, даже
    во время телефонных интервью.&lt;/li&gt;
&lt;li&gt;Во время интервью кандидатов не ограничивают в наборе инструментов,
    можно даже гуглить.&lt;/li&gt;
&lt;li&gt;Поиск людей с опытом в крупных проектах достаточно сложен, так как
    всего нескольких компаниях по всему миру решают подобные проблемы.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="podvodim-itogi"&gt;Подводим итоги&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Автоматизация - ключ к успеху крупного проекта.&lt;/li&gt;
&lt;li&gt;При партиционировании MySQL может масштабироваться, но лишь при
    преобладании операций чтения.&lt;/li&gt;
&lt;li&gt;Redis с отключенной персистентностью легко может заменить memcached.&lt;/li&gt;
&lt;li&gt;Scala достойно себя проявляет в роли языка программирования для
    внутренних сервисов, во многом благодаря обширной Java-экосистеме.&lt;/li&gt;
&lt;li&gt;Внедряйте новые технологии постепенно, поначалу работать с HBase и
    Redis было очень болезненно, они были включены в основной стек
    технологий только после испытаний в некритичных сервисах и
    подпроектах, где цена ошибки не так велика.&lt;/li&gt;
&lt;li&gt;Проект должен строиться вокруг навыков его команды, а не наоборот.&lt;/li&gt;
&lt;li&gt;Нужно нанимать людей только если они вписываются в команду и в
    состоянии довести работу до результата.&lt;/li&gt;
&lt;li&gt;При выборе технологического стека одну из ключевых ролей играет
    доступность соответствующих специалистов на кадровом рынке.&lt;/li&gt;
&lt;li&gt;Читайте публикации и статьи в блогах. Ключевые аспекты архитектуры,
    включая "ячейки" и частичную материализацию были позаимствованы из
    внешних источников.&lt;/li&gt;
&lt;li&gt;Поспрашивайте своих коллег, кто-то из них мог общаться с
    специалистами из
    &lt;a href="https://www.insight-it.ru/highload/2010/arkhitektura-facebook/"&gt;Facebook&lt;/a&gt;,
    &lt;a href="https://www.insight-it.ru/highload/2011/arkhitektura-twitter-dva-goda-spustya/"&gt;Twitter&lt;/a&gt;,
    &lt;a href="https://www.insight-it.ru/highload/2011/arkhitektura-google-2011/"&gt;Google&lt;/a&gt;
    или
    &lt;a href="https://www.insight-it.ru/highload/2008/arkhitektura-linkedin/"&gt;LinkedIn&lt;/a&gt; -
    если нет прямого доступа, всегда можно получить нужную информацию
    через одно-два "рукопожатия".&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Статья написана на основе &lt;a href="https://www.insight-it.ru/goto/1444dc9b/" rel="nofollow" target="_blank" title="http://highscalability.com/blog/2012/2/13/tumblr-architecture-15-billion-page-views-a-month-and-harder.html"&gt;интервью&lt;/a&gt;&amp;ensp;&lt;a href="https://www.insight-it.ru/goto/f1d04e95/" rel="nofollow" target="_blank" title="https://www.linkedin.com/in/bmatheny"&gt;Blake Matheny&lt;/a&gt;, директора по разработке платформы Tumblr.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Tue, 21 Feb 2012 16:29:00 +0400</pubDate><guid>tag:www.insight-it.ru,2012-02-21:highload/2012/arkhitektura-tumblr/</guid><category>Apache</category><category>Capistrano</category><category>CentOS</category><category>Finagle</category><category>Func</category><category>gearman</category><category>Git</category><category>Hadoop</category><category>HAProxy</category><category>HBase</category><category>jenkins</category><category>kafka</category><category>Kestrel</category><category>LAMP</category><category>Mac OS X</category><category>Memcached</category><category>MySQL</category><category>nginx</category><category>PHP</category><category>puppet</category><category>Redis</category><category>Ruby</category><category>Scala</category><category>Thrift</category><category>Tumblr</category><category>Varnish</category><category>ZooKeeper</category></item><item><title>Архитектура Вконтакте</title><link>https://www.insight-it.ru//highload/2010/arkhitektura-vkontakte/</link><description>&lt;p&gt;&lt;img alt="Логотип Вконтакте" class="left" src="https://www.insight-it.ru/images/vkontakte-logo.png" title="Логотип Вконтакте"/&gt;
Самая популярная социальная сеть в рунете пролила немного света на то,
как же она работает. Представители проекта в лице Павла Дурова и Олега
Илларионова на конференции &lt;a href="https://www.insight-it.ru/event/2010/highload-2010/"&gt;HighLoad++&lt;/a&gt; ответили на шквал вопросов по совершенно разным аспектам работы
&lt;a href="https://www.insight-it.ru/goto/1a5d3494/" rel="nofollow" target="_blank" title="https://vk.com"&gt;Вконтакте&lt;/a&gt;, в том числе и техническим. Спешу
поделиться своим взглядом на архитектуру проекта по результатам данного
выступления.&lt;!--more--&gt;&lt;/p&gt;
&lt;h2 id="platforma"&gt;Платформа&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/tag/debian/"&gt;Debian&lt;/a&gt;&amp;ensp;&lt;a href="/tag/linux/"&gt;Linux&lt;/a&gt; - основная операционная
    система&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/nginx/"&gt;nginx&lt;/a&gt; - балансировка нагрузки&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/php/"&gt;PHP&lt;/a&gt; + &lt;a href="/tag/xcache/"&gt;XCache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/apache/"&gt;Apache&lt;/a&gt; + &lt;a href="/tag/mod_php/"&gt;mod_php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/memcached/"&gt;memcached&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/mysql/"&gt;MySQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Собственная СУБД на &lt;a href="/tag/c/"&gt;C&lt;/a&gt;, созданная "лучшими умами" России&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/node-js/"&gt;node.js&lt;/a&gt; - прослойка для реализации XMPP, живет за
    &lt;a href="/tag/haproxy/"&gt;HAProxy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Изображения отдаются просто с файловой системы &lt;a href="/tag/xfs/"&gt;xfs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/ffmpeg/"&gt;ffmpeg&lt;/a&gt; - конвертирование видео&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="statistika"&gt;Статистика&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;95 миллионов учетных записей&lt;/li&gt;
&lt;li&gt;40 миллионов активных пользователей во всем мире (сопоставимо с
    аудиторией интернета в России)&lt;/li&gt;
&lt;li&gt;11 миллиардов запросов в день&lt;/li&gt;
&lt;li&gt;200 миллионов личных сообщений в день&lt;/li&gt;
&lt;li&gt;Видеопоток достигает 160Гбит/с&lt;/li&gt;
&lt;li&gt;Более 10 тысяч серверов, из которых только 32 - фронтенды на
    &lt;a href="/tag/nginx/"&gt;nginx&lt;/a&gt; (количество серверов с
    &lt;a href="/tag/apache/"&gt;Apache&lt;/a&gt; неизвестно)&lt;/li&gt;
&lt;li&gt;30-40 разработчиков, 2 дизайнера, 5 системных администраторов, много
    людей в датацентрах&lt;/li&gt;
&lt;li&gt;Каждый день выходит из строя около 10 жестких дисков&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="arkhitektura"&gt;Архитектура&lt;/h2&gt;
&lt;h3 id="obshchie-printsipy"&gt;Общие принципы&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Cервера многофункциональны и используются одновременно в нескольких
    ролях:&lt;ul&gt;
&lt;li&gt;Перебрасывание полуавтоматическое&lt;/li&gt;
&lt;li&gt;Требуется перезапускать daemon'ы&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Генерация страниц с новостями (микроблоги) происходит очень похожим
    образом с &lt;a href="/tag/facebook/"&gt;Facebook&lt;/a&gt; (см. &lt;a href="https://www.insight-it.ru/highload/2010/arkhitektura-facebook/"&gt;Архитектура&amp;nbsp;Facebook&lt;/a&gt;), основное
    отличие - использование собственной СУБД вместо MySQL&lt;/li&gt;
&lt;li&gt;При балансировке нагрузки используются:&lt;ul&gt;
&lt;li&gt;Взвешенный round robin внутри системы&lt;/li&gt;
&lt;li&gt;Разные сервера для разных типов запросов&lt;/li&gt;
&lt;li&gt;Балансировка на уровне ДНС на 32 IP-адреса&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Большая часть внутреннего софта написано самостоятельно, в том
    числе:&lt;ul&gt;
&lt;li&gt;Собственная СУБД (см. ниже)&lt;/li&gt;
&lt;li&gt;Мониторинг с уведомлением по СМС (Павел сам помогал верстать
    интерфейс :) )&lt;/li&gt;
&lt;li&gt;Автоматическая система тестирования кода&lt;/li&gt;
&lt;li&gt;Анализаторы статистики и логов&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Мощные сервера:&lt;ul&gt;
&lt;li&gt;8-ядерные процессоры Intel (по два на сервер, видимо)&lt;/li&gt;
&lt;li&gt;64Гб оперативной памяти&lt;/li&gt;
&lt;li&gt;8 жестких дисков (соответственно скорее всего корпуса 2-3U)&lt;/li&gt;
&lt;li&gt;RAID не используется&lt;/li&gt;
&lt;li&gt;Не брендированные&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Вычислительные мощности серверов используются менее, чем на 20%&lt;/li&gt;
&lt;li&gt;Сейчас проект расположен в 4 датацентрах в Санкт-Петербурге и
    Москве, причем:&lt;ul&gt;
&lt;li&gt;Вся основная база данных располагается в одном датацентре в
    Санкт-Петербурге&lt;/li&gt;
&lt;li&gt;В Московских датацентрах только аудио и видео&lt;/li&gt;
&lt;li&gt;В планах сделать репликацию базы данных в другой датацентр в
    ленинградской области&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/cdn/"&gt;CDN&lt;/a&gt; на данный момент не используется, но в планах есть&lt;/li&gt;
&lt;li&gt;Резервное копирование данных происходит ежедневно и инкрементально&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="volshebnaia-baza-dannykh-na-c"&gt;Волшебная база данных на &lt;a href="/tag/c/"&gt;C&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Этому продукту, пожалуй, уделялось максимум внимания аудитории, но при
этом почти никаких подробностей о том, что он собственно говоря собой
представляет, так и не было обнародовано. Известно, что:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Разработана "лучшими умами" России, победителями олимпиад и
    конкурсов топкодер; озвучили даже имена этих "героев" Вконтакте
    (писал на слух и возможно не всех успел, так что извиняйте):&lt;ul&gt;
&lt;li&gt;Андрей Лопатин&lt;/li&gt;
&lt;li&gt;Николай Дуров&lt;/li&gt;
&lt;li&gt;Арсений Смирнов&lt;/li&gt;
&lt;li&gt;Алексей Левин&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Используется в огромном количестве сервисов:&lt;ul&gt;
&lt;li&gt;Личные сообщения&lt;/li&gt;
&lt;li&gt;Сообщения на стенах&lt;/li&gt;
&lt;li&gt;Статусы&lt;/li&gt;
&lt;li&gt;Поиск&lt;/li&gt;
&lt;li&gt;Приватность&lt;/li&gt;
&lt;li&gt;Списки друзей&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Нереляционная модель данных&lt;/li&gt;
&lt;li&gt;Большинство операций осуществляется в оперативной памяти&lt;/li&gt;
&lt;li&gt;Интерфейс доступа представляет собой расширенный протокол
    &lt;a href="/tag/memcached/"&gt;memcached&lt;/a&gt;, специальным образом составленные ключи
    возвращают результаты сложных запросов (чаще всего специфичных для
    конкретного сервиса)&lt;/li&gt;
&lt;li&gt;Хотели бы сделать из данной системы универсальную СУБД и
    опубликовать под GPL, но пока не получается из-за высокой степени
    интеграции с остальными сервисами&lt;/li&gt;
&lt;li&gt;Кластеризация осуществляется легко&lt;/li&gt;
&lt;li&gt;Есть репликация&lt;/li&gt;
&lt;li&gt;Если честно, я так и не понял зачем им &lt;a href="/tag/mysql/"&gt;MySQL&lt;/a&gt; с такой
    штукой - возможно просто как legacy живет со старых времен&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="audio-i-video"&gt;Аудио и видео&lt;/h3&gt;
&lt;p&gt;Эти подпроекты являются побочными для социальной сети, на них особо не
фокусируются. В основном это связанно с тем, что они редко коррелируют с
основной целью использования социальной сети - &lt;em&gt;общением&lt;/em&gt;, а также
создают большое количество проблем: видео траффик - основная статья
расходов проекта, плюс всем известные проблемы с нелегальным контентом и
претензиями правообладателей. Медиа-файлы банятся по хэшу при удалении
по просьбе правообладателей, но это неэффективно и планируется
усовершенствовать этот механизм.&lt;/p&gt;
&lt;p&gt;1000-1500 серверов используется для перекодирования видео, на них же оно
и хранится.&lt;/p&gt;
&lt;h3 id="xmpp"&gt;XMPP&lt;/h3&gt;
&lt;p&gt;Как известно, некоторое время назад появилась возможность общаться на
Вконтакте через протокол Jabber (он же XMPP). Протокол совершенно
открытый и существует масса opensource реализаций.&lt;/p&gt;
&lt;p&gt;По ряду причин, среди которых проблемы с интеграцией с остальными
сервисами, было решено за месяц создать собственный сервер,
представляющий собой прослойку между внутренними сервисами Вконтакте и
реализацией XMPP протокола. Основные особенности этого сервиса:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Реализован на &lt;a href="/tag/node-js/"&gt;node.js&lt;/a&gt; (выбор обусловлен тем, что
    JavaScript знают практически все разработчики проекта, а также
    хороший набор инструментов для реализации задачи)&lt;/li&gt;
&lt;li&gt;Работа с большими контакт-листами - у многих пользователей
    количество друзей на Вконтакте измеряется сотнями и тысячами&lt;/li&gt;
&lt;li&gt;Высокая активность смены статусов - люди появляются и исчезают из
    онлайна чаще, чем в других аналогичных ситуациях&lt;/li&gt;
&lt;li&gt;Аватарки передаются в &lt;code&gt;base64&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Тесная интеграция с внутренней системой обмена личными сообщениями
    Вконтакте&lt;/li&gt;
&lt;li&gt;60-80 тысяч человек онлайн, в пике - 150 тысяч&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/haproxy/"&gt;HAProxy&lt;/a&gt; обрабатывает входящие соединения и
    используется для балансировки нагрузки и развертывания новых версий&lt;/li&gt;
&lt;li&gt;Данные хранятся в &lt;a href="/tag/mysql/"&gt;MySQL&lt;/a&gt; (думали о MongoDB, но
    передумали)&lt;/li&gt;
&lt;li&gt;Сервис работает на 5 серверах разной конфигурации, на каждом из них
    работает код на&lt;a href="/tag/node-js/"&gt;node.js&lt;/a&gt; (по 4 процесса на сервер), а
    на трех самых мощных - еще и &lt;a href="/tag/mysql/"&gt;MySQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;В &lt;a href="/tag/node-js/"&gt;node.js&lt;/a&gt; большие проблемы с использованием
    &lt;a href="/tag/openssl/"&gt;OpenSSL&lt;/a&gt;, а также течет память&lt;/li&gt;
&lt;li&gt;Группы друзей в XMPP не связаны с группами друзей на сайте - сделано
    по просьбе пользователей, которые не хотели чтобы их друзья из-за
    плеча видели в какой группе они находятся&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="integratsiia-so-vneshnimi-resursami"&gt;Интеграция со внешними ресурсами&lt;/h3&gt;
&lt;p&gt;Во Вконтакте считают данное направление очень перспективным и
осуществляют массу связанной с этим работы. Основные предпринятые шаги:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Максимальная кроссбраузерность для виджетов на основе библиотек
    easyXDM и fastXDM&lt;/li&gt;
&lt;li&gt;Кросс-постинг статусов в &lt;a href="/tag/twitter/"&gt;Twitter&lt;/a&gt;, реализованный с
    помощью очередей запросов&lt;/li&gt;
&lt;li&gt;Кнопка "поделиться с друзьями", поддерживающая openGraph теги и
    автоматически подбирающая подходящую иллюстрацию (путем сравнивание
    содержимых тега &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; и атрибутов alt у изображений, чуть ли не
    побуквенно)&lt;/li&gt;
&lt;li&gt;Возможность загрузки видео через сторонние видео-хостинги (YouTube,
    RuTube, Vimeo, и.т.д.), открыты к интеграции с другими&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="interesnye-fakty-ne-po-teme_1"&gt;Интересные факты не по теме&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Процесс разработки близок к Agile, с недельными итерациями&lt;/li&gt;
&lt;li&gt;Ядро операционной системы модифицированно (на предмет работы с
    памятью), есть своя пакетная база для &lt;a href="/tag/debian/"&gt;Debian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Фотографии загружаются на два жестких диска одного сервера
    одновременно, после чего создается резервная копия на другом сервере&lt;/li&gt;
&lt;li&gt;Есть много доработок над &lt;a href="/tag/memcached/"&gt;memcached&lt;/a&gt;, в.т.ч. для
    более стабильного и длительного размещения объектов в памяти; есть
    даже persistent версия&lt;/li&gt;
&lt;li&gt;Фотографии не удаляются для минимизации фрагментации&lt;/li&gt;
&lt;li&gt;Решения о развитии проекта принимают Павел Дуров и Андрей Рогозов,
    ответственность за сервисы - на них и на реализовавшем его
    разработчике&lt;/li&gt;
&lt;li&gt;Павел Дуров откладывал деньги на хостинг с 1 курса :)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="podvodim-itogi"&gt;Подводим итоги&lt;/h2&gt;
&lt;p&gt;В целом Вконтакте развивается в сторону увеличения скорости
распространения информацию внутри сети. Приоритеты поменялись в этом
направлении достаточно недавно, этим обусловлено, например, перенос
выхода почтового сервиса Вконтакте, о котором очень активно говорили
когда появилась возможность забивать себе текстовые URL вроде
&lt;code&gt;vkontakte.ru/ivan.blinkov&lt;/code&gt;. Сейчас этот подпроект имеет низкий приоритет
и ждет своего часа, когда они смогут предложить что-то более удобное и
быстрое, чем Gmail.&lt;/p&gt;
&lt;p&gt;Завеса тайны насчет технической реализации Вконтакте была немного
развеяна, но много моментов все же остались секретом. Возможно в будущем
появится более детальная информация о собственной СУБД Вконтакте,
которая как оказалось является ключом к решению всех самых сложных
моментов в масштабируемости системы.&lt;/p&gt;
&lt;p&gt;Как я уже упоминал этот пост написан почти на память, на основе
небольшого конспекта "круглого стола Вконтакте", так что хочется сразу
извиниться за возможные неточности и недопонимания. Я лишь
структурировал хаотичную кучу ответов на вопросы. Буду рад уточнениям и
дополнениям.&lt;/p&gt;
&lt;p&gt;Если хотите быть в курсе новых веяний в сфере масштабируемости
высоконагруженных интернет-проектов - по традиции рекомендую
&lt;a href="/feed/"&gt;подписаться на RSS&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Thu, 28 Oct 2010 21:12:00 +0400</pubDate><guid>tag:www.insight-it.ru,2010-10-28:highload/2010/arkhitektura-vkontakte/</guid><category>Apache</category><category>C++</category><category>Debian</category><category>featured</category><category>ffmpeg</category><category>HAProxy</category><category>highload</category><category>Linux</category><category>Memcached</category><category>mod_php</category><category>MySQL</category><category>nginx</category><category>node.js</category><category>openssl</category><category>PHP</category><category>XCache</category><category>xfs</category><category>Архитектура Вконтакте</category><category>Вконтакте</category></item><item><title>Архитектура Facebook</title><link>https://www.insight-it.ru//highload/2010/arkhitektura-facebook/</link><description>&lt;p&gt;&lt;img alt="Facebook Logo" class="left" src="https://www.insight-it.ru/images/facebook_logo.jpg" title="Facebook Logo"/&gt;
На сегодняшний день &lt;a href="https://www.insight-it.ru/goto/fbae133c/" rel="nofollow" target="_blank" title="https://www.facebook.com"&gt;Facebook&lt;/a&gt; является пожалуй
самым обсуждаемым интернет-проектом во всем мире. Не смотря на довольно
низкий уровень проникновения Facebook в России, темпы захвата аудитории
этим проектом мягко говоря поражают. Как же им удается управляться с
таким огромным социальным графом и удовлетворять потребности в общении
невероятно большого количества людей по всему миру?&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id="platforma"&gt;Платформа&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/tag/linux/"&gt;Linux&lt;/a&gt; - операционная система&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/php/"&gt;PHP&lt;/a&gt; с &lt;a href="/tag/hiphop/"&gt;HipHop&lt;/a&gt; - код на PHP компилируется в
    C++&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/memcached/"&gt;memcached&lt;/a&gt; - агрессивное кэширование объектов&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/mysql/"&gt;MySQL&lt;/a&gt; - используется как хранилище пар ключ-значение,
    никаких join'ов&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/thrift/"&gt;Thrift&lt;/a&gt; - интерфейс взаимодействия между сервисами,
    написанными на разных языках программирования&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/scribe/"&gt;Scribe&lt;/a&gt; - универсальная система сбора и агрегации
    данных с рабочих серверов&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="statistika"&gt;Статистика&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Более 500 миллионов активных пользователей (месячная аудитория)&lt;/li&gt;
&lt;li&gt;Более миллиарда социальных связей&lt;/li&gt;
&lt;li&gt;Более 200 миллиардов просмотров страниц в месяц&lt;/li&gt;
&lt;li&gt;Более 4 триллионов действий попадает в новостные ленты каждый день&lt;/li&gt;
&lt;li&gt;Более 150 миллионов обращений к кэшу в секунду; 2 триллиона объектов
    в кэше&lt;/li&gt;
&lt;li&gt;Более 8 миллиардов минут провели пользователи на Facebook'е
    ежедневно&lt;/li&gt;
&lt;li&gt;Более 3 миллиардов фотографий загружается каждый месяц, до 1.2
    миллиона фотографий в секунду&lt;/li&gt;
&lt;li&gt;20 миллиардов фотографий в 4 разрешениях = 80 миллиардов фотографий,
    их бы хватило чтобы покрыть поверхность земли в 10 слоев; это
    больше, чем на всех других фото-ресурсах в месте взятых&lt;/li&gt;
&lt;li&gt;О более чем 5 миллиардах единиц контента рассказывается друзьям
    еженедельно&lt;/li&gt;
&lt;li&gt;Более миллиарда сообщений в чате каждый день&lt;/li&gt;
&lt;li&gt;Более ста миллионов поисковых запросов в день&lt;/li&gt;
&lt;li&gt;Более 250 приложений и 80 тысяч сторонних ресурсов на платформе
    Facebook Connect&lt;/li&gt;
&lt;li&gt;Более 400 тысяч разработчиков сторонних приложений&lt;/li&gt;
&lt;li&gt;Менее 500 разработчиков и системных администраторов в штате&lt;/li&gt;
&lt;li&gt;Более миллиона активных пользователей на одного инженера&lt;/li&gt;
&lt;li&gt;Десятки тысяч серверов, десятки гигабит трафика&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="arkhitektura"&gt;Архитектура&lt;/h2&gt;
&lt;h3 id="obshchie-printsipy"&gt;Общие принципы&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Балансировщик нагрузки выбирает веб-сервер для обработки запроса&lt;/li&gt;
&lt;li&gt;PHP-код в веб-сервере подготавливает HTML, пользуясь данными из
    различных источников:&lt;ul&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;memcached&lt;/li&gt;
&lt;li&gt;Специализированные сервисы&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Если взглянуть с другой стороны, то получим трехуровневую
    архитектуру:&lt;ul&gt;
&lt;li&gt;Вер-приложение&lt;/li&gt;
&lt;li&gt;Распределенный индекс&lt;/li&gt;
&lt;li&gt;Постоянное хранилище&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Использование открытых технологий там, где это возможно&lt;/li&gt;
&lt;li&gt;Поиск возможностей оптимизации используемых продуктов&lt;/li&gt;
&lt;li&gt;Философия Unix:&lt;ul&gt;
&lt;li&gt;Старайтесь делать каждый компонент системы простым и
    производительным&lt;/li&gt;
&lt;li&gt;Комбинируйте компоненты для решения задач&lt;/li&gt;
&lt;li&gt;Концентрируйте внимание на хорошо обозначенных точках
    взаимодействия&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Все усилия направлены на масштабируемость&lt;/li&gt;
&lt;li&gt;Попытки минимизации количества точек отказа&lt;/li&gt;
&lt;li&gt;Простота, простота, простота!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="php"&gt;PHP&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Почему PHP?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Во многом "так исторически сложилось"&lt;/li&gt;
&lt;li&gt;Хорошо подходит для веб-разработки&lt;/li&gt;
&lt;li&gt;Легок в изучении: небольшой набор выражений и языковых конструкций&lt;/li&gt;
&lt;li&gt;Легок в написании: нестрогая типизация и универсальный "массив"&lt;/li&gt;
&lt;li&gt;Легок в чтении: синтаксис похож на C++ и Java&lt;/li&gt;
&lt;li&gt;Прост в дебаггинге: нет необходимости в перекомпиляции&lt;/li&gt;
&lt;li&gt;Большой ассортимент библиотек, актуальных для веб-проектов&lt;/li&gt;
&lt;li&gt;Подходит для процесса разработки с короткими итерациями&lt;/li&gt;
&lt;li&gt;Активное сообщество разработчиков по всему миру&lt;/li&gt;
&lt;li&gt;Динамическая типизация, интерпретируемый язык для скриптов&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Как оказалось на самом деле?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Высокий расход оперативной памяти и вычислительных ресурсов&lt;/li&gt;
&lt;li&gt;Сложно работать, когда объем исходного кода очень велик: слабая
    типизация и ограниченные возможности для статичного анализа и
    оптимизации кода&lt;/li&gt;
&lt;li&gt;Не особо оптимизирован для использования в крупных проектах&lt;/li&gt;
&lt;li&gt;Линейный рост издержек при подключении файлов с исходным кодом&lt;/li&gt;
&lt;li&gt;Механизм разработки расширений не очень удобен&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Доработки:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Оптимизация байт-кода&lt;/li&gt;
&lt;li&gt;Улучшения в APC (ленивая загрузка, оптимизация блокировок,
    "подогрев" кэша)&lt;/li&gt;
&lt;li&gt;Свои расширения (клиент memcache, формат сериализации, логи,
    статистика, мониторинг, механизм асинхронной обработки событий)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.insight-it.ru/goto/169aa287/" rel="nofollow" target="_blank" title="http://github.com/facebook/hiphop-php"&gt;HipHop&lt;/a&gt;&lt;/strong&gt; - трансформатор
    исходных кодов:&lt;ul&gt;
&lt;li&gt;Разработчики пишут на PHP, который конвертируется в
    оптимизированный C++&lt;/li&gt;
&lt;li&gt;Статический анализ, определение типов данных, генерация кода,
    и.т.д.&lt;/li&gt;
&lt;li&gt;Облегчает разработку расширений&lt;/li&gt;
&lt;li&gt;Существенно сокращает расходы оперативной памяти и
    вычислительных ресурсов&lt;/li&gt;
&lt;li&gt;У команды из трех программистов ушло полтора года на разработку,
    переписаны большая часть интерпретатора и многие расширения
    языка&lt;/li&gt;
&lt;li&gt;Опубликован под opensource лицензией в начале года, нет
    необходимости проходить этот же путь с нуля&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mysql"&gt;MySQL&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Как используется MySQL?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Используется как хранилище пар ключ-значение&lt;/li&gt;
&lt;li&gt;Большое количество логических узлов распределено между физическими
    машинами&lt;/li&gt;
&lt;li&gt;Балансировка нагрузке на уровне физических серверов&lt;/li&gt;
&lt;li&gt;Репликация для распределения операций чтения &lt;strong&gt;не&lt;/strong&gt; используется&lt;/li&gt;
&lt;li&gt;Большинство запросов касаются самой свежей информации: оптимизация
    таблиц для доступа к новым данным, архивация старых записей&lt;/li&gt;
&lt;li&gt;В целом быстро и надежно&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Как оказалось на самом деле?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Логическая миграция данных &lt;em&gt;очень&lt;/em&gt; сложна&lt;/li&gt;
&lt;li&gt;Создавать большое количество логических баз данных и
    перераспределять их между физическими узлами, балансируя таким
    образом нагрузку, намного удобнее&lt;/li&gt;
&lt;li&gt;Никаких join'ов на рабочих серверах баз данных&lt;/li&gt;
&lt;li&gt;Намного проще наращивать вычислительные мощности на веб-серверах,
    чем на серверах баз данных&lt;/li&gt;
&lt;li&gt;Схемы, основанные на структуре данных, делают программистов
    счастливыми и создают большую головную боль администраторам&lt;/li&gt;
&lt;li&gt;Никогда не храните не-статичные данные в централизованное базе
    данных&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Доработки:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Практически никаких модификаций исходного кода MySQL&lt;/li&gt;
&lt;li&gt;Своя схема партиционирования с глобально-уникальными
    идентификаторами&lt;/li&gt;
&lt;li&gt;Своя схема архивирования, основанная на частоте доступа к данным
    относительно каждого пользователя&lt;/li&gt;
&lt;li&gt;Расширенный движок запросов для репликации между датацентрами и
    поддержания консистенции кеша&lt;/li&gt;
&lt;li&gt;Библиотеки для доступа к данным на основе графа:&lt;ul&gt;
&lt;li&gt;Объекты (вершины графа) с ограниченными типами данных (целое
    число, строка ограниченно длины, текст)&lt;/li&gt;
&lt;li&gt;Реплицированные связи (ребра графа)&lt;/li&gt;
&lt;li&gt;Аналоги распределенных внешних ключей (foreign keys)&lt;/li&gt;
&lt;li&gt;Большинство данных распределено случайно&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="memcache"&gt;Memcache&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Как используется memcached?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Высокопроизводительная распределенная хэш-таблица&lt;/li&gt;
&lt;li&gt;Содержит "горячие" данные из MySQL&lt;/li&gt;
&lt;li&gt;Снижает нагрузку на уровень баз данных&lt;/li&gt;
&lt;li&gt;Основная форма кэширования&lt;/li&gt;
&lt;li&gt;Используется более 25TB памяти на нескольких тысячах серверов&lt;/li&gt;
&lt;li&gt;Среднее время отклика менее 250 микро-секунд&lt;/li&gt;
&lt;li&gt;Кэшируются сериализованные структуры данных PHP&lt;/li&gt;
&lt;li&gt;Отсутствие автоматического механизма проверки консистенции данных
    между memcached и MySQL - приходится делать это на уровне
    программного кода&lt;/li&gt;
&lt;li&gt;Множество multi-get запросов для получения данных на другом конце
    ребер графа&lt;/li&gt;
&lt;li&gt;Ограниченная модель данных, неэффективен для маленьких объектов&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Доработки:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Порт на 64-битную архитектуру&lt;/li&gt;
&lt;li&gt;Более эффективная сериализация&lt;/li&gt;
&lt;li&gt;Многопоточность&lt;/li&gt;
&lt;li&gt;Улучшенный протокол&lt;/li&gt;
&lt;li&gt;Компрессия&lt;/li&gt;
&lt;li&gt;Проксирование запросов&lt;/li&gt;
&lt;li&gt;Доступ к memcache через UDP:&lt;ul&gt;
&lt;li&gt;уменьшает расход памяти благодаря отсутствию тысяч буферов TCP
    соединений&lt;/li&gt;
&lt;li&gt;управление ходом исполнения приложение (оптимизация для
    multi-get)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Статистика о работе потоков по запросу - уменьшает блокировки&lt;/li&gt;
&lt;li&gt;Ряд изменений в ядре Linux для оптимизации работы memcache:&lt;ul&gt;
&lt;li&gt;распределение управления сетевыми прерывания по всем ядрам&lt;/li&gt;
&lt;li&gt;оппортунистический опрос сетевых интерфейсов&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;После вышеперечисленных модификаций memcached способен выполнять до
    250 тысяч операций в секунду, по сравнению со стандартными 30-40
    тысячами без данных изменений&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="thrift"&gt;Thrift&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Что это?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Легкий механизм построения приложений с использованием нескольких
    языков программирования&lt;/li&gt;
&lt;li&gt;Высокая цель: предоставить механизм прозрачного взаимодействия между
    языками программирования.&lt;/li&gt;
&lt;li&gt;Предоставляет язык описания интерфейсов, статический генератор кода&lt;/li&gt;
&lt;li&gt;Поддерживаемые языки: &lt;a href="/tag/c/"&gt;C++&lt;/a&gt;, &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;,
    &lt;a href="/tag/python/"&gt;Python&lt;/a&gt;, &lt;a href="/tag/java/"&gt;Java&lt;/a&gt;, &lt;a href="/tag/ruby/"&gt;Ruby&lt;/a&gt;,
    &lt;a href="/tag/erlang/"&gt;Erlang&lt;/a&gt;, &lt;a href="/tag/perl/"&gt;Perl&lt;/a&gt;, &lt;a href="/tag/haskell/"&gt;Haskell&lt;/a&gt; и
    многие другие&lt;/li&gt;
&lt;li&gt;Транспорты: простой интерфейс для ввода-вывода (сокеты, файлы,
    буферы в памяти)&lt;/li&gt;
&lt;li&gt;Протоколы: стандарты сериализации (бинарный, JSON)&lt;/li&gt;
&lt;li&gt;Серверы: неблокирующие, асинхронные, как однопоточные, так и
    многопоточные&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Почему именно Thrift?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Альтернативные технологии: SOAP, CORBA, COM, Pillar, Protocol
    Buffers - но у всех есть свои существенные недостатки, что вынудило
    Facebook создать свою технологию&lt;/li&gt;
&lt;li&gt;Он быстрый, очень быстрый&lt;/li&gt;
&lt;li&gt;Меньше рабочего времени тратится каждым разработчиком на сетевые
    интерфейсы и протоколы&lt;/li&gt;
&lt;li&gt;Разделение труда: работа над высокопроизводительными серверами
    ведется отдельно от работы над приложениями&lt;/li&gt;
&lt;li&gt;Общий инструментарий, знакомый всем разработчикам&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="scribe"&gt;Scribe&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Что это?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Масштабированный распределенный механизм ведения логов&lt;/li&gt;
&lt;li&gt;Перемещает данные с серверов в центральный репозиторий&lt;/li&gt;
&lt;li&gt;Широкая сфера применения:&lt;ul&gt;
&lt;li&gt;Логи поисковых запросов&lt;/li&gt;
&lt;li&gt;Публикации в новостных лентах&lt;/li&gt;
&lt;li&gt;Данные по A/B тестированиям&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Более надежен, чем традиционные системы логгирования, но
    недостаточно надежен для транзакций баз данных&lt;/li&gt;
&lt;li&gt;Простая модель данных&lt;/li&gt;
&lt;li&gt;Построен на основе Thrift&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="khranenie-fotografii"&gt;Хранение фотографий&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Сначала сделали это просто:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Загрузка на сервер: приложение принимает изображение, создает
    миниатюры в нужных разрешениях, сохраняет в NFS&lt;/li&gt;
&lt;li&gt;Загрузка с сервера: изображения отдаются из NFS через HTTP&lt;/li&gt;
&lt;li&gt;NFS построена на коммерческих продуктах&lt;/li&gt;
&lt;li&gt;Это было необходимо, чтобы сначала проверить, что продукт
    востребован пользователями и они правда будут активно загружать
    фотографии&lt;/li&gt;
&lt;li&gt;На самом деле оказалось, что:&lt;ul&gt;
&lt;li&gt;Файловые системы непригодны для работы с большим количеством
    небольших файлов&lt;/li&gt;
&lt;li&gt;Метаданные не помещаются в оперативную память, что приводит к
    дополнительным обращениям к дисковой подсистеме&lt;/li&gt;
&lt;li&gt;Ограничивающим фактором является ввод-вывод, а не плотность
    хранения&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Потом начали оптимизировать:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Кэширование более часто используемых миниатюр изображений в памяти
    на оригинальных серверах для масштабируемости, надежности и
    производительности&lt;/li&gt;
&lt;li&gt;Распределение их по &lt;a href="/tag/cdn/"&gt;CDN&lt;/a&gt; для уменьшения сетевых задержек&lt;/li&gt;
&lt;li&gt;Возможно сделать еще лучше:&lt;ul&gt;
&lt;li&gt;Хранение изображений в больших бинарных файлах (blob)&lt;/li&gt;
&lt;li&gt;Сервис, отвечающий за фотографии имеет информацию о том, в каком
    файле и с каким отступом от начала расположена каждая фотография
    (по ее идентификатору)&lt;/li&gt;
&lt;li&gt;Этот сервис в Facebook называется Haystack и он оказался в 10
    раз эффективнее "простого" подхода и в 3 раза эффективнее
    "оптимизированного"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="drugie-servisy"&gt;Другие сервисы&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SMC&lt;/strong&gt;: консоль управления сервисами - централизованная
    конфигурация, определение на какой физической машине работает
    логический сервис&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ODS&lt;/strong&gt;:&amp;nbsp;инструмент для визуализации изменений любых статистических
    данных, имеющихся в системе; удобен для мониторинга и оповещений&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gatekeeper:&lt;/strong&gt; разделение развертывания и запуска, A/B
    тестирования, таргетированный запуск, постепенный запуск&lt;/li&gt;
&lt;li&gt;И еще около 50 других сервисов...&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="kak-eto-rabotaet-vse-vmeste_1"&gt;Как это работает все вместе?&lt;/h2&gt;
&lt;h3 id="novye-albomy-druzei"&gt;Новые альбомы друзей&lt;/h3&gt;
&lt;p&gt;&lt;img alt="Facebook Screenshot" class="responsive-img" src="https://www.insight-it.ru/images/facebook_screenshot.jpg" title="Facebook"/&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Получаем профиль по идентификатору пользователя (скорее всего из
    кэша, но потенциально возможно обращение к базе данных)&lt;/li&gt;
&lt;li&gt;Получаем список друзей (опять же на основе идентификатора
    пользователя из кэша или из базы данных в случае промаха)&lt;/li&gt;
&lt;li&gt;Параллельно запрашиваем идентификаторы последних 10 альбомов для
    каждого из друзей (multi-get, каждый промах мимо кэша индивидуально
    вытаскивается из MySQL)&lt;/li&gt;
&lt;li&gt;Параллельно получаем данные о всех альбомах (на основе
    идентификаторов альбомов из предыдущего шага)&lt;/li&gt;
&lt;li&gt;Все данные получены, выполняем логику отрисовки конкретной страницы
    на PHP&lt;/li&gt;
&lt;li&gt;Отправляем HTML в браузер, пользователь счастлив :)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="novostnaia-lenta"&gt;Новостная лента&lt;/h3&gt;
&lt;p&gt;&lt;img alt="News Feed Screenshot" class="responsive-img" src="https://www.insight-it.ru/images/facebook_screenshot_2.jpg" title="News Feed"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="News Feed Scheme" class="responsive-img" src="https://www.insight-it.ru/images/facebook_screenshot_3.jpg" title="News Feed"/&gt;&lt;/p&gt;
&lt;h3 id="poisk"&gt;Поиск&lt;/h3&gt;
&lt;p&gt;&lt;img alt="Search Screenshot" class="responsive-img" src="https://www.insight-it.ru/images/facebook_screenshot_4.jpg" title="Search"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Search Scheme" class="responsive-img" src="https://www.insight-it.ru/images/facebook_screenshot_5.jpg" title="Search"/&gt;&lt;/p&gt;
&lt;h2 id="podvodim-itogi_1"&gt;Подводим итоги&lt;/h2&gt;
&lt;p&gt;LAMP не идеален&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PHP+MySQL+Memcache решает большинство задач, но не может решить
    совсем все:&lt;ul&gt;
&lt;li&gt;PHP не может хранить состояния&lt;/li&gt;
&lt;li&gt;PHP не самый производительный язык&lt;/li&gt;
&lt;li&gt;Все данные находятся удаленно&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Facebook разрабатывает собственные внутренние сервисы, чтобы:&lt;ul&gt;
&lt;li&gt;Располагать исполняемый код ближе к данным&lt;/li&gt;
&lt;li&gt;Скомпилированное окружение более эффективно&lt;/li&gt;
&lt;li&gt;Некоторая функциональность присутствует только в других языках
    программирования&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Философия сервисов:&lt;ul&gt;
&lt;li&gt;Создание сервисов только при необходимости (минимизация издержек
    по развертке, поддержке и ведению отдельной кодовой базы;
    потенциальная дополнительная точка сбоя)&lt;/li&gt;
&lt;li&gt;Создание общего набора инструментов для создания сервисов
    (Thrift, Scribe, ODS, средства мониторинга и уведомлений)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Использование правильных языка программирования, библиотек и
    инструментов для решения задачи&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Возвращение инноваций общественности - важный аспект разработки в
    Facebook:&lt;ul&gt;
&lt;li&gt;Опубликованные свои проекты:&lt;ul&gt;
&lt;li&gt;Thrift&lt;/li&gt;
&lt;li&gt;Scribe&lt;/li&gt;
&lt;li&gt;Tornado&lt;/li&gt;
&lt;li&gt;Cassandra&lt;/li&gt;
&lt;li&gt;Varnish&lt;/li&gt;
&lt;li&gt;Hive&lt;/li&gt;
&lt;li&gt;xhprof&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Доработки популярных решений:&lt;ul&gt;
&lt;li&gt;PHP&lt;/li&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;memcached&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Информация о взаимодействии Facebook с opensource-сообществом,
    этих и других проектах расположена на &lt;a href="https://www.insight-it.ru/goto/535d8e6b/" rel="nofollow" target="_blank" title="http://developers.facebook.com/opensource/"&gt;странице, посвященной
    opensource&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ключевые моменты культуры разработки в Facebook:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Двигайся быстро&lt;/strong&gt; и не бойся ломать некоторые вещи&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Большое влияние&lt;/strong&gt; маленьких команд&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Будь откровенным&lt;/strong&gt; и инновационным&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="istochniki-informatsii"&gt;Источники информации&lt;/h2&gt;
&lt;p&gt;Данная статья не является переводом готовой статьи, в качестве
источников информации послужили записи выступлений сотрудников Facebook
на конференциях:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/4c672c94/" rel="nofollow" target="_blank" title="http://www.infoq.com/presentations/Facebook-Software-Stack"&gt;Facebook Architecture: Science and the Social Graph&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/2ccd1899/" rel="nofollow" target="_blank" title="http://www.infoq.com/presentations/Facebook-Moving-Fast-at-Scale"&gt;Facebook: Moving Fast at Scale&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/3867625a/" rel="nofollow" target="_blank" title="http://www.infoq.com/presentations/Scale-at-Facebook"&gt;Scale at Facebook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Очень рекомендую посмотреть материалы в оригинале, так как естественно я
осветил в статье далеко не все, да и неточности какие-либо неисключены.
Помимо этого возможно многим будет интересно мероприятие &lt;a href="https://www.insight-it.ru/goto/ff11ad2b/" rel="nofollow" target="_blank" title="http://styleru.timepad.ru/event/3571"&gt;"Facebook: how we scaled to 500 000 000 users "&lt;/a&gt;,
где Robert Johnson выступает 22 октября в Москве. Еще он числится в
списке докладчиков &lt;a href="https://www.insight-it.ru/goto/727c9436/" rel="nofollow" target="_blank" title="http://www.highload.ru"&gt;Highload++&lt;/a&gt; с аналогичным
выступлением. Дополнительную информацию можно почерпнуть в &lt;a href="https://www.insight-it.ru/goto/f38bc794/" rel="nofollow" target="_blank" title="http://facebook.com/eblog"&gt;блоге инженеров Facebook&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPD:&lt;/strong&gt; Обновил некоторые моменты после посещения вышеупомянутого
выступления Роберта.&lt;/p&gt;
&lt;p&gt;И по традиции напоминаю, что так как я пишу довольно редко - читать мой
блог намного удобнее по &lt;a href="/feed/"&gt;RSS&lt;/a&gt;. Спасибо за внимание :)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Wed, 20 Oct 2010 13:02:00 +0400</pubDate><guid>tag:www.insight-it.ru,2010-10-20:highload/2010/arkhitektura-facebook/</guid><category>CDN</category><category>Facebook</category><category>featured</category><category>HipHop</category><category>Linux</category><category>Memcached</category><category>MySQL</category><category>ODS</category><category>PHP</category><category>Scribe</category><category>Thrift</category><category>Архитектура Facebook</category></item><item><title>memcached на пальцах</title><link>https://www.insight-it.ru//storage/2009/memcached-na-palcakh/</link><description>&lt;p&gt;Ранее уже была сделана публикация с &lt;a href="https://www.insight-it.ru/storage/2008/obzor-memcached/"&gt;обзором memcached&lt;/a&gt;. Давайте вернемся к данной теме и рассмотрим практику работы с memcached на примерах.
&lt;!--more--&gt;&lt;/p&gt;
&lt;div class="card blue lighten-4"&gt;
&lt;p&gt;&lt;div class="card-content justify"&gt;
К сожалению, у меня по прежнему не доходят руки активно заниматься
блогом, но наконец-то появился появился первый человек, откликнувшийся
на &lt;a href="https://www.insight-it.ru/guest-posts/"&gt;мое предложение стать гостевым автором данного блога&lt;/a&gt;.
Его имя &lt;em&gt;Владислав Клименко&lt;/em&gt; и именно он является автором данного поста,
а я лишь выступаю в роли редактора. Может быть данный пример подтолкнет
и других читателей поучаствовать в возвращении &lt;strong class="trebuchet"&gt;Insight IT&lt;/strong&gt; к жизни.
&lt;div class="right"&gt;С уважением,&lt;br&gt;Иван Блинков&lt;/br&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Итак, пара слов о предмете разговора. memcached - это распределенная
система кэширования объектов в оперативной памяти. Разрабатывается
фирмой &lt;a href="https://www.insight-it.ru/goto/4742ee7f/" rel="nofollow" target="_blank" title="http://danga.com/"&gt;Danga Interactive&lt;/a&gt; (кстати, они являются
авторами не только memcached, но и других интересных проектов). Но о
них, возможно, в следующий раз. Обычно memcached используется
приложениями для временного хранения данных, которые надо часто читать.
Приложения не взаимодействуют (обычно) напрямую с сервером memcached, а
работают при помощи клиентских библиотек. На настоящее время созданы
библиотеки для многих языков программирования (а для некоторых еще и по
нескольку альтернативных)&amp;nbsp; - полный список клиентских библиотек доступен
на &lt;a href="https://www.insight-it.ru/goto/39c492fb/" rel="nofollow" target="_blank" title="http://code.google.com/p/memcached/wiki/Clients"&gt;wiki проекта&lt;/a&gt;. В
целом, данная схема похожа на работу с БД, знакомую многим
разработчикам.&lt;/p&gt;
&lt;p&gt;Будем рассматривать установку и использование memcached для Linux. Так
же при рассмотрении примеров на PHP и обзоре кэширования сессий
потребуются PHP и Apache. Возможно, их придется установить, но мы не
будем заострять внимание на вопросах установки.&lt;/p&gt;
&lt;h2 id="server-memcached"&gt;Сервер memcached&lt;/h2&gt;
&lt;p&gt;Давайте приступим к установке memcached. Практически во всех
дистрибутивах Linux memcached можно установить из репозитариев. Если
есть желание собрать самую свежую версию, то можно заглянуть на &lt;a href="https://www.insight-it.ru/goto/df55df38/" rel="nofollow" target="_blank" title="http://danga.com/memcached/"&gt;сайт
разработчика&lt;/a&gt; (на момент написания этих
строк последняя версия -
&lt;a href="https://www.insight-it.ru/goto/5fa8e800/" rel="nofollow" target="_blank" title="http://memcached.googlecode.com/files/memcached-1.4.0.tar.gz"&gt;1.4.0&lt;/a&gt;).
Также, возможно, понадобится установить libevent. Последняя стабильная
версия -
&lt;a href="https://www.insight-it.ru/goto/a42e2966/" rel="nofollow" target="_blank" title="http://www.monkey.org/~provos/libevent-1.4.11-stable.tar.gz"&gt;1.4.11&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Собираем, устанавливаем и запускаем memcached в режиме вывода сообщений.
Интересно же посмотреть, что с ним происходит:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;memcached -vv
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Процесс запускается и ждет подключений (по умолчанию на порту 11211).
Серверная часть готова обрабатывать подключения клиентов и кэшировать
полученные данные.&lt;/p&gt;
&lt;p&gt;Но для разработчика приложений это только полпути. Необходимо поддержать
работу с memcached в своем приложении. Для этого, рассмотрим некоторые
существующие клиентские библиотеки memcached.&lt;/p&gt;
&lt;h2 id="klienty-memcached"&gt;Клиенты memcached&lt;/h2&gt;
&lt;p&gt;Из всего многообразия клиентских библиотек рассмотрим две:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;libmemcached (для Си);&lt;/li&gt;
&lt;li&gt;PECL extension для PHP (построенный на базе предыдущей библиотеки).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="si"&gt;Си&lt;/h2&gt;
&lt;p&gt;Библиотека libmemcached на данный момент активно развивается и
представляется наиболее подходящим выбором при работе с Си и PHP. Также,
в комплекте с самой клиентской библиотекой поставляются дополнительные
утилиты для работы с memcached, позволяющие просматривать,
устанавливать, удалять значения в кэше memcached. Кстати, удивляет, что
набор утилит идет не с серверной частью, а с клиентской библиотекой.&lt;/p&gt;
&lt;p&gt;Итак, приступим к установке libmemcached. На момент написания этих строк
текущая версия libmemcached -
&lt;a href="https://www.insight-it.ru/goto/44752735/" rel="nofollow" target="_blank" title="http://download.tangent.org/libmemcached-0.31.tar.gz"&gt;0.31&lt;/a&gt;.
Компилируем, устанавливаем. Для начала, наслаждаемся чтением страниц
man:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;man libmemcached
man libmemcached_examples
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;C библиотекой поставляются описание несложных примеров использования. За
более интересными же способами применения имеет смысл заглянуть в
исходные тексты утилит, благо все идет вместе.&lt;/p&gt;
&lt;p&gt;Рекомендую обратить внимание на собранные утилиты. Наверняка многие из
них станут верными помощниками при разработке приложений.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;memstat&lt;/code&gt; - выдает информацию о сервере memcached&lt;/li&gt;
&lt;li&gt;&lt;code&gt;memcat&lt;/code&gt; - выдает значение по ключу&lt;/li&gt;
&lt;li&gt;&lt;code&gt;memrm&lt;/code&gt; - удаляет значение по ключу&lt;/li&gt;
&lt;li&gt;&lt;code&gt;memdump&lt;/code&gt; - выдает список ключей&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Для начала посмотрим, что скажет сервер memcached, запущенный нами
немного ранее в режиме выдачи сообщений. Запросим статистику сервера при
помощи утилиты memstat:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;memstat --servers localhost

 Listing &lt;span class="m"&gt;1&lt;/span&gt; Server
 Server: localhost &lt;span class="o"&gt;(&lt;/span&gt;11211&lt;span class="o"&gt;)&lt;/span&gt;
 pid: 14534
  uptime: 1950
 &lt;span class="nb"&gt;time&lt;/span&gt;: 1247390264
 version: 1.4.0
 pointer_size: 32
 rusage_user: 0.0
 rusage_system: 0.0
 curr_items: 0
 total_items: 0
 bytes: 0
 curr_connections: 10
 total_connections: 11
 connection_structures: 11
 cmd_get: 0
 cmd_set: 0
 get_hits: 0
 get_misses: 0
 evictions: 0
 bytes_read: 0
 bytes_written: 0
 limit_maxbytes: 67108864
 threads: 5
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Получили статистику - следовательно memcached функционирует и
откликается на запросы.&lt;/p&gt;
&lt;p&gt;Итак, на настоящий момент готовы к использованию сервер memcached и
клиентская библиотека. Осталось дело за малым - внедрить использование
memcached в разрабатываемое приложение. Что касается приложения - все в
руках разработчиков, а мы рассмотрим небольшой пример работы с базовыми
функциями.&lt;/p&gt;
&lt;p&gt;memcached предоставляет следующий набор основных функций (их, конечно,
больше, но здесь приведены основные):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;set&lt;/strong&gt;&amp;nbsp;- занести в кэш пару ключ-значение&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;add&lt;/strong&gt;&amp;nbsp;- занести в кэш значение при условии, что значения с таким
    ключом в кэше еще нет&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;replace&lt;/strong&gt; - обновляет кэш при условии, что значение с таким ключом
    в кэше уже есть&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;get&lt;/strong&gt; - получает значение из кэша по указанному ключу&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="primer-programmy-na-c"&gt;Пример программы на C&lt;/h3&gt;
&lt;p&gt;Файл mc.c:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;#include "stdio.h"&lt;/span&gt;
&lt;span class="cp"&gt;#include "string.h"&lt;/span&gt;
&lt;span class="cp"&gt;#include "memcached.h"&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;value2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;memcached_return&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. создать структуру для работы с кэшем&lt;/span&gt;
    &lt;span class="n"&gt;memcached_st&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;memc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memcached_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. указать сервер с которым будем работать&lt;/span&gt;
    &lt;span class="n"&gt;memcached_server_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;11211&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. занести пару ключ-значение в кэш&lt;/span&gt;
    &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memcached_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;time_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;MEMCACHED_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// обработать ошибку&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. получить значение&lt;/span&gt;
    &lt;span class="n"&gt;value2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memcached_get&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;     &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;MEMCACHED_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// обработать ошибку&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 5. высвободить структуру&lt;/span&gt;
    &lt;span class="n"&gt;memcached_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Программа состоит из 5 основных операций и в особых комментариях не
нуждается. Разве что можно отметить, что в пункте 2 можно добавлять
много серверов, в случае использования распределенной системы.&lt;/p&gt;
&lt;p&gt;Компилируем, возможно придется явно указать пути к библиотекам:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;gcc -Wall -o mc mc.c -I/usr/local/include/libmemcached/ -lmemcached
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Запускаем:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;./mc
 value
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Видим требуемое значение - должно быть, &lt;em&gt;заработало&lt;/em&gt;!&lt;/p&gt;
&lt;p&gt;Для уточнения деталей, смотрим сообщения на сервере memcached:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&amp;lt;&lt;span class="m"&gt;32&lt;/span&gt; new auto-negotiating client connection
32: Client using the ascii protocol
&lt;span class="m"&gt;32&lt;/span&gt; STORED
&lt;span class="m"&gt;32&lt;/span&gt; sending key key
&amp;gt;32 END
&amp;lt;&lt;span class="m"&gt;32&lt;/span&gt; quit
&amp;lt;&lt;span class="m"&gt;32&lt;/span&gt; connection closed.
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В данном примере представлены следующие события: подключение клиента,
установка пары ключ-значение, чтение данных по ключу и отключение
клиента.&lt;/p&gt;
&lt;p&gt;Посмотрим статистику на сервере:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;memstat --servers localhost
 Listing &lt;span class="m"&gt;1&lt;/span&gt; Server
 Server: localhost &lt;span class="o"&gt;(&lt;/span&gt;11211&lt;span class="o"&gt;)&lt;/span&gt;
 pid: 14534
 uptime: 4659
 &lt;span class="nb"&gt;time&lt;/span&gt;: 1247392973
 version: 1.4.0
 pointer_size: 32
 rusage_user: 0.0
 rusage_system: 0.0
 curr_items: 1
 total_items: 1
 bytes: 58
 curr_connections: 10
 total_connections: 13
 connection_structures: 11
 cmd_get: 1
 cmd_set: 1
 get_hits: 1
 get_misses: 0
 evictions: 0
 bytes_read: 58
 bytes_written: 58
 limit_maxbytes: 67108864
 threads: 5
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Следующие две строчки показывают, что в кэше появилось значение:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;curr_items: 1
total_items: 1
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Посмотрим на данное значение:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;memcat --servers localhost key
 value
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Итак, приложение, использующее memcached - готово.&lt;/p&gt;
&lt;h2 id="php_1"&gt;PHP&lt;/h2&gt;
&lt;p&gt;Для начала установим PECL extension для PHP - memcached&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;pecl install memcached
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;На этом этапе возможно появление сообщения об ошибке вида:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;ERROR: &lt;span class="s1"&gt;'phpize'&lt;/span&gt; failed
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Это означает, что не установлен пакет php-dev или его аналог.
Устанавливаем его и можно пробовать снова:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;pecl install memcached
 install ok: channel://pecl.php.net/memcached-1.0.0
 You should add &lt;span class="s2"&gt;"extension=memcached.so"&lt;/span&gt; to php.ini
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Как нам и советуют, дописываем &lt;code&gt;extension=memcached.so&lt;/code&gt; в php.ini и
перезапускаем Apache.&lt;/p&gt;
&lt;p&gt;Смотрим информацию об используемом PHP:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;memcached support&amp;nbsp; enabled
Version  1.0.0
libmemcached version &amp;nbsp;&amp;nbsp; 0.31
Session support &amp;nbsp;&amp;nbsp; yes
igbinary support &amp;nbsp; no
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="primer-programmy-na-php"&gt;Пример программы на PHP&lt;/h3&gt;
&lt;p&gt;Можно смело использовать обращения к memcached из PHP. Как обычно,
рассмотрим пример:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Memcached&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;addServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11211&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'phpkey'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'phpvalue'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;var_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'phpkey'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Результат работы данного скрипта:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="x"&gt;string(8)&amp;nbsp; "phpvalue"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Итак, PHP-приложение, использующее memcached - готово.&lt;/p&gt;
&lt;h2 id="keshirovanie-dannykh-sessii_1"&gt;Кэширование данных сессий&lt;/h2&gt;
&lt;p&gt;Memcached можно использовать и как хранилище данных сессий для PHP.
Такой подход часто используется в реальных приложениях. Давайте
рассмотрим, что для этого надо сделать.&lt;/p&gt;
&lt;p&gt;Вносим изменения в php.ini&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c1"&gt;;session.save_handler = files&lt;/span&gt;
&lt;span class="na"&gt;session.save_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;memcached&lt;/span&gt;

&lt;span class="c1"&gt;;session.save_path = /var/lib/php5&lt;/span&gt;
&lt;span class="na"&gt;session.save_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;localhost:11211&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Параметр &lt;code&gt;session.save_handler&lt;/code&gt; указывает, что теперь данные будут
храниться в memcached. Второй параметр - &lt;code&gt;session.save_path&lt;/code&gt; указывает
сервер memcached (их может быть указано несколько, через запятую) на
котором будут сохранятся данные.&lt;/p&gt;
&lt;p&gt;Перезапускаем Apache - и готово!&lt;/p&gt;
&lt;p&gt;Теперь надо проверить, что теперь данные сессии реально хранятся не на
диске, а в memcached.&lt;/p&gt;
&lt;p&gt;Рассмотрим работу несложного скрипта, заносящего что-нибудь в сессию:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nb"&gt;session_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'intval'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'strval'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"qwe"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Запускаем скрипт, он заносит данные в сессию, после чего смотрим на кэш&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;memdump --servers localhost
 key
 keyphp
 memc.sess.key.3ff8ccab14424082ff83a6dfbcf0941f
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Итак - к нашим знакомым по предыдущим примерам ключам, добавился ключ с
характерным именем &lt;code&gt;memc.sess.key.3ff8ccab14424082ff83a6dfbcf0941f&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Хранение данных сессии перенесено в систему кэширования. Более подробную
информацию по работе с memcached из PHP можно почитать &lt;a href="https://www.insight-it.ru/goto/6146f92c/" rel="nofollow" target="_blank" title="http://ru2.php.net/manual/ru/book.memcached.php"&gt;на сайте PHP&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="zakliuchenie"&gt;Заключение&lt;/h2&gt;
&lt;p&gt;Мы рассмотрели установку и примеры использования memcached. Следует
особо подчеркнуть, что memcached - это не система хранения данных,
поэтому на практике memcached почти всегда используется в паре с БД.
Также следовало бы уделить внимание своевременной инвалидации данных в
кэше и вопросам безопасности. В общем, тема интересная, и еще далека от
закрытия.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Владислав Клименко</dc:creator><pubDate>Wed, 15 Jul 2009 15:09:00 +0400</pubDate><guid>tag:www.insight-it.ru,2009-07-15:storage/2009/memcached-na-palcakh/</guid><category>C++</category><category>Memcached</category><category>PHP</category><category>Программирование</category></item><item><title>Архитектура Digg</title><link>https://www.insight-it.ru//highload/2008/arkhitektura-digg/</link><description>&lt;p&gt;Трафик, генерируемый более чем 1.2 миллионами пользователей
&lt;a href="https://www.insight-it.ru/goto/e072c5ff/" rel="nofollow" target="_blank" title="http://www.digg.com"&gt;Digg&lt;/a&gt;, знаменитых своей жаждой информации,
способен загнать любой невинный сайт за рамки его вычислительных
ресурсов и пропускной способности канала. Как же сам Digg справляется с
такой нагрузкой?
&lt;!--more--&gt;&lt;/p&gt;
&lt;h3 id="istochniki-informatsii"&gt;Источники информации&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Этот текст - перевод
&lt;a href="https://www.insight-it.ru/goto/83eb8e27/" rel="nofollow" target="_blank" title="http://highscalability.com/digg-architecture"&gt;статьи&lt;/a&gt;, автор - &lt;a href="https://www.insight-it.ru/goto/f3f1b405/" rel="nofollow" target="_blank" title="http://highscalability.com/user/todd-hoff"&gt;Todd
Hoff&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/3c9da0db/" rel="nofollow" target="_blank" title="http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;amp;articleId=9017778"&gt;Как Digg.com использует LAMP для масштабирования&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/fa3d3949/" rel="nofollow" target="_blank" title="http://www.oreillynet.com/onlamp/blog/2006/04/digg_phps_scalability_and_perf.html"&gt;Масштабируемость и производительность PHP в Digg&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="platforma"&gt;Платформа&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/tag/mysql/"&gt;MySQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/linux/"&gt;Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/lucene/"&gt;Lucene&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/apc/"&gt;APC PHP Accelerator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/memcached/"&gt;Memcached&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="statistika"&gt;Статистика&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Проект стартовал в конце 2004 года на одном сервере под управлением
    &lt;a href="/tag/linux/"&gt;Linux&lt;/a&gt; с использованием &lt;a href="/tag/apache/"&gt;Apache 1.3&lt;/a&gt;, &lt;a href="/tag/php/"&gt;PHP
    4&lt;/a&gt; и &lt;a href="/tag/mysql/"&gt;MySQL 4.0&lt;/a&gt; (со стандартной системой
    хранения данных - MyISAM).&lt;/li&gt;
&lt;li&gt;Более 1.2 миллиона пользователей.&lt;/li&gt;
&lt;li&gt;Более 200 миллионов просмотров страниц в месяц.&lt;/li&gt;
&lt;li&gt;100 серверов расположены в нескольких датацентрах, из них:
    &amp;ndash; 20 серверов баз данных;
    &amp;ndash; 30 веб-серверов;
    &amp;ndash; несколько поисковых серверов, использующих Lucene;
    &amp;ndash; остальные используются для обеспечения избыточности.&lt;/li&gt;
&lt;li&gt;30 GB данных.&lt;/li&gt;
&lt;li&gt;Ни одна из проблем, с которыми пришлось столкнуться проекту не была
    связана с &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;, в основном они касались базы данных.&lt;/li&gt;
&lt;li&gt;Легковесная природа &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt; позволила переместить
    вычислительные работы из базы данных в приложение для улучшения
    производительности.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="chto-vnutri"&gt;Что внутри?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Балансировщик нагрузки равномерно распределяет запросы между
    &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt; серверами.&lt;/li&gt;
&lt;li&gt;MySQL используется по принципу master-slave:
    -&amp;nbsp; Сервера, обрабатывающие большое количество транзакций, используют
    движок InnoDB.
    -&amp;nbsp; Сервера, выполняющие аналитическую обработку данных в реальном
    времени, используют MyISAM.
    -&amp;nbsp; Снижения производительности при переходе с &lt;a href="/tag/mysql/"&gt;MySQL&lt;/a&gt; 4.1
    на версию 5 замечено не было.&lt;/li&gt;
&lt;li&gt;Для кэширования используется &lt;a href="/tag/memcached/"&gt;Memcached&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Используется сегментирование баз данных.&lt;/li&gt;
&lt;li&gt;Особенности использования Digg существенно облегчают процесс
    масштабирования. Большинство посетителей просто просматривают
    главную страницу и уходят. Это приводит к тому, что 98% запросов к
    базе данных являются операциями чтения. Такое соотношение операций
    чтения и записи позволяет не беспокоиться о комплексной работе по
    проектированию операций записи, что позволяет намного проще
    масштабировать проект.&lt;/li&gt;
&lt;li&gt;Возникали проблемы, связанные с системой хранения данных, которые
    сообщали, что данные уже записаны на диск, когда на самом деле это
    было не так. Контроллеры делали это для создания впечатления более
    высокой производительности. Но на практике это приводило лишь к
    проблемам с целостностью данных. Это достаточно распространенная
    проблема, которую порой не так уж просто решить, правда все зависит
    от используемого оборудования.&lt;/li&gt;
&lt;li&gt;Для облегчения нагрузки на базы данных используется кэширование и
    &lt;a href="/tag/apc/"&gt;APC PHP Accelerator&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;С использованием рабочих потоков &lt;a href="/tag/apache/"&gt;Apache2&lt;/a&gt;, FastCGI и
    &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt; акселератора возможно избежать необходимости каждый
    раз заново интерпретировать и компилировать PHP скрипты: скрипт
    компилируется только при первом обращении, что существенно ускоряет
    скорость его выполнения при последующих обращениях.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="podvodim-itogi"&gt;Подводим итоги&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Используйте возможность выбора движка для &lt;a href="/tag/mysql/"&gt;MySQL&lt;/a&gt;. Если
    Вам нужны транзакции - используйте InnoDB, если нет - MyISAM.
    Например, если на master сервере расположены транзакционные таблицы,
    то для slave серверов можно использовать и MyISAM.&lt;/li&gt;
&lt;li&gt;В определенный момент рост стал невозможен путем добавления
    дополнительной оперативной памяти, пришлось продолжать рост путем
    изменения архитектуры.&lt;/li&gt;
&lt;li&gt;Люди часто жалуются, что Digg медлителен. Скорее это вызвано их
    огромными &lt;a href="/tag/javascript/"&gt;JavaScript&lt;/a&gt; библиотеками, чем работой их
    серверной системы.&lt;/li&gt;
&lt;li&gt;Стоит тщательно выбирать какие именно приложения развертывать. Они
    приложили все усилия, чтобы не использовать приложения, требующие
    больших вычислительных мощностей. Очевидно, что Digg работает на
    совершенно стандартной &lt;a href="/tag/lamp/"&gt;LAMP&lt;/a&gt; архитектуре, но тем не
    менее реализована она достаточно интересно. У инженеров часто
    возникает желание реализовать какой-либо дополнительный функционал,
    но всегда стоит иметь ввиду, что они могут разрушить инфраструктуру,
    если она не сможет расти теми же темпами. Так что с этим стоит
    повременить до тех пор пока система сможет выдерживать все
    необходимые нагрузки. Это приводит к планированию ресурсов, особенно
    большое внимание этому аспекту уделяет &lt;a href="/tag/flickr/"&gt;Flickr&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Вам остается лишь догадываться, сможет ли &lt;a href="/tag/digg/"&gt;Digg&lt;/a&gt; удержать
    свои позиции, если и дальше будет ограничивать добавление новых
    возможностей, или уступит более активно развивающимся сервисам
    социальных закладок? Возможно если бы была возможность увеличивать
    масштабы более простыми методами, более быстрое добавление новых
    функций и возможностей позволило бы более эффективно конкурировать
    на этом рынке? С другой стороны, просто добавление новых
    возможностей может и не поменять ситуацию кардинальным образом.&lt;/li&gt;
&lt;li&gt;Основные проблемы с масштабируемостью и производительностью связаны
    с обработкой данных и в большинстве случаев они не зависят от
    используемого языка программирования. Вы столкнетесь с ними при
    работе с &lt;a href="/tag/java/"&gt;Java&lt;/a&gt;, &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;, &lt;a href="/tag/ruby/"&gt;Ruby&lt;/a&gt;, или
    подставьте сюда Ваш любимый язык программирования.&lt;/li&gt;
&lt;/ul&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Tue, 01 Apr 2008 20:49:00 +0400</pubDate><guid>tag:www.insight-it.ru,2008-04-01:highload/2008/arkhitektura-digg/</guid><category>APC</category><category>Digg</category><category>LAMP</category><category>Linux</category><category>Lucene</category><category>Memcached</category><category>MySQL</category><category>online</category><category>PHP</category><category>архитектура</category><category>архитектура Digg</category><category>интернет</category></item><item><title>Архитектура Wikimedia</title><link>https://www.insight-it.ru//highload/2008/arkhitektura-wikimedia/</link><description>&lt;p&gt;&lt;a href="https://www.insight-it.ru/goto/687b5b81/" rel="nofollow" target="_blank" title="http://wikimedia.org"&gt;Wikimedia&lt;/a&gt; является платформой для
&lt;a href="https://www.insight-it.ru/goto/35f4fea7/" rel="nofollow" target="_blank" title="http://wikipedia.org"&gt;Wikipedia&lt;/a&gt;, &lt;a href="https://www.insight-it.ru/goto/389e980c/" rel="nofollow" target="_blank" title="http://wiktionary.org"&gt;Wiktionary&lt;/a&gt; и
еще семи менее крупных wiki-проектов. Этот документ очень пригодится
новичкам, пытающимся довести свои проекты до масштабов гигантских
вебсайтов. Здесь можно найти множество интересных деталей и
инновационных идей, которые уже успели доказать свою работоспособность
на самых посещаемых сайтах всего Интернета.
&lt;!--more--&gt;&lt;/p&gt;
&lt;h3 id="istochniki-informatsii"&gt;Источники информации&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Перевод &lt;a href="https://www.insight-it.ru/goto/cd47c021/" rel="nofollow" target="_blank" title="http://highscalability.com/wikimedia-architecture"&gt;статьи&lt;/a&gt;.
Автор - &lt;a href="https://www.insight-it.ru/goto/f3f1b405/" rel="nofollow" target="_blank" title="http://highscalability.com/user/todd-hoff"&gt;Todd Hoff&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/c8e5f8d0/" rel="nofollow" target="_blank" title="http://www.nedworks.org/~mark/presentations/san/Wikimedia%20architecture.pdf"&gt;Архитектура Wikimedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/62eceab/" rel="nofollow" target="_blank" title="http://meta.wikimedia.org/wiki/Wikimedia_servers"&gt;Серверы Wikimedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/c56ad62f/" rel="nofollow" target="_blank" title="http://oracle2mysql.wordpress.com/2007/08/22/12/"&gt;scale-out vs scale-up&lt;/a&gt; из блога "Oracle to MySQL"&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="platforma"&gt;Платформа&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/tag/apache/"&gt;Apache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/linux/"&gt;Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/mysql/"&gt;MySQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/squid/"&gt;Squid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/lvs/"&gt;LVS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/lucene/"&gt;Lucene&lt;/a&gt; для поиска&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/memcached/"&gt;Memcached&lt;/a&gt; для распределенного кэширования объектов&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/lighttpd/"&gt;lighttpd&lt;/a&gt; для работы с изображениями&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="statitstika"&gt;Статитстика&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;8 миллионов статей распределены по сотням языковых подпроектов
    (английские, голландские, ...)&lt;/li&gt;
&lt;li&gt;В десятке самых высоконагруженных проектов по данным
    &lt;a href="https://www.insight-it.ru/goto/3b390e59/" rel="nofollow" target="_blank" title="http://alexa.com"&gt;Alexa&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Экспоненциальный рост: в терминах посетителей, трафика и серверов
    удвоение происходит каждые 4-6 месяцев&lt;/li&gt;
&lt;li&gt;30000 HTTP запросов в секунду в периоды пиковой нагрузки&lt;/li&gt;
&lt;li&gt;3 GBps трафик данных&lt;/li&gt;
&lt;li&gt;3 датацентра: Тампа, Амстердам, Сеул&lt;/li&gt;
&lt;li&gt;350 серверов, конфигурации варьируются от однопроцессорных Pentium 4
    с 512 MB оперативной памяти до двухпроцессорных Xeon Quad-Core с 16
    GB RAM.&lt;/li&gt;
&lt;li&gt;Управляется ~6 людьми&lt;/li&gt;
&lt;li&gt;Три кластера на трех разных континентах&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="arkhitektura"&gt;Архитектура&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Географическая балансировка нагрузки, основываясь на IP клиента,
    перенаправляет их на ближайший кластер. Происходит статическое
    отображение множества IP адресов на множество стран, а затем и на
    множество кластеров.&lt;/li&gt;
&lt;li&gt;Кэширование с помощью &lt;a href="/tag/squid/"&gt;Squid&lt;/a&gt; группируется по типу
    контента: текст для wiki отдельно от изображений и больших
    статических файлов.&lt;/li&gt;
&lt;li&gt;На данный момент функционирует 55 &lt;a href="/tag/squid/"&gt;Squid&lt;/a&gt; серверов, плюс
    еще 20 подготавливается к запуску.&lt;/li&gt;
&lt;li&gt;1000 HTTP запросов в секунду на каждый сервер, в периоды повышенной
    нагрузки эта цифра может достигать 2500.&lt;/li&gt;
&lt;li&gt;~ 100-250 MBps на сервер.&lt;/li&gt;
&lt;li&gt;~ 14000-32000 открытых соединений на каждом сервере.&lt;/li&gt;
&lt;li&gt;До 40 GB дискового кэша на каждом Squid сервере.&lt;/li&gt;
&lt;li&gt;До 4 дисков в каждом сервере (1U серверы).&lt;/li&gt;
&lt;li&gt;8 GB оперативной памяти, половину использует &lt;a href="/tag/squid/"&gt;Squid&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/e669c01a/" rel="nofollow" target="_blank" title="http://www.powerdns.com"&gt;PowerDNS&lt;/a&gt; предоставляет геораспределение.&lt;/li&gt;
&lt;li&gt;В основном и региональных датацентрах текстовые и медиа кластеры
    построены на &lt;a href="/tag/lvs/"&gt;LVS&lt;/a&gt;,
    &lt;abbr title="Common Address Redundancy Protocol"&gt;CARP&lt;/abbr&gt;
&lt;a href="/tag/squid/"&gt;Squid&lt;/a&gt;, кэш &lt;a href="/tag/squid/"&gt;Squid&lt;/a&gt;. В основном датацентре
    также находится хранилище медиа-данных.&lt;/li&gt;
&lt;li&gt;Для того, чтобы обеспечить предоставление только последних версий
    страниц, всем Squid-серверам отправляются инвалидационные запросы.&lt;/li&gt;
&lt;li&gt;Централизованно управляемая и синхронизированная установка
    программного обеспечения для сотен серверов.&lt;/li&gt;
&lt;li&gt;MediaWiki отлично масштабируется с несколькими процессорами, так что
    закупаются двухпроцессорный четырех ядерные серверы (8 ядер на
    сервер).&lt;/li&gt;
&lt;li&gt;Одно и то же оборудование выполняет как задачи внешнего хранения
    данных, так и кэширования &lt;a href="/tag/memcached/"&gt;Memcached&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/memcached/"&gt;Memcached&lt;/a&gt; используется для кэширования метаданных
    изображений, данных парсера, различий между ревизиями,
    пользователей, сессий. Метаданные, такие как история ревизий,
    отношений статей (ссылки, категории и так далее), учетные записи
    пользователей хранятся в основных базах данных&lt;/li&gt;
&lt;li&gt;Сам текст находится во внешних хранилищах данных.&lt;/li&gt;
&lt;li&gt;Статические (загруженные пользователями) файлы, например
    изображения, хранятся отдельно на сервере изображений, а метаданные
    (размер, тип и так далее) кэшируются в основной базе данных и
    объектном кэше.&lt;/li&gt;
&lt;li&gt;Отдельная база данных для каждой вики (не отдельный сервер!).&lt;/li&gt;
&lt;li&gt;Один master и много реплицированных slave серверов.&lt;/li&gt;
&lt;li&gt;Операции чтения равномерно распределяются по slave серверам,
    операции записи направляются на master.&lt;/li&gt;
&lt;li&gt;Иногда master используется и для операция чтения, когда репликация
    на slave еще не завершена.&lt;/li&gt;
&lt;li&gt;Внешнее хранение данных:&lt;ul&gt;
&lt;li&gt;Текст статей хранится на отдельных кластерах, которые представляют
собой простой средство хранения данных с возможностью только
дописывания новых данных. Такой подход позволяет сохранить
дорогостоящее место в высоконагруженных основных базах данных от
редко используемой информации.&lt;/li&gt;
&lt;li&gt;Благодаря этому появляются дополнительные неиспользованные ресурсы
на серверах приложений (порой 250-500 GB на сервер).&lt;/li&gt;
&lt;li&gt;На данной момент используются реплицируемые кластеры из 3
&lt;a href="/tag/mysql/"&gt;MySQL&lt;/a&gt; серверов, но в будущем это может измениться, так
как требуется более удобное управление ими.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="podvodim-itogi"&gt;Подводим итоги&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Сфокусируйтесь на архитектуре, а не на операциях или чем-то другом.&lt;/li&gt;
&lt;li&gt;Иногда кэширование обходится дороже, чем повторные вычисление или
    поиск данных в исходном источнике.&lt;/li&gt;
&lt;li&gt;Старайтесь избегать сложных алгоритмов, запросов к базе данных и
    тому подобного.&lt;/li&gt;
&lt;li&gt;Кэшируйте каждый результат, который дорого вам обошелся и является
    относительно локальным.&lt;/li&gt;
&lt;li&gt;Сфокусируйтесь на "горячих точках" в коде.&lt;/li&gt;
&lt;li&gt;Масштабируйтесь разделением:&lt;ul&gt;
&lt;li&gt;операций чтения и записи (master/slave);&lt;/li&gt;
&lt;li&gt;сложных операций и более частых и простых (группы запросов);&lt;/li&gt;
&lt;li&gt;больших, популярных вики и более мелких.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Улучшайте кэширование: временная и пространственная локализация
    данных, а также уменьшение набора данных на каждом сервере.&lt;/li&gt;
&lt;li&gt;Выполняйте компрессию текстовых данных, храните только изменения в
    статьях.&lt;/li&gt;
&lt;li&gt;Казалось бы простые вызовы библиотечных функций порой на практике
    могут занимать слишком много времени.&lt;/li&gt;
&lt;li&gt;Скорость поиска данных на диске ограничена, так что чем больше
    дисков - тем лучше!&lt;/li&gt;
&lt;li&gt;Масштабирование с использованием обычного оборудование не означает
    использование самых дешевых вещей, которые удастся найти. Серверы
    баз данных Wikipedia сегодня представляют собой 16GB RAM, двух- или
    четырех-ядерные серверы с 6 15000 rpm SCSI дисками, организованными
    в RAID 0. Возможно они бы и использовали более дешевые системы, но
    16 GB как раз хватает для размещения основного объема данных, а
    остальное берут на себя жесткие диски, это вполне соответствует
    потребностям системы, которую они построили. Примерно по таким же
    причинам их веб-сервера имеют 8 ядер, так как это позволяет достичь
    неплохой производительности &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt; при достаточно простой
    организации балансировки нагрузки.&lt;/li&gt;
&lt;li&gt;Для масштабирования требуется выполнение массы работы, но если
    заранее этого не предусмотреть - понадобится сделать еще больше.
    MediaWiki изначально была написана для одного master сервера баз
    данных. Затем добавилась поддержка slave. Затем добавилось
    распределение по языкам и проектам. Дизайн системы с тех пор
    прекрасно выдерживает все нагрузки, но без очистки от новых узких
    мест системы не обошлось.&lt;/li&gt;
&lt;li&gt;Каждый, кто хочет разработать свою базу данных таким образом, чтобы
    она позволила недорого масштабироваться с уровня одного сервера до
    уровня десятки лучших сайтов Интернета, должен начать с обработки
    слегка устаревших данных на реплицированных slave серверах, при этом
    не забывать балансировать нагрузку операций чтения между slave
    серверами. Если это возможно - блоки данных (группы пользователей,
    учетных записей, или чего угодно) должны размещаться каждый на
    разных серверах. Можно делать это с самого начала используя
    виртуализацию, чтобы удостовериться в работоспособности архитектуры,
    когда вы еще "маленькие". Это &lt;strong&gt;намного&lt;/strong&gt; проще, чем когда вы
    делаете то же самое, но под ежемесячно удваивающейся нагрузкой.&lt;/li&gt;
&lt;/ul&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Fri, 28 Mar 2008 15:32:00 +0300</pubDate><guid>tag:www.insight-it.ru,2008-03-28:highload/2008/arkhitektura-wikimedia/</guid><category>Apache</category><category>lighttpd</category><category>Linux</category><category>Lucene</category><category>LVS</category><category>Memcached</category><category>MySQL</category><category>PHP</category><category>Squid</category><category>архитектура</category><category>архитектура Wikimedia</category><category>геораспределение</category><category>Масштабируемость</category></item><item><title>Модификация алгоритма хэширования</title><link>https://www.insight-it.ru//php/2008/modifikaciya-algoritma-khehshirovaniya/</link><description>&lt;p&gt;Если Вы уже успели прочитать &lt;a href="https://www.insight-it.ru/security/2008/obratnogo-puti-net/"&gt;одну из моих предыдущих записей о
хэшировании&lt;/a&gt;, то Вы уже
имеете базовое представление о теме сегодняшнего разговора.
Одним из возможных способов применения хэшей является хранение
аутентификационных данных пользователей интернет-приложения, об
особенностях реализации формирования и проверки хэшей при регистрации и
авторизации пользователей средствами &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt; я и хотел бы с Вами
поговорить.
&lt;!--more--&gt;
Сомневаюсь, что Вы услышите что-то новое, если я скажу, что в
&lt;a href="/tag/php/"&gt;PHP&lt;/a&gt; даже в "стандартной комплектации" реализована масса
алгоритмов хэширования, начиная с широкораспространенных &lt;strong&gt;md5();&lt;/strong&gt; и
&lt;strong&gt;sha1();&lt;/strong&gt; и заканчивая модулями &lt;strong&gt;hash&lt;/strong&gt; и &lt;strong&gt;mhash&lt;/strong&gt;, в которых
реализована еще целая масса алгоритмов. Все они давно уже
стандартизованы и доступны для изучения всем желающим получить о них
какую-либо информацию.&lt;/p&gt;
&lt;p&gt;Допустим мы храним пароли пользователей в виде какого-то стандартного
хэша, для примера - &lt;strong&gt;md5&lt;/strong&gt;, в базе данных. Все было отлично, но в один
прекрасный момент нашелся подлый злоумышленник, который неким хитрым
способом получил доступ к базе данных логинов и паролей. Перед ним стоит
цель - узнать изначальный пароль у максимального числа пользователей.
Посмотрим на ситуацию с его стороны:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Первым делом он бы попытался определить, какой именно хэш перед ним
    находится - чаще всего это делается либо просто взглянув на длину
    хэша, либо если приложение широко распространено (популярная CMS
    скажем) - покопавшись в ее исходниках, еще есть вариант найти свой
    собственный аккаунт - и зная пароль попробовать на нем разные
    алгоритмы, способов можно придумать множество - все ограничивается
    лишь воображением. Узнав ответ на свой вопрос ему лишь останется
    набрать в &lt;a href="/tag/google/"&gt;Google&lt;/a&gt; фразу вроде &lt;em&gt;"md5 decrypt"&lt;/em&gt;, а
    дальше уже дело техники.&lt;/li&gt;
&lt;li&gt;Еще один вариант решения задачи - взглянуть на список хэшей на
    предмет наличия совпадений. С очень высокой степенью вероятности за
    значительной группой одинаковых хэшей будет скрываться какой-либо
    банальный пароль вроде &lt;em&gt;123456&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Задача же разработчика приложения максимально обезопасить систему от
подобных ситуаций. Конечно же можно просто стараться минимизировать
возможности реализации методов получения информации из базы данных, но
предугадать все варианты невозможно: в любом из используемых компонентов
системы может оказаться уязвимость в коде, на которую наверняка найдется
умник, который напишет &lt;em&gt;exploit&lt;/em&gt;, а значит полностью исключить такую
вероятность не получится, в лучшем случае выйдет просто ее
минимизировать.&lt;/p&gt;
&lt;p&gt;Именно по этим причинам и стоит задуматься об усложнении задачи
злоумышленника в случае возникновения описанной выше ситуации. Для
исключения возможности просто расшифровывания хэшей по словарю (то есть
первый случай, когда определяется тип хэша и соответствующий ему словарь
&lt;em&gt;хэш =&amp;gt; исходное значение&lt;/em&gt;) достаточно исключить возможность
идентификации алгоритма хэширования или наличия к нему заранее
подготовленного словаря. Для этого достаточно лишь сделать шаг в сторону
от стандартного алгоритма любым пришедшим в голову способом, например:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;хранить хэш не от самого пароля, а от &lt;em&gt;пароль + какая-либо
    фиксированная строка&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;поменять местами группы символов в получившемся стандартном хэше&lt;/li&gt;
&lt;li&gt;сделать сдвиг символов в стандартном хэше (или можно даже не сами
    символы двигать, а с помощью битовых операций их значения)&lt;/li&gt;
&lt;li&gt;комбинировать два стандартных алгоритма хэширования, или алгоритм
    хэширования с алгоритмом обратимого шифрования, которых доступно
    также множество&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Список этот можно было бы продолжать достаточно долго, это было лишь
первое, что пришло мне в голову. Но ни один из приведенных способов не
избавит от возможности второго варианта раскрывания исходного пароля.
Основывается он на однозначности стандартных алгоритмов - одним и тем же
исходным данным соответствует один и тот же хэш. Для отказа от этого
свойства стандартных алгоритмов придется выполнить более сложную
модификацию используемой для генерации хэша функции (которая конечно же
тоже поможет и для борьбы с первым вариантом). Сразу приведу пример
&lt;a href="/tag/kod/"&gt;кода&lt;/a&gt;, реализующего этот механизм, а дальше попытаюсь его
объяснить:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$salt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$salt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$salt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;randomString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nv"&gt;$hash&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$salt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$salt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Как не трудно заметить - используется самодельная функция
&lt;strong&gt;randomString();&lt;/strong&gt;, которая возвращает случайную строку, состоящую из
указанного количества шестнадцатеричных цифр (надеюсь Вы в состоянии
написать ее своими силами). Именно этот момент и гарантирует элемент
случайности при каждой новой генерации хэша. В том месте, где я прочитал
про этот механизм (ссылку, к сожалению, привести не могу - в bookmark'ах
не нашел), этот случайный компонент назывался словом &lt;strong&gt;salt&lt;/strong&gt;, смысл его
заключается в том, что он приписывается ко входным данным, передаваемым
стандартной функции хэширования, а затем им же подменяется какая-либо
фиксированная часть полученного хэша.
Наверняка у Вас возник вопрос: а как же потом понять, что пользователь
ввел верные данные, ведь для тех же исходных данных получится другой хэш
и возможности их сравнить не будет? Ответ достаточно прост, его можно
было увидеть даже в коде: при повторной инициализации хэша из &lt;a href="/tag/bd/"&gt;базы
данных&lt;/a&gt; достается заранее известная часть хранящегося там хэша,
соответствующего конкретному пользователю - тот самый &lt;strong&gt;salt&lt;/strong&gt;, и
передается нашей функции. В этом случае в механизме будет использоваться
именно он, а не новое случайное значение, и, как следствие, в случае
правильности введенных данных на выходе получатся совпадающие хэши. Вот
такой вот простенький, но иногда достаточно полезный трюк.&lt;/p&gt;
&lt;p&gt;Если Вам понравился этот пост - возможно Вам придутся по душе и
&lt;a href="https://www.insight-it.ru/dzhentelmenskij-nabor-php-programmista/"&gt;остальные записи из этой серии статей&lt;/a&gt;, а не пропустить
публикацию новых записей Вам может помочь &lt;a href="/feed/"&gt;RSS feed&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Fri, 15 Feb 2008 13:17:00 +0300</pubDate><guid>tag:www.insight-it.ru,2008-02-15:php/2008/modifikaciya-algoritma-khehshirovaniya/</guid><category>hash</category><category>md5</category><category>PHP</category><category>sha1</category><category>код</category><category>кодинг</category><category>Программирование</category><category>технология</category><category>хранение данных</category><category>хэш</category><category>хэширование</category><category>шифрование</category></item><item><title>Архитектура Flickr</title><link>https://www.insight-it.ru//highload/2008/arkhitektura-flickr/</link><description>&lt;p&gt;&lt;a href="https://www.insight-it.ru/goto/f50a76e1/" rel="nofollow" target="_blank" title="http://www.flickr.com"&gt;Flickr&lt;/a&gt; является мировым лидером среди сайтов
размещения фотографий. Перед Flickr стоит впечатляющая задача, они
должны контролировать обширное море ежесекундно обновляющегося контента,
непрерывно пополняющиеся легионы пользователей, постоянный поток новых
предоставляемых пользователям возможностей, а делается все это при
постоянной поддержке отличной производительности. Как же они это
делают?
&lt;!--more--&gt;&lt;/p&gt;
&lt;h3 id="istochniki-informatsii"&gt;Источники информации&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Как и предыдущий пост &lt;a href="https://www.insight-it.ru/highload/2008/arkhitektura-google/"&gt;"Архитектура Google"&lt;/a&gt;, этот тоже является
переводом &lt;a href="https://www.insight-it.ru/goto/e7a0ee0d/" rel="nofollow" target="_blank" title="http://highscalability.com/flickr-architecture"&gt;статьи&lt;/a&gt; от
&lt;a href="https://www.insight-it.ru/goto/f3f1b405/" rel="nofollow" target="_blank" title="http://highscalability.com/user/todd-hoff"&gt;Todd'а Hoff'а&lt;/a&gt;. Возможно
читателям &lt;a href="/tag/google/"&gt;Google&lt;/a&gt; был более интересен, но подход Flickr к
масштабируемости тоже более чем заслуживает внимания. Далее привожу
источники информации из оригинальной статьи:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/88014756/" rel="nofollow" target="_blank" title="http://www.niallkennedy.com/blog/uploads/flickr_php.pdf"&gt;Flickr и PHP&lt;/a&gt;
    (ранний документ)&lt;/li&gt;
&lt;li&gt;Планирование нагрузок на LAMP&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/6df1dabf/" rel="nofollow" target="_blank" title="http://www.bytebot.net/blog/archives/2007/04/25/federation-at-flickr-a-tour-of-the-flickr-architecture"&gt;Федерация Flickr: Тур по архитектуре Flickr&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/9e0a13a1/" rel="nofollow" target="_blank" title="http://highscalability.com/book-building-scalable-web-sites"&gt;Построение масштабируемых веб-сайтов&lt;/a&gt;
    от Call Handerson'а из Flickr&lt;/li&gt;
&lt;li&gt;История войн баз данных #3: Tim O'Reilly о Flickr&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.insight-it.ru/goto/d881b0d9/" rel="nofollow" target="_blank" title="http://www.iamcal.com/talks/"&gt;Cal Henderson's Talks&lt;/a&gt; - много
    полезных презентаций&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="platforma"&gt;Платформа&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/sql/"&gt;MySQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Сегментирование &lt;em&gt;(прим.: разбиение системы на части, обслуживающие
    каждая свою группу пользователей; называть можно было по-разному, но
    давайте остановимся на этом варианте перевода слова "Shards")&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/memcached/"&gt;Memcached&lt;/a&gt; для кэширования&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/squid/"&gt;Squid&lt;/a&gt; в качестве обратной-прокси для html и
    изображений&lt;/li&gt;
&lt;li&gt;&lt;a href="/linux"&gt;Linux&lt;/a&gt; (&lt;a href="/tag/redhat/"&gt;RedHat&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/smarty/"&gt;Smarty&lt;/a&gt; в роли шаблонизатора&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/perl/"&gt;Perl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;PEAR для парсинга e-mail и XML&lt;/li&gt;
&lt;li&gt;ImageMagick для обработки изображений&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/java/"&gt;Java&lt;/a&gt; для узлового сервиса&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/apache/"&gt;Apache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/systemimager/"&gt;SystemImager&lt;/a&gt; для развертывания систем&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/ganglia/"&gt;Ganglia&lt;/a&gt; для мониторинга распределенных систем&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/subcon/"&gt;Subcon&lt;/a&gt; хранит важные системные конфигурационные файлы
    в SVN-репозитории для легкого развертывания на машины в кластере.&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/cvsup/"&gt;Cvsup&lt;/a&gt; для распространения и обновления коллекций
    файлов по сети&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="statistika"&gt;Статистика&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Более четырех миллиардов запросов в день&lt;/li&gt;
&lt;li&gt;Примерно 35 миллионов фотографий в кэше &lt;a href="/tag/squid/"&gt;Squid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Около двух миллионов фотографий в оперативной памяти
    &lt;a href="/tag/squid/"&gt;Squid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Всего приблизительно 470 миллионов изображений, каждое представлено
    в 4 или 5 размерах&lt;/li&gt;
&lt;li&gt;38 тысяч запросов к &lt;a href="/tag/memcached/"&gt;memcached&lt;/a&gt; (12 миллионов
    объектов)&lt;/li&gt;
&lt;li&gt;2 петабайта дискового пространства&lt;/li&gt;
&lt;li&gt;Более 400000 фотографий добавляются ежедневно&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="arkhitektura"&gt;Архитектура&lt;/h3&gt;
&lt;p&gt;Симпатичное изображение архитектуры Flickr можно увидеть на &lt;a href="https://www.insight-it.ru/goto/d30e097b/" rel="nofollow" target="_blank" title="http://www.slideshare.net/techdude/scalable-web-architectures-common-patterns-and-approaches/138"&gt;этом слайде&lt;/a&gt;.
Краткое ее описание выглядит следующим образом:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Два ServerIron&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/squid/"&gt;Squid&lt;/a&gt; кэши&lt;/li&gt;
&lt;li&gt;Системы хранения NetApp&lt;/li&gt;
&lt;li&gt;Серверы &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt; приложений&lt;/li&gt;
&lt;li&gt;Менеджер хранения данных&lt;/li&gt;
&lt;li&gt;Master-master сегменты&lt;/li&gt;
&lt;li&gt;Центральная база данных, структурированная по принципу Dual
Tree&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/memcached/"&gt;Memcached&lt;/a&gt; кластер&lt;/li&gt;
&lt;li&gt;Поисковая система&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Хранение данных&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Структура Dual Tree является индивидуальным набором модификаций для
&lt;a href="/tag/sql/"&gt;MySQL&lt;/a&gt;, позволяющим масштабировать систему путем добавления
новых мастер-серверов без использования кольцевой архитектуры. Эта
система позволяет экономить на масштабировании, так как варианты
мастер-мастер требовали бы удвоенных вложений в оборудование.&lt;/li&gt;
&lt;li&gt;Центральная база данных включает в себя таблицу пользователей,
состоящую из основных ключей пользователей (несколько уникальных
идентификационных номеров) и указатель на сегмент, на котором может быть
найдена остальная информация о конкретном пользователе.&lt;/li&gt;
&lt;li&gt;Использование выделенных серверов для статического контента&lt;/li&gt;
&lt;li&gt;Все, за исключением фотографий, хранится в базе данных&lt;/li&gt;
&lt;li&gt;Отсутствие состояний заключается в том, что в случае необходимости
    они имеют возможность передать пользователей от сервера к серверу,
    что стало намного проще для них после создания своего API&lt;/li&gt;
&lt;li&gt;В основе масштабируемости лежит репликация, но этот факт помогает
    лишь при обработке операций чтения&lt;/li&gt;
&lt;li&gt;Для поиска по определенной части базы данных создается отдельная
    копия этого фрагмента&lt;/li&gt;
&lt;li&gt;Использования горизонтального масштабирования для того чтобы можно
    было проще добавлять новые машины в систему&lt;/li&gt;
&lt;li&gt;Обработка изображений, полученных от пользователей по электронной
    почте, происходит с помощью &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Раньше система страдала от задержек связанных с организацией по
    принципу мастер-слуга. При слишком большой нагрузке они имели одну
    точку, которая теоретически могла дать сбой.&lt;/li&gt;
&lt;li&gt;Им было необходимо иметь возможность проводить технические работы во
    время непрерывной работы сайта, не прекращая его функционирование.&lt;/li&gt;
&lt;li&gt;Были проведены отличные работы по планированию распределения
    дискового пространства, более подробную информацию можно найти по
    ссылкам в разделе "Источники информации".&lt;/li&gt;
&lt;li&gt;Для обеспечения возможности масштабирования в будущем, они пошли по
    федеративному пути развития:&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Сегменты системы:&lt;/em&gt; Мои данные хранятся на моем сегменте, но
запись о Вашем комментарии хранится на Вашем сегменте.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Глобальное кольцо:&lt;/em&gt; Принцип работы схож с DNS, Вам необходимо
знать куда Вы хотите пойти и кто контролирует то место, куда Вы
собираетесь пойти.&lt;/li&gt;
&lt;li&gt;Логика на &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt; устанавливает соединение с сегментом и
поддерживает целостность данных (10 строк кода с комментариями!)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Сегменты:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Срез основной базы данных&lt;/li&gt;
&lt;li&gt;Активная репликация по принципу мастер-мастер: имеет несколько
недостатков в &lt;a href="/tag/sql/"&gt;MySQL&lt;/a&gt; 4.1. Автоматическое
инкрементирование идентификационных номеров используется для
поддержания системы в режиме одновременной активности обоих серверов
в паре&lt;/li&gt;
&lt;li&gt;Привязывание новых учетных записей к сегментам системы происходит
случайным образом&lt;/li&gt;
&lt;li&gt;Миграция пользователей проводится время от времени для того, чтобы
избавиться от проблем, связанных с излишне активными пользователями.
Необходима сбалансированность в этом процессе, особенно в случаях с
большим количеством фотографий&amp;hellip; 192 тысячи фотографий, 700 тысяч
тэгов, может занять несколько минут. Миграция выполняется вручную.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Нажатие на &lt;strong&gt;Favorite&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Получается информация об учетной записи владельца из кэша для
того, чтобы узнать к какому сегменту он привязан (допустим на
shard-5)&lt;/li&gt;
&lt;li&gt;Получается информация о моей учетной записи из кэша, более
конкретно - мой сегмент (например shard-13)&lt;/li&gt;
&lt;li&gt;Начинается "распределенная транзакция" для определения ответов на
вопросы: Кто добавил эту фотографию в избранное? Как изменился
список избранных фотографий?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Подобные вопросы могут задаваться любому сегменту, информация на них
    абсолютно избыточна.&lt;/li&gt;
&lt;li&gt;Для избавления от задержек, связанных с репликацией...&lt;ul&gt;
&lt;li&gt;при каждой загрузке страницы, пользователю предоставляется список
серверов&lt;/li&gt;
&lt;li&gt;если сервер не в состоянии ответить на запрос, запрос переходит к
следующему серверу в списке; если список кончился - выводится
сообщение об ошибке. При этом не используются постоянные соединения,
каждый раз создаются и разрываются новые соединения.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Запросы на чтение и запись от каждого пользователя ограничиваются
    рамками одного сегмента. Задержки репликации исчезают из поля зрения
    пользователей.&lt;/li&gt;
&lt;li&gt;Каждый сервер в рамках одного сегмента в обычном состоянии нагружен
    ровно на половину. Выключите половину серверов в каждом сегменте и
    система продолжит функционировать без изменений. Это значит, что
    один сервер внутри сегмента может взять на себя всю нагрузку
    второго, в то время как второй сервер может по каким либо причинам
    быть отключен от системы, например для проведения технических работ.
    Обновление оборудования производится очень просто: отключается
    половина сегмента, она же обновляется, подключается обратно, процесс
    повторяется для оставшейся половины.&lt;/li&gt;
&lt;li&gt;Периоды пиковой нагрузки также нарушают правило 50% нагрузки. В
    такие моменты система получает 6-7 тысяч запросов в секунду, в то
    время как на данный момент система может работать на
    пятидесятипроцентном уровне нагрузки только при четырех тысячах
    запросов в секунду.&lt;/li&gt;
&lt;li&gt;В среднем при загрузке одной страницы выполняется 27-35
    SQL-запросов. Списки избранных фотографий обрабатываются в реальном
    времени, ровно как и доступ через API к базе данных. Все требования
    к нагрузке в реальном времени выполняются без каких-либо
    недостатков.&lt;/li&gt;
&lt;li&gt;Более 36 тысяч запросов в секунду может выполняться не выходя за
    рамки возможностей системы, даже при резком росте трафика.&lt;/li&gt;
&lt;li&gt;Каждый сегмент содержит данные о более чем 400 тысячах
    пользователей.&lt;/li&gt;
&lt;li&gt;Многие данные хранятся в двух местах одновременно. Например,
    комментарий является частью между комментатором и автором
    комментируемого контента. Где его хранить? Как насчет обоих мест?
    Транзакции используются для предотвращения рассинхронизации данных:
    открывается первая транзакция, выполняется запись, открывается
    вторая транзакция, выполняется запись, подтверждается первая
    транзакция если все нормально, после чего вторая подтверждается
    только в случае если первая прошла успешно.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Поиск&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Используется два варианта поиска: поиск в рамках сегмента,
поддерживающий до 35 тысяч запросов в секунду, а также проприетарный
веб-поиск от Yahoo!&lt;/li&gt;
&lt;li&gt;В 90% случаев используется система от Yahoo!, за исключением
поиска по тэгу фотографий одного пользователя и массовых изменений
тэгов.&lt;/li&gt;
&lt;li&gt;Эту систему стоит рассматривать как аналог Lucene.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Оборудование&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;EMT64 под управлением RHEL 4 с 16 Gb оперативной памяти.&lt;/li&gt;
&lt;li&gt;6 жестких дисков с 15000rpm, объединены в RAID-10.&lt;/li&gt;
&lt;li&gt;Размер для пользовательских метаданных достигает 12 терабайт (это
не включает фотографии, для них цифры существенно больше).&lt;/li&gt;
&lt;li&gt;Используются 2U корпуса.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Резервное копирование данных&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ibbackup выполняется регулярно посредством cron daemon'а, на
каждом сегменте настроен на разное время.&lt;/li&gt;
&lt;li&gt;Каждую ночь делается снимок со всего кластера баз данных.&lt;/li&gt;
&lt;li&gt;Запись или удаление нескольких больших файлов с резервными копиями
одновременно на реплицирующую систему хранения может сильно
сократить производительность системы вцелом на последующие несколько
часов из-за процесса репликации. Выполнение этого на активно
работающей системе хранения фотографий было бы не самой лучшей
идеей.&lt;/li&gt;
&lt;li&gt;Содержание нескольких резервных копий всех Ваших данных требует
существенных материальных затрат, но оно того стоит. Особенно это
актуально для тех ситуаций, когда Вы понимаете, что что-то пошло не
так только спустя несколько дней после того как это случилось, в
таких случаях неплохо иметь, например, резервные копии 1, 3, 10 и
30-дневной давности.&lt;/li&gt;
&lt;li&gt;Фотографии хранятся в системе хранения данных. После загрузки
изображения система выдает различные его размеры, на чем ее работа
заканчивается. Метаданные и ссылки на файловые системы, где
расположены фотографии, хранятся в базе данных.&lt;/li&gt;
&lt;li&gt;Агрегация данных проходит очень быстро, так как она ограничена
пределами сегмента.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max_connections = 400&lt;/code&gt; соединений на каждый сегмент, неплохой запас.
Значение для кэша потоков установлено равным 45, так как не бывает
ситуаций когда более 45 пользователей одновременно выполняют
какие-либо действия с одним конкретным сегментом.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Тэги&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Тэги плохо вписываются в традиционную нормализованную схему
реляционной базы данных. Денормализация или активное кэширование -
единственные способы сгенерировать облако меток для сотен миллионов
тэгов в течении миллисекунд.&lt;/li&gt;
&lt;li&gt;Некоторые данные обрабатываются отдельными вычислительными
кластерами, которые сохраняют результаты своей работы в MySQL, так
как иначе вычисление сложных отношений заняло бы все процессорное
время основных серверов баз данных.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Направления для развития&lt;/h4&gt;
&lt;p&gt;Ускорение работы с помощью создания
организационного плана для непрерывной работы всей системы на уровне
нескольких датацентров, таким образом чтобы все датацентры имели
возможность получать запросы на общий уровень данных (как сами БД,
так и memcache и прочее) все вместе одновременно. Если все части
системы постоянно активны - время простоя оборудования будет сведено
к минимуму.&lt;/p&gt;
&lt;h3 id="podvodim-itogi"&gt;Подводим итоги&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Старайтесь думать о своем приложении как о чем-то большем, чем просто
    веб-приложении, тогда у Вас возможно появятся поддержка различных
    API, RSS и Atom ленты и многие другие возможности.&lt;/li&gt;
&lt;li&gt;Отсутствие состояний системы позволяет более легко выполнять
    модернизации не моргнув и глазом.&lt;/li&gt;
&lt;li&gt;Реструктуризация базы данных - не самое лучшее занятие.&lt;/li&gt;
&lt;li&gt;Планирование нагрузок должно проводиться уже на ранних этапах
    развития проекта&lt;/li&gt;
&lt;li&gt;Начинайте медленно. Не покупайте сразу много оборудования просто
    из-за того, что Вы рады/боитесь, что ваш сайт взорвется.&lt;/li&gt;
&lt;li&gt;Измеряйте реально, планирование нагрузок должно базироваться на
    реальных вещах, а не абстрактных.&lt;/li&gt;
&lt;li&gt;Внедряйте ведение логов и индивидуальные измерения для оценки
    реальных показателей на основе серверной статистики, статистика
    использования не менее важна чем серверная.&lt;/li&gt;
&lt;li&gt;Кэширование и оперативная память может стать ответом на все вопросы.&lt;/li&gt;
&lt;li&gt;Создавайте четкие уровни абстракции между работой базы данных,
    бизнес-логикой, логикой страниц, разметкой страниц и презентационным
    уровнем. Это позволяет ускорить циклы итеративной разработки.&lt;/li&gt;
&lt;li&gt;Разделение приложения на уровни позволяет каждому заниматься своим
    делом: разработчики могут строить логику страниц, в то время как
    дизайнеры работают с удобством работы для пользователей.&lt;/li&gt;
&lt;li&gt;Делайте релизы как можно чаще, пускай даже это будет происходить
    каждые полчаса.&lt;/li&gt;
&lt;li&gt;Забудьте о всех небольших эффективных вещах, предварительная
    оптимизация является корнем всего зла в примерно 97% всех случаев.&lt;/li&gt;
&lt;li&gt;Тестируйте в работе. Постройте архитектурные механизмы (флаги
    конфигурации, балансировку нагрузки, и так далее), которые позволят
    Вам разворачивать новое оборудование в (и из) работу.&lt;/li&gt;
&lt;li&gt;Забудьте об искусственных тестах, они годятся только для получения
    общего представления о нагрузках, но не для планирования.
    Искуственные тесты дают искусственные результаты, для настоящих
    тестов все же стоит пользоваться реальным временем выполнения задач.&lt;/li&gt;
&lt;li&gt;Найдите максимальное значения для всех показателей:&lt;ul&gt;
&lt;li&gt;Какой максимум чего-то, что может выполнять каждый сервер?&lt;/li&gt;
&lt;li&gt;Как близко параметр находится к максимуму и каковы тенденции?&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/sql/"&gt;MySQL&lt;/a&gt; (дисковый ввод/вывод?)&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/squid/"&gt;Squid&lt;/a&gt; (дисковый ввод/вывод? или процессорное время?)&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/memcached/"&gt;Memcached&lt;/a&gt; (процессорное время? или пропускная способность?)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Старайтесь учесть особенности использования Вашего приложения.&lt;ul&gt;
&lt;li&gt;Возможен ли резкий рост нагрузки, связанный с каким-либо событием?
Например: какое-либо бедствие, или может быть новость?&lt;/li&gt;
&lt;li&gt;Flickr получает на 20-40% больше новых фотографий в первый рабочий
день нового года, чем в любой пик в предыдущем году.&lt;/li&gt;
&lt;li&gt;По воскресеньям нагрузка в среднем на 40-50% выше, чем в любой
другой день недели.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Учтите возможность экспоненциального роста. Больше пользователей
    означает больше контента, больше контента означает больше
    соединений, больше соединений означает более активное использование.&lt;/li&gt;
&lt;li&gt;Планируйте возможные варианты управления работой системы в периоды
    пиковых нагрузок.&lt;/li&gt;
&lt;/ul&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Fri, 08 Feb 2008 22:41:00 +0300</pubDate><guid>tag:www.insight-it.ru,2008-02-08:highload/2008/arkhitektura-flickr/</guid><category>Apache</category><category>Cvsup</category><category>flickr</category><category>Ganglia</category><category>Java</category><category>Linux</category><category>Memcached</category><category>MySQL</category><category>online</category><category>Perl</category><category>PHP</category><category>RedHat</category><category>shard</category><category>Smarty</category><category>Squid</category><category>Subcon</category><category>SystemImager</category><category>архитектура</category><category>архитектура Flickr</category><category>интернет</category><category>кластер</category><category>Масштабируемость</category><category>сервер</category></item><item><title>На пути к идеалу</title><link>https://www.insight-it.ru//php/2008/na-puti-k-idealu/</link><description>&lt;blockquote&gt;
&lt;p&gt;...или 15 привычек, которые помогут ускорить PHP-приложение&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Практически каждый программист стремится в своих приложениях не только
максимально точно реализовать требуемый функционал, но и сделать это как
можно более эффективным методом. Для этого конечно же необходимо
проектирование, подходящий выбор используемых технологий, возможно
некоторый опыт в предметной области, этот список можно продолжать
достаточно долго, но я позволю себе этого не делать, так как речь
сегодня пойдет не об этом. Вместо этого хочу обратить Ваше внимание на
более простые и "приземленные" методы &lt;a href="/tag/optimizaciya/"&gt;оптимизации&lt;/a&gt;
PHP-кода, которые может быть и не так эффективны по сравнению с
указанными выше, но зато не требуют каких-либо усилий со стороны кодера
и/или программиста, достаточно лишь воспринимать их как "не вредные"
привычки.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Прочитав достаточно солидный объем разного рода документации по
&lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;, я часто натыкался на статьи и тексты, так или иначе
связанные с &lt;a href="/tag/proizvoditelnost/"&gt;производительностью&lt;/a&gt; PHP-скриптов.
Порой в такого рода источниках информации удавалось найти достаточно
интересные и неочевидные факты об этом языке программирования, которые
не смотря на свою простоту могли дать вполне заметный прирост к
&lt;a href="/tag/proizvoditelnost/"&gt;производительности&lt;/a&gt; итогового приложения. Я
почему-то очень серьезно стал относиться к производительности написанных
мной скриптов, и довольно часто стал испытывать на практике спорные
моменты в реализации, о которых узнавал из Сети или каких-либо
других источников, с помощью самописных или
&lt;a href="/tag/opensource/"&gt;opensource&lt;/a&gt; benchmark'ов, хотя порой и просто внедряя
в реальные приложения. Как ни странно, в большинстве случаев практика
подтверждала теорию, и я стал постоянно пользоваться этими простыми
правилами, о которых я и хочу Вам рассказать.&lt;/p&gt;
&lt;h4&gt;Повышения значения индекса с помощью ++\$i;&lt;/h4&gt;
&lt;p&gt;Этот факт был наверное одним из самых удивительных для меня, когда я
впервые о нем услышал, но действительно операция &lt;strong&gt;++&lt;span style="color: blue"&gt;\$i&lt;/span&gt;&lt;/strong&gt;; выполняется несколько быстрее, чем
&lt;strong&gt;&lt;span style="color: blue"&gt;\$i&lt;/span&gt;++;&lt;/strong&gt;. или другие вариации на ту
же тему вроде &lt;strong&gt;&lt;span style="color: blue"&gt;\$i&lt;/span&gt;+=&lt;span style="color: blue"&gt;1&lt;/span&gt;;&lt;/strong&gt;. Привычка использовать в качестве
индекса цикла переменную под названием i, казалось бы стара как Мир, мне
она досталась в наследство от &lt;strong&gt;C&lt;/strong&gt;, а в месте с ней "в комплекте" шла
привычка писать выражение &lt;strong&gt;i++&lt;/strong&gt; в заголовках циклов. Разница в
скорости обработки этих выражений, насколько мне известно, обусловлена
разным количеством элементарных машинных операций, которые необходимо
выполнить процессору (в точных цифрах не уверен, пишу по памяти, но
&lt;strong&gt;++&lt;span style="color: blue"&gt;\$i&lt;/span&gt;;&lt;/strong&gt; требует трех элементарных
операций, а &lt;strong&gt;&lt;span style="color: blue"&gt;\$i&lt;/span&gt;++;&lt;/strong&gt; &amp;ndash; четырех). В
справедливости этого факта не трудно убедиться, достаточно написать
простенький скрипт, состоящий из цикла с достаточно большим количеством
итераций, и замерить любым способом точное время его выполнения при
использовании разных способов инкрементации индекса цикла.&lt;/p&gt;
&lt;h4&gt;Вывод статического контента без помощи PHP&lt;/h4&gt;
&lt;p&gt;Сейчас тот факт, что использование интерпретатора PHP для вывода
статического контента сильно замедляет этот процесс, кажется мне
очевидным, но поначалу я использовал &lt;strong&gt;&lt;span style="color: #17349c"&gt;echo&lt;/span&gt;&lt;/strong&gt; там, где он был необходим, ничуть
не чаще, чем там, где он лишь замедляет работу скрипта. От использования
еще менее эффективного способа - &lt;strong&gt;&lt;span style="color: #17349c"&gt;print&lt;/span&gt;&lt;/strong&gt;, меня избавила моя лень: писать
каждый раз на одну букву больше дико не хотелось (в отличии от &lt;strong&gt;&lt;span style="color: #17349c"&gt;echo&lt;/span&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;span style="color: #17349c"&gt;print&lt;/span&gt;&lt;/strong&gt; возвращает информацию об
успешности выполнения своей работы, что в большинстве случаев
просто-напросто не нужно). Проверить опять же не трудно - нужен лишь
объемистый текстовый файл, который достаточно вывести в browser разными
способами и засечь уходящее на это время.&lt;/p&gt;
&lt;h4&gt;Вывод статического контента из отдельного файла&lt;/h4&gt;
&lt;p&gt;Частенько при желании выполнить указанное в заголовке действие по
привычке используют &lt;strong&gt;&lt;span style="color: #a3a423"&gt;include&lt;/span&gt;&lt;/strong&gt;,
&lt;strong&gt;&lt;span style="color: #a3a423"&gt;require&lt;/span&gt;&lt;/strong&gt; или их &lt;strong&gt;&lt;span style="color: #a3a423"&gt;_once&lt;/span&gt;&lt;/strong&gt; версии, что является далеко не
самой лучшей идеей с точки зрения производительности. Самым быстрыми
быстрыми и экономичными поотношению к оперативной памяти являются
функции &lt;strong&gt;&lt;span style="color: #17349c"&gt;readfile&lt;/span&gt;&lt;/strong&gt; и &lt;strong&gt;&lt;span style="color: #17349c"&gt;fpassthru&lt;/span&gt;&lt;/strong&gt;. В качестве доказательства
этого факта приведу таблицу, демонстрирующую статистику выполнения этой
операции различными методами и позаимствованную с &lt;a href="https://www.insight-it.ru/goto/f18fa5ab/" rel="nofollow" target="_blank" title="http://www.raditha.com/wiki/Readfile_vs_include"&gt;одного англоязычного
сайта&lt;/a&gt;:&lt;/p&gt;
&lt;table align="center" border="1" cellspacing="0" width="100%"&gt;
&lt;tr&gt;
&lt;th align="center" 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" valign="center"&gt;
Функция

&lt;/th&gt;
&lt;th align="center" 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"&gt;
Время (сек.)

&lt;/th&gt;
&lt;th align="center" 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"&gt;
Оперативная память (байт)

&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="center" style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial"&gt;
32Kb файл

&lt;/td&gt;
&lt;td align="center" style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial"&gt;
1Mb файл

&lt;/td&gt;
&lt;td align="center" style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial"&gt;
32Kb файл

&lt;/td&gt;
&lt;td align="center" style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial"&gt;
1Mb файл

&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
file\_get\_contents

&lt;/td&gt;
&lt;td align="right"&gt;
0.00152

&lt;/td&gt;
&lt;td align="right"&gt;
0.00564

&lt;/td&gt;
&lt;td align="right"&gt;
52480

&lt;/td&gt;
&lt;td align="right"&gt;
1067856

&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
fpassthru

&lt;/td&gt;
&lt;td align="right"&gt;
**0.00117**

&lt;/td&gt;
&lt;td align="right"&gt;
0.00184

&lt;/td&gt;
&lt;td align="right"&gt;
20016

&lt;/td&gt;
&lt;td align="right"&gt;
20032

&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
fgets

&lt;/td&gt;
&lt;td align="right"&gt;
0.00195

&lt;/td&gt;
&lt;td align="right"&gt;
0.07190

&lt;/td&gt;
&lt;td align="right"&gt;
30760

&lt;/td&gt;
&lt;td align="right"&gt;
30768

&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
file

&lt;/td&gt;
&lt;td align="right"&gt;
0.00157

&lt;/td&gt;
&lt;td align="right"&gt;
0.06464

&lt;/td&gt;
&lt;td align="right"&gt;
87344

&lt;/td&gt;
&lt;td align="right"&gt;
2185624

&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
require\_once

&lt;/td&gt;
&lt;td align="right"&gt;
0.00225

&lt;/td&gt;
&lt;td align="right"&gt;
0.08065

&lt;/td&gt;
&lt;td align="right"&gt;
67992

&lt;/td&gt;
&lt;td align="right"&gt;
2067696

&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
readfile

&lt;/td&gt;
&lt;td align="right"&gt;
**0.00117**

&lt;/td&gt;
&lt;td align="right"&gt;
0.00191

&lt;/td&gt;
&lt;td align="right"&gt;
**19192**

&lt;/td&gt;
&lt;td align="right"&gt;
19208

&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h4&gt;Вывод переменных&lt;/h4&gt;
&lt;p&gt;Наверняка вам известно, что переменные можно выводить с помощью
конструкции вроде &lt;strong&gt;&lt;span style="color: #17349c"&gt;echo&lt;/span&gt; &lt;span style="color: red"&gt;"&lt;/span&gt;&lt;span style="color: blue"&gt;\$var&lt;/span&gt; &lt;span style="color: red"&gt;text"&lt;/span&gt;;&lt;/strong&gt;, что является одним из самых удобных
вариантов решения этой задачи благодаря минимальному количеству
символов, которые необходимо набрать, но с точки зрения быстродействия
этот вариант далек от идеала, так как влечет за собой достаточно
серьезные преобразования в памяти сервера, эффект которых порой бывает
заметен невооруженным глазом. Частично ущерб производительности можно
сгладить заменой этой конструкции на &lt;strong&gt;&lt;span style="color: #17349c"&gt;echo&lt;/span&gt; &lt;span style="color: blue"&gt;\$var&lt;/span&gt;.&lt;span style="color: red"&gt;"
text"&lt;/span&gt;;&lt;/strong&gt;, что приводит к несколькому усложнению внешнего вида
кода и несколько поправляет ситуацию со скоростью выполнения. Но как
известно знак . обозначает конкатенацию двух строк, что тоже требует
некоторых вычислений и затрат памяти, но и от нее можно избавиться,
заменив на запятую. Выражение &lt;strong&gt;&lt;span style="color: #17349c"&gt;echo&lt;/span&gt;
&lt;span style="color: blue"&gt;\$var&lt;/span&gt;,&lt;span style="color: red"&gt;"
text"&lt;/span&gt;;&lt;/strong&gt; ничем по своему эффекту не отличается от предложенных
ранее вариантов, за исключением максимального быстрого выполнения,
обусловленного отсутствием дополнительных преобразований в процессе
передачи просто последовательности из константы и переменной.&lt;/p&gt;
&lt;h4&gt;Избегайте выполнения лишних действий&lt;/h4&gt;
&lt;p&gt;Достаточно абстрактное утверждение, но тем не мение постоянное
напоминание себе о нем может избавить Вас от совершения массы ошибок.
Самой широкораспространенной является наверное вызов какой-либо функции
(чаще всего &lt;strong&gt;&lt;span style="color: #17349c"&gt;count&lt;/span&gt;();&lt;/strong&gt; или &lt;strong&gt;&lt;span style="color: #17349c"&gt;strlen&lt;/span&gt;();&lt;/strong&gt;) в проверке условия выхода из
цикла. Когда-нибудь доводилось писать видеть в собственном или чужом
коде выражение вида &lt;strong&gt;&lt;span style="color: #a3a423"&gt;for&lt;/span&gt;(&lt;span style="color: blue"&gt;\$i&lt;/span&gt; = &lt;span style="color: #a3a423"&gt;0&lt;/span&gt;;
&lt;span style="color: blue"&gt;\$i&lt;/span&gt; \&amp;lt; &lt;span style="color: #17349c"&gt;count&lt;/span&gt;(&lt;span style="color: blue"&gt;\$array&lt;/span&gt;); ++&lt;span style="color: blue"&gt;\$i&lt;/span&gt;) { ... }&lt;/strong&gt;? А задумываться о
последовательности выполнения действий при его обработке? Стоит только
немного начать размышлять и ошибка становится очевидной: &lt;strong&gt;&lt;span style="color: #17349c"&gt;count&lt;/span&gt;();&lt;/strong&gt; выполняется при каждой итерации
цикла, что приводит к подсчету количества элементов массива при каждой
проверки условия выхода из цикла - почему бы не посчитать это значение
заранее и сравнивать значения индекса с переменной, а не с результатом
выполнения функции?&lt;/p&gt;
&lt;h4&gt;@&lt;/h4&gt;
&lt;p&gt;Использование этого оператора стоит избегать при каждой возможности.
Казалось бы такое простое действие, как сокрытие вывода возможного
сообщения об ошибке, влечет за собой достаточно трудоемкую
последовательность действий: устанавливает значение параметра
PHP-интерпретатора &lt;strong&gt;error_reporting = 0&lt;/strong&gt;, выполняет указанное за этим
оператором действие, возвращает значение &lt;strong&gt;error_reporting&lt;/strong&gt; в исходное
состояние.&lt;/p&gt;
&lt;h4&gt;Маленькие мелочи&lt;/h4&gt;
&lt;p&gt;Развивая тему предыдущего подраздела, хочется обратить внимания, что
даже на еще более элементарных вещах можно сэкономить драгоценное
процессорное время:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Вместо условия &lt;strong&gt;&lt;span style="color: #a3a423"&gt;if&lt;/span&gt;(&lt;span style="color: blue"&gt;\$variableOne&lt;/span&gt; == &lt;span style="color: blue"&gt;\$variableTwo&lt;/span&gt;) { ... }&lt;/strong&gt; можно написать
    &lt;strong&gt;&lt;span style="color: #a3a423"&gt;if&lt;/span&gt;(&lt;span style="color: blue"&gt;\$variableOne&lt;/span&gt; === &lt;span style="color: blue"&gt;\$variableTwo&lt;/span&gt;) { ... }&lt;/strong&gt;, что избавит от
    проверки на соответствие типов данных и приведения их друг к другу,
    в некоторых случаях эти действия эти случаях эти действия конечно же
    и бывают необходимы, но бывает это далеко не часто.&lt;/li&gt;
&lt;li&gt;Глядя на выражения вроде &lt;strong&gt;&lt;span style="color: #a3a423"&gt;if&lt;/span&gt;(&lt;span style="color: blue"&gt;\$boolean&lt;/span&gt; == true) { ... }&lt;/strong&gt;, я чаще
    всего вспоминаю цитату из одного малоизвестного интернет-ресурса:
    &lt;strong&gt;if (b.ToString().length \&amp;lt; 5) { ... }&lt;/strong&gt;. Хоть и не имет никакого
    отношения к PHP, но суть проблемы отражает очень ярко.&lt;/li&gt;
&lt;li&gt;Самым очевидным способом проверить попадает ли длина строки в
    какой-либо диапазон является использование функции &lt;strong&gt;&lt;span style="color: #17349c"&gt;strlen&lt;/span&gt;();&lt;/strong&gt; и сравнение полученного
    результата с фиксированными значениями, но зачем выполнять лишний
    вызов функции, если можно воспользоваться услугами конструкцией
    языка PHP &lt;strong&gt;&lt;span style="color: #17349c"&gt;isset&lt;/span&gt;();&lt;/strong&gt; для
    определения наличия в строке определенных символов. &lt;strong&gt;&lt;span style="color: #a3a423"&gt;if&lt;/span&gt;(&lt;span style="color: #17349c"&gt;isset&lt;/span&gt;(&lt;span style="color: blue"&gt;\$str&lt;/span&gt;{&lt;span style="color: blue"&gt;5&lt;/span&gt;})) { ... }&lt;/strong&gt; приведет к абсолютно тем
    же результатам, что и &lt;strong&gt;&lt;span style="color: #a3a423"&gt;if&lt;/span&gt;(&lt;span style="color: #17349c"&gt;strlen&lt;/span&gt;(\$str)&amp;gt;4){ ... }&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Битовые операции выполняются намного быстрее относительно обычных
    арифметических действий. Об этом факте редко вспоминают, да и
    работать с ними умеет далеко не каждый, но порой они бывают очень
    актуальны, особенно при частой работе с числами кратными двойке.&lt;/li&gt;
&lt;li&gt;Угадайте, что делает интерпретатор при виде надписи &lt;strong&gt;&lt;span style="color: blue"&gt;1&lt;/span&gt;/&lt;span style="color: blue"&gt;2&lt;/span&gt;&lt;/strong&gt;?
    Правильно: делит &lt;strong&gt;&lt;span style="color: blue"&gt;1&lt;/span&gt;&lt;/strong&gt; на &lt;strong&gt;&lt;span style="color: blue"&gt;2&lt;/span&gt;&lt;/strong&gt;. Зачем лишний раз утруждать его,
    когда можно написать просто половину - &lt;strong&gt;&lt;span style="color: blue"&gt;0.5&lt;/span&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;При возвращении значения переменной из функции при помощи &lt;strong&gt;&lt;span style="color: #17349c"&gt;global&lt;/span&gt;&lt;/strong&gt; выполняется на порядок больше
    действий, чем при классическом &lt;strong&gt;&lt;span style="color: #17349c"&gt;return&lt;/span&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Конечно же фраза &lt;strong&gt;&lt;span style="color: blue"&gt;\$array&lt;/span&gt;[text];&lt;/strong&gt;
    интерпритируется практически точно так же, как и &lt;strong&gt;&lt;span style="color: blue"&gt;\$array&lt;/span&gt;[&lt;span style="color: red"&gt;'text'&lt;/span&gt;];&lt;/strong&gt;, но зачем выполнять лишнее
    преобразование из необъявленной константы в строку, проверять, что
    такой константы все же не существует, выводить сообщение типа
    &lt;strong&gt;E_NOTICE&lt;/strong&gt;, если можно всего этого не делать?&lt;/li&gt;
&lt;li&gt;По возможности не используйте &lt;strong&gt;&lt;span style="color: #17349c"&gt;require_once&lt;/span&gt;();&lt;/strong&gt; или &lt;strong&gt;&lt;span style="color: #17349c"&gt;include_once&lt;/span&gt;();&lt;/strong&gt; неоднократно по
    отношению к одному и тому же файлу. При отсутствии какого-либо
    эффекта, попусту тратится время на обработку повторного запроса.&lt;/li&gt;
&lt;li&gt;Даже "безобидных" ошибок стоит избегать, лишняя проаерка потратит не
    так много процессорного времени, как генерирование достаточно
    длинного сообщения об ошибке и вывод его в &lt;em&gt;stdout&lt;/em&gt;, &lt;em&gt;stderr&lt;/em&gt; или
    &lt;em&gt;лог-файл&lt;/em&gt;, а также не стоит забывать, что даже "безобидные" ошибки
    могут стать потенциальной угрозой безопасности приложения вцелом.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;В заключении...&lt;/h4&gt;
&lt;p&gt;...хотелось бы упомянуть одну из первых статей по
&lt;a href="/tag/optimizaciya/"&gt;оптимизации&lt;/a&gt; &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;, которые мне доводилось
читать, до сих пор храню ссылку на нее в bookmark'ах, именно она и
выступала в роли &lt;em&gt;&lt;a href="https://www.insight-it.ru/goto/ce3d47c9/" rel="nofollow" target="_blank" title="http://mgccl.com/php-speed-freaks"&gt;одного из основных источников
информации&lt;/a&gt;&lt;/em&gt; для этого текста. В
качестве возможных вариантов продолжения чтения про &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;
хотелось бы предложить Вам соответствующие &lt;a href="/category/php/"&gt;раздел сайта&lt;/a&gt;, &lt;a href="https://www.insight-it.ru/dzhentelmenskij-nabor-php-programmista/"&gt;серию
статей&lt;/a&gt;, &lt;a href="/tag/php/"&gt;тэг&lt;/a&gt; и
&lt;a href="/feed/"&gt;RSS-ленту&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Thu, 07 Feb 2008 15:39:00 +0300</pubDate><guid>tag:www.insight-it.ru,2008-02-07:php/2008/na-puti-k-idealu/</guid><category>online</category><category>PHP</category><category>интернет</category><category>информационные технологии</category><category>код</category><category>кодинг</category><category>оптимизация</category><category>Программирование</category><category>производительность</category></item><item><title>Шаблонизация</title><link>https://www.insight-it.ru//php/2008/shablonizaciya/</link><description>&lt;p&gt;Наверняка Вы часто замечали, что в пределах одного сайта все (или
покрайней мере большинство) страниц имеют много общего: структуру,
расположение элементов, дизайн и так далее. Основным различием обычно
является лишь содержание. Естественно, что делается это не спроста:
именно общие компоненты сайта создают в голове посетителей тот самый
образ, который производит общее подсознательное впечатление о сайте, а
также позволяет посетителям отличать сайт А от сайта Б.&lt;/p&gt;
&lt;p&gt;Продолжая разговор, начатый еще в
&lt;a href="https://www.insight-it.ru/php/2008/obshhaemsya-s-bazojj-dannykh/"&gt;одной&lt;/a&gt; из &lt;a href="https://www.insight-it.ru/dzhentelmenskij-nabor-php-programmista/"&gt;предыдущих
статей&lt;/a&gt;, рассмотрим организацию интерфейса между двумя другими составляющими практически любого
интернет-проекта: скриптов (все так же на примере PHP) и страницами,
отправляемыми посетителям посредством http-сервера.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;С точки зрения веб-разработчика было бы как минимум не логично мешать в
кучу постоянные части страниц с динамическими. Для этого существует
множество причин, в том числе, например, экономия вычислительной
мощности сервера на отсутствии необходимости каждый раз заново
генерировать статичные элементы или неминуемое сокращение объемов кода,
который необходимо написать, в случае если статический и динамический
контент разделены. Отделенную подобным образом статическую часть страниц
(слегка модифицированную с целью обозначить правила, по которым будет
проводиться "заполнение" ее динамическим контентом) принято называть
словом &lt;em&gt;"шаблон"&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Наверняка у Вас уже возникло два вполне логичных вопроса:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Как можно разделить таким образом контент?&lt;/li&gt;
&lt;li&gt;Как потом восстановить страницу в исходном виде?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Вариантов ответа на каждый из них можно придумать множество: начиная с
банальных вариаций на тему применения &lt;strong&gt;echo&lt;/strong&gt;, заканчивая применением
достаточно серьезных готовых решений вроде широкораспространенного
&lt;a href="https://www.insight-it.ru/goto/2915d764/" rel="nofollow" target="_blank" title="http://smarty.php.net"&gt;&lt;strong&gt;Smarty&lt;/strong&gt;&lt;/a&gt; или существенно более эффективного
&lt;a href="https://www.insight-it.ru/goto/8c8929a1/" rel="nofollow" target="_blank" title="http://alexeyrybak.com/blitz/blitz_ru.html"&gt;&lt;strong&gt;Blitz&lt;/strong&gt;&lt;/a&gt;. Каждый из них
имеет свои сильные и слабые стороны, но в целом любой из них можно
оценить по двум критериям: производительности и удобстве организации
кода.&lt;/p&gt;
&lt;p&gt;Какие-либо цифры оценки производительности приводить не буду, так как,
во-первых, в Сети можно найти много benchmark'ов, посвященных этой теме,
а, во-вторых, просто-напросто вовсе не о цифрах я хотел с Вами
поговорить. Как известно самым производительным по крайней мере с
теоретической точки зрения является метод под названием &lt;strong&gt;php mess&lt;/strong&gt;, заключается он в следующем: вся страница размещается в рамках
одного файла, при этом статическая часть документа пишется просто "как
есть" в соответствии с необходимым стандартом, а изменяемые части
организуются размещенным в необходимых местах PHP-кодом, окруженным
стандартной конструкцией &lt;code&gt;&amp;lt;?php&amp;nbsp;&amp;nbsp;&amp;nbsp;?&amp;gt;&lt;/code&gt;. Но огромнейший недостаток
очевиден - огромное количество информации расположенной в одном файле,
при отсутствии какого-либо более четкого разделения PHP-кода и
остального содержимого, чем указанная выше конструкция, приводит к
постоянной путанице в коде, а также существенным затратам времени
программиста при попытках исправить ту или иную часть документа.&lt;/p&gt;
&lt;p&gt;На противоположной стороне нашей шкалы &lt;em&gt;удобство-производительность&lt;/em&gt; я
бы расположил уже упомянутое выше решение под названием &lt;strong&gt;Smarty&lt;/strong&gt;.
Представляет оно собой целую систему, реализованную также на PHP, и
предоставляющую огромное количество возможностей по решению нашей
задачи. Шаблоны хранятся в отдельных файлах, для определения мест
расположения динамического контента используется специальный синтаксис,
который прост как три копейки, так как разрабатывался с расчетом не на
программистов, а по принципу "чем проще, тем лучше". Именно этот факт
сделал &lt;strong&gt;Smarty&lt;/strong&gt; одним из самых (если не самым) распространенных
движков шаблонизации (или как их принято правильно называть "Template
Engine"). Но, к сожалению, за удобство приходится платить, в этом случае
производительностью: вся система сама по себе громоздка и состоит из
множества файлов, между которыми все данные так или иначе передаются, а
так как написано она на PHP (который является далеко не самым
производительным языком программирования, в основном в силу своей
интерпритируемости и некоторых других особенностей), конкуренции в плане
производительности многим другим вариантам решения нашей задачи
&lt;strong&gt;Smarty&lt;/strong&gt; составить не в состоянии.&lt;/p&gt;
&lt;p&gt;Одним из лучших "компромиссных" вариантов, которые доступны на данный
момент, могу назвать также упомянутый выше &lt;strong&gt;Blitz&lt;/strong&gt;. Реализован он в
виде модуля PHP, написанного на языке &lt;strong&gt;C&lt;/strong&gt;, что является залогом его
отличной производительности. При этом общая его концепция близка к
&lt;strong&gt;Smarty&lt;/strong&gt;: шаблоны также хранятся в отдельных файлах и подчинены
незамысловатому синтаксису (который вообще можно понять и запомнить
буквально за 15-20 минут, прочитав статью, &lt;a href="https://www.insight-it.ru/goto/8c8929a1/" rel="nofollow" target="_blank" title="http://alexeyrybak.com/blitz/blitz_ru.html"&gt;ссылку на
которую&lt;/a&gt; я уже приводил
выше), а в PHP-скриптах после установки становится доступен специальный
класс для управления модулем. Но основное достоинство этого решения
является одновременно и его основным недостатком - редкий хостинг имеет
этот модуль в списке предустановленных (видимо в силу своей не очень
обширной известности, обусловленной ), а доступ к http-серверу и
PHP-интерпретатору, который необходим для установки PHP-модулей,
предоставляется чаще всего только на дорогих тарифах виртуального
хостинга или на различных вариантах VPS или арендуемых серверов.&lt;/p&gt;
&lt;p&gt;Помимо этого некоторые энтузиасты берутся на написание "собственных"
&lt;em&gt;Template Engine&lt;/em&gt;, базирующихся на различных вариантов использования
PHP-функций вроде &lt;strong&gt;preg_replace&lt;/strong&gt;. Если честно такие попытки редко
заканчиваются успехом: в лучшем случае удается добиться удобства
использования самим разработчиком, но чаще всего в ущерб
производительности. Заниматься подобными экспериментами я Вам не
советую, вместо этого я предлагаю написать &lt;em&gt;"обертку"&lt;/em&gt; к приглянувшемуся
распространенному &lt;em&gt;Template Engine&lt;/em&gt;, что позволит не только сделать его
использование более удобным конкретно для Вас, но и позволит заменить
его на другой с минимальными затратами сил и времени (например в случае,
если модуль &lt;strong&gt;Blitz&lt;/strong&gt; недоступен).&lt;/p&gt;
&lt;h3 id="razrabatyvaem-obertku"&gt;Разрабатываем "обертку"&lt;/h3&gt;
&lt;p&gt;Сразу скажу, что цели привести конкретный пример пригодного для
реального использования кода я перед собой не ставлю в этой части моего
повествования. Я лишь хочу показать направление, в котором можно
провести разработку с целью облегчения собственной же жизни, т.е.
предоставить Вам альтернативу простому использованию тех или иных
решений в том виде, в котором они предоставлены разработчиками.&lt;/p&gt;
&lt;p&gt;Если Ваш выбор всетаки пал на написание "оболочки", не смотря на
принесение в жертву несущественной части производительности, то стоит
для начала определиться: а что же мы будем "заворачивать"? В качестве
примера я, пожалуй, буду использовать &lt;strong&gt;Blitz&lt;/strong&gt;, как самый оптимальный
вариант (по крайней мере с моей точки зрения). Начать стоит как обычно с пустой заготовки для класса.&lt;/p&gt;
&lt;p&gt;Далее следует решить какие все же модификации мы будем производить для
собственного удобства над стандартным решением. Попробую привести
несколько примеров в отношении &lt;strong&gt;Blitz&lt;/strong&gt;, для начала хочу обратить
внимание, что при внимательном прочтении все той же статьи от
разработчика этого шаблонизатора, можно обнаружить, что модуль
показывает более высокие показатели производительности при однократном
вызове метода &lt;em&gt;set&lt;/em&gt;. Достичь это можно выполнением этого метода с
указанием в качестве одного из входных параметров "многоуровнего"
массива, составленного специальным образом (надеюсь Вы все же к этому
моменту уже успели прочитать неоднократно упоминавшуюся статью, и
представляете принцип работы модуля). Написание механизма составления
такого массива позволит как сократить время разработки, так и сэкономит
драгоценные миллисекунды, вычитаемые из свободного времени посетителей
сайта в процессе генерации страницы.&lt;/p&gt;
&lt;p&gt;В любом случае понадобится переменная для его хранения:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TemplateEngine&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;//можно сразу указать указать путь к папке с шаблонами&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Blitz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./template/'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'.tpl'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;А также метод, переопределяющий стандартный &lt;em&gt;set&lt;/em&gt; на метод, добавляющий
новые значения к нашему массиву (хотя можно и любое другое понравившееся
название использовать):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$caption&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;После чего оригинальный &lt;em&gt;set&lt;/em&gt; можно использовать уже непосредственно
перед &lt;em&gt;parse&lt;/em&gt;, с указанием уже собранного массива в качестве параметра.
За компанию позволю произвести себе еще одну модификацию: в подавляющем
большинстве случаев &lt;em&gt;parse&lt;/em&gt; используется в совокупности с &lt;strong&gt;echo&lt;/strong&gt;,
чтобы не указывать каждый раз это слово - можно включить его прямо в наш
класс:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;array&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;array&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Еще одним возможным вариантом модификации может стать обработка всех
(или какой-то части, если есть необходимость) динамических данных с
помощью какой-либо функции, например это актуально для
&lt;strong&gt;htmlspecialchars&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$caption&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rawset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$caption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$caption&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;is_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$array&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$caption&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;htmlspecialchars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;ENT_QUOTES&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Как нетрудно заметить, в методе используется рекурсия, так как структура
передаваемых параметром массивов неизвестна.&lt;/p&gt;
&lt;p&gt;Надеюсь написанный выше текст подтолкнет Вас к действию или хотябы
заставит задуматься над имеющимся выбором, если же Вы читали его лишь
"для общего развития", то тем более хочется сказать Вам огромное
&lt;em&gt;Спасибо за то, что дочитали до конца это повествование.&lt;/em&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Sat, 26 Jan 2008 15:25:00 +0300</pubDate><guid>tag:www.insight-it.ru,2008-01-26:php/2008/shablonizaciya/</guid><category>blitz</category><category>PHP</category><category>Smarty</category><category>template</category><category>template engine</category><category>интернет</category><category>ООП</category><category>разработка</category><category>шаблон</category><category>шаблонизация</category></item><item><title>Общаемся с базой данных</title><link>https://www.insight-it.ru//php/2008/obshhaemsya-s-bazojj-dannykh/</link><description>&lt;p&gt;На этот раз хочется обсудить такой одновременно важный и несложный
момент в реализации работы любого интернет-проекта, как координации
работы Ваших скриптов с СУБД.&lt;/p&gt;
&lt;p&gt;Если подойти к этому вопросу "в лоб", то код станет очень неудобен как
для понимания, так и для использования: код станет переполнен различными
функциями соединения с БД, отправки запросов, преобразования результатов
запросов в массивы PHP, подсчета строк, которые затронул запрос, а также
многие и многие другие.&lt;/p&gt;
&lt;p&gt;Для желающих минимизировать подобного рода издержки в процессе написания
кода, хочу предложить один из, на мой взгляд, самых эффективных способов
решения этой проблемы.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Этим способом будет являться написание класса, реализующего интерфейс
между СУБД и PHP-скриптами. Для начала стоит определиться с
ассортиментом функций, которые будет призван выполнять наш класс:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;установка соединения&lt;/em&gt;, а также проверка успешности выполнения этого
    действия;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;отправка запросов&lt;/em&gt;, как заданных извне так и, возможно, из
    какого-либо ассортимента заранее написанных запросов;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;обработка результатов запросов&lt;/em&gt;, не ограничивающаяся одним SELECT,
    должны быть предоставлены методы обработки результатов любых видов
    запросов (или хотябы большинства).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Вполне очевидным является тот факт, что методы этого класса будут
использоваться практически повсеместно в большинстве проектов.
Вследствии чего становится нецелесообразным создание объекта нашего
класса и передача его по всем функциям и методам всех скриптов, в таких
случае намного предпочтительнее делать владельцем методов и переменных
сам класс, а не экземпляр класса, с помощью ключевого слова &lt;strong&gt;static&lt;/strong&gt;.
Это позволит пользоваться услугами нашего класса из любого места кода.
Приступим-с собственно к кодингу, начать стоит с заготовки пустого
класса:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SQL&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В зависимости от предпочитаемой Вами СУБД набор конкретных функций,
используемых в реализации нашего класса, будет вариироваться. В
большинстве случаев предпочитаю пользоваться PostgreSQL, на это причин у
меня несколько, но это тема для отдельного разговора. Благодаря этому
факту приводимый в качестве примера код будет использовать функции для
работы именно с этой СУБД. Для поклонников же других этот систем вопрос
в подавляющем большинстве случаев заключается лишь в замене этих функций
на аналогичные из других модулей PHP, например для популярной и
широкораспространенной MySQL достаточно будет всеголишь пройтись
автозаменой &lt;strong&gt;pg_ =&amp;gt; mysql_&lt;/strong&gt; и слегка подредактировать параметры
некоторых функций.&lt;/p&gt;
&lt;p&gt;Перейдем к реализации установления соединения с СУБД, не стоит ожидать
увидеть здесь ничего необычного:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// установка соединения&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;pg_pconnect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"host=localhost dbname=pgsql user=pgsql password=MyPassword"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// не забываем менять указанные данные для авторизации на правильные&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Сайт не работает по техническим причинам.&lt;/span&gt;
&lt;span class="s2"&gt;Просим прощения за доставленные неудобства."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// ни в коем случае не выводим более информативных сообщений об ошибке, чем это&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;А вот с отправкой и обработкой результатов запросов ситуация далеко не
так однозначна. Помимо простой передачи самого текста запроса в СУБД,
необходимо правильно определить тип запроса и в соответствии с этим
обработать результат. Можно конечно попытаться сделать это автоматически
на основе вытаскивания первого слова из текста запроса, но мне всетаки
кажется более предпочтительным определение "вручную" желаемого вида
представление результата. Выполнение произвольных запросов может
выглядеть, например, следующим образом:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$bool&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// произвольный запрос&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//echo $str."&lt;/span&gt;
&lt;span class="s2"&gt;"; // очень удобно на стадии разработки в процессе поиска ошибок&lt;/span&gt;
&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;=@pg_query(self::&lt;/span&gt;&lt;span class="si"&gt;$connection&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="si"&gt;$str&lt;/span&gt;&lt;span class="s2"&gt;); // @ - для сокрытия теоретически возможных ошибок&lt;/span&gt;
&lt;span class="s2"&gt;  // or die('Query failed: '.pg_last_error());&lt;/span&gt;
&lt;span class="s2"&gt;  // не забываем убирать в комментарий в финальном варианте проекта&lt;/span&gt;
&lt;span class="s2"&gt;  // или совсем удалять&lt;/span&gt;
&lt;span class="s2"&gt;  if(&lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;)  // Если получен результат, отличный от false&lt;/span&gt;
&lt;span class="s2"&gt;  {&lt;/span&gt;
&lt;span class="s2"&gt;    if(&lt;/span&gt;&lt;span class="si"&gt;$bool&lt;/span&gt;&lt;span class="s2"&gt;)  // Если выбран результат в виде boolean&lt;/span&gt;
&lt;span class="s2"&gt;    {&lt;/span&gt;
&lt;span class="s2"&gt;      return true;&lt;/span&gt;
&lt;span class="s2"&gt;    }&lt;/span&gt;
&lt;span class="s2"&gt;    else  // Если выбран результат в виде массива&lt;/span&gt;
&lt;span class="s2"&gt;    {&lt;/span&gt;
&lt;span class="s2"&gt;      &lt;/span&gt;&lt;span class="si"&gt;$n&lt;/span&gt;&lt;span class="s2"&gt;=pg_num_rows(&lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;);  // для создания универсального формата массива&lt;/span&gt;
&lt;span class="s2"&gt;      if(&lt;/span&gt;&lt;span class="si"&gt;$n&lt;/span&gt;&lt;span class="s2"&gt;==1)return pg_fetch_array(&lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;,0,PGSQL_ASSOC);&lt;/span&gt;
&lt;span class="s2"&gt;      else  // даже когда результат содержит только одну строку&lt;/span&gt;
&lt;span class="s2"&gt;      {&lt;/span&gt;
&lt;span class="s2"&gt;        &lt;/span&gt;&lt;span class="si"&gt;$j&lt;/span&gt;&lt;span class="s2"&gt;=pg_num_rows(&lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;);&lt;/span&gt;
&lt;span class="s2"&gt;        &lt;/span&gt;&lt;span class="si"&gt;$list&lt;/span&gt;&lt;span class="s2"&gt;=array();&lt;/span&gt;
&lt;span class="s2"&gt;        for(&lt;/span&gt;&lt;span class="si"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;=0;&lt;/span&gt;&lt;span class="si"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;&lt;/span&gt;&lt;span class="si"&gt;$j&lt;/span&gt;&lt;span class="s2"&gt;;&lt;/span&gt;&lt;span class="si"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;++)&lt;/span&gt;
&lt;span class="s2"&gt;        &lt;/span&gt;&lt;span class="si"&gt;$list&lt;/span&gt;&lt;span class="s2"&gt;[]=pg_fetch_array(&lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="si"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;,PGSQL_ASSOC);&lt;/span&gt;
&lt;span class="s2"&gt;        return &lt;/span&gt;&lt;span class="si"&gt;$list&lt;/span&gt;&lt;span class="s2"&gt;;&lt;/span&gt;
&lt;span class="s2"&gt;      }&lt;/span&gt;
&lt;span class="s2"&gt;    }&lt;/span&gt;
&lt;span class="s2"&gt;  }else return false;&lt;/span&gt;
&lt;span class="s2"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;?&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Помимо базовой отправки запросов, в некоторых случаях имеет смысл
написать несколько методов, отправляющих частоиспользуемые запросы, что
в некоторых случаях позволяет сократить объем и уменьшить
нагроможденность кода. Хоть я и предпочитаю не пользоваться такими
вещами, но привести пример такого рода метода все же стоит:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;selectAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// пример метода отправки чаcтоиспользуемых запросов&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"select * from "&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;";"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Если чувствуете необходимость в подобных функциях, можно написать
огромное количество, все ограничивается лишь Вашим воображением и
знаниями SQL.&lt;/p&gt;
&lt;p&gt;Что ж, осталось лишь собрать весь код в единый листинг:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SQL&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// соединение с СУБД&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// установка соединения&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;pg_pconnect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"host=localhost dbname=pgsql user=pgsql password=MyPassword"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// не забываем менять указанные данные для авторизации на правильные&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Сайт не работает по техническим причинам.&amp;lt;br /&amp;gt;Просим прощения за доставленные неудобства."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ни в коем случае не выводим более информативных сообщений об ошибке, чем это&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$bool&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// произвольный запрос&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//echo $str."&amp;lt;br&amp;gt;"; // очень удобно на стадии разработки в процессе поиска ошибок&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;=@&lt;/span&gt;&lt;span class="nb"&gt;pg_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// @ - для сокрытия теоретически возможных ошибок&lt;/span&gt;
    &lt;span class="c1"&gt;// or die('Query failed: '.pg_last_error());&lt;/span&gt;
    &lt;span class="c1"&gt;// не забываем убирать в комментарий в финальном варианте проекта&lt;/span&gt;
    &lt;span class="c1"&gt;// или совсем удалять&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Если получен результат, отличный от false&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Если выбран результат в виде boolean&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;  &lt;span class="c1"&gt;// Если выбран результат в виде массива&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;pg_num_rows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// для создания универсального формата массива&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;pg_fetch_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;PGSQL_ASSOC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;  &lt;span class="c1"&gt;// даже когда результат содержит только одну строку&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nv"&gt;$j&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;pg_num_rows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nv"&gt;$list&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$j&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="nv"&gt;$list&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;pg_fetch_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;PGSQL_ASSOC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$list&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;selectAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// пример метода отправки чаcтоиспользуемых запросов&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"select * from "&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;";"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;```&lt;/p&gt;
&lt;p&gt;Вот так вот оно и выглядит в простейшем варианте, дорабатывать под
собственные нужды код можно до бесконечности естественно, но в
большинстве случаев даже такой реализации вполне должно хватать.&lt;/p&gt;
&lt;p&gt;Эта статья является частью &lt;a href="https://www.insight-it.ru/dzhentelmenskij-nabor-php-programmista/"&gt;серии статей "Джентельменский набор PHP программиста"&lt;/a&gt;, если Вам
понравилась эта статья то очень вероятно, что Вам придутся по душе и
остальные статьи.&lt;/p&gt;
&lt;p&gt;Не забываем &lt;a href="/feed/"&gt;подписываться на RSS блога&lt;/a&gt;!&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Wed, 16 Jan 2008 22:04:00 +0300</pubDate><guid>tag:www.insight-it.ru,2008-01-16:php/2008/obshhaemsya-s-bazojj-dannykh/</guid><category>MySQL</category><category>PHP</category><category>PostgreSQL</category><category>SQL</category><category>БД</category><category>интерфейс</category><category>кодинг</category><category>ООП</category><category>СУБД</category><category>технология</category><category>хранение данных</category></item><item><title>Защита интернет-ресурсов в картинках</title><link>https://www.insight-it.ru//php/2008/zashhita-internet-resursov-v-kartinkax/</link><description>&lt;p&gt;Этой статьей мне хотелось бы открыть мою первую &lt;a href="https://www.insight-it.ru/dzhentelmenskij-nabor-php-programmista/"&gt;серию статей "Джентельменский набор PHP программиста"&lt;/a&gt;. Как и во всей остальной серии здесь пойдет речь о программировании на PHP для интернет-проектов, но в каждой статье я буду выбирать один узкий аспект
и на протяжении всей статьи буду стараться показать возможные варианты
его реализации и применения.&lt;/p&gt;
&lt;p&gt;Сегодня таким аспектом станет защита интернет-ресурса от возможного
возникновения нежелательного контента со стороны пользователей с помощью
технологии captcha (точнее о "графическом" варианте ее реализации), о
которой уже &lt;a href="https://www.insight-it.ru/security/2008/otkuda-voznikaet-spam-i-kak-s-nim-borotsya/"&gt;неоднократно шла речь&lt;/a&gt;.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Начать имеет смысл с небольшого напоминания о принципе работы этой
технологии: перед потенциальным посетителем ставится некое препятствие,
которое ему необходимо преодолеть для продолжения работы с
интернет-ресурсом. Существует множество &lt;a href="https://www.insight-it.ru/security/2008/7-sposobov-zashhitit-svoj-internet-resurs-ot-nezhelatelnoj-informacii/"&gt;вариантов такого рода
препятствий&lt;/a&gt;.
Как уже упоминалось, сегодня мы будем реализовывать только один наиболее
распространенный тип - "графический". В простейшем случае он
представляет собой просьбу переписать с изображения некий набор
символов. В процессе генерирования изображения, символы сильно
искажаются с целью предотвращения возможности их распознавания любой
программой с помощью технологии OCR.&lt;/p&gt;
&lt;h3 id="podgotovka"&gt;Подготовка&lt;/h3&gt;
&lt;p&gt;Прежде чем начать писать код стоит более детально осознать какая же цель
перед нами стоит: нам необходимо написать скрипт, генерирующий
искаженное изображение некоторого набора символов и незаметно для
пользователя передающее этот набор какому-либо другому скрипту, который
нас пока мало интересует, но ясно лишь, что собственно проверкой будет
заниматься именно он на основе данных полученных от пользователя и
нашего скрипта. Способов исказить текст существует огромное количество,
в ходе написания статьи постараюсь упомянуть несколько самых эффективных
и широкоиспользуемых из них.&lt;/p&gt;
&lt;p&gt;В первую очередь стоит подготовить некий каркас кода, который мы будем
впоследствии заполнять. Он будет состоять из двух частей:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Описание класса, генерирующего изображение&lt;/li&gt;
&lt;li&gt;Файл, который будет вызываться browser'ом. В нем будет подключено
    описание нашего класса, выбор настроек данного конкретного
    изображения и выполнено создание объекта класса, в соответствии с
    выбранными настройками.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Для начала давайте определимся со списком параметров, которые будет
иметь наш класс. Во-первых, нужно решить какой текст будет
генерироваться, самый простой и распространенный вариант - просто четыре
цифры, я в примере на нем и остановлюсь, а реально же можно использовать
абсолютно любые приходящие в голову варианты. Во-вторых, размеры
изображения и текста - их лучше подобрать фиксированными так, чтобы было
максимально читабельно, при минимальных размерах изображения, но при
желании можно сделать и возможность изменения их извне. Последним в
списке параметров будет цвет фона и текста - их как раз лучше задавать
вне класса, так как основным действием, необходимым при переносе этого
скрипта с одного сайт на другой - подбор используемых цветов таким
образом, чтобы изображение смотрелось не очень ужасно при текущем
варианте дизайна, изменения в других параметрах требуются на порядок
реже.&lt;/p&gt;
&lt;p&gt;Итак, создание объекта будем производить максимально простым способом,
параметрами укажем белый и черный цвета. Заготовка для самого класса будет выглядеть примерно следующим образом
(предположим, что он хранится в файле &lt;em&gt;captcha.class.php&lt;/em&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Captcha&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// генерируемый текст&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$bgcol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// основной цвет фона&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$fgcol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// основной цвет текста&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// высота изображения&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ширина изображения&lt;/span&gt;
   &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bgcol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$fgcol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// конструктор, вызывается при создании экземпляра класса&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="zadaem-parametry"&gt;Задаем параметры&lt;/h3&gt;
&lt;p&gt;Первым делом при создании объекта необходимо задать остальные параметры,
размеры изображения можно указать прямо в конструкторе, а для генерации
текста лучше написать отдельную функцию:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateImage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// генерация изображения&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$fgcol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$bgcol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;generateSymbols&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateSymbols&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="c1"&gt;// генерация четырех цифр&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;string&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;leadingZero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;leadingZero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// дополнения числа num лидирующими нулями&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;                        &lt;span class="c1"&gt;// до длины length&lt;/span&gt;
  &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;strrev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$num&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="o"&gt;.=&lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;strrev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Этих данных нам должно хватить для написания функции, генерирующей
изображение.&lt;/p&gt;
&lt;h3 id="generiruem-izobrazhenie"&gt;Генерируем изображение&lt;/h3&gt;
&lt;p&gt;Если забыть, что текст необходимо искажать, то функция, генерирующая
изображение выглядела бы просто как:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateImage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// генерация изображения&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="o"&gt;=@&lt;/span&gt;&lt;span class="nb"&gt;imagecreatetruecolor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nv"&gt;$bcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;imagecolorallocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
   &lt;span class="nv"&gt;$fcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;imagecolorallocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
   &lt;span class="nb"&gt;imagefill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$bcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nb"&gt;imagettftext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$fcol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"./font/font_name.ttf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;string&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
   &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type: image/png'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nb"&gt;imagepng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nb"&gt;imagedestroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="card yellow lighten-4"&gt;&lt;div class="card-content"&gt;
В данном методе используются
функции модуля PHP под названием GD, основывающегося на одноименной
библиотеке, убедитесь, что на Вашем хостинге этот модуль
установлен.
&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Реально же ей пользоваться не стоит - такое изображение с легкостью
поддается OCR. Полученный текст необходимо тем или иным образом
исказить. Для вывода изображения используется формат PNG, но никто не
мешает воспользоваться JPEG или GIF, для этого достаточно заменить везде
png на название соответствующего формата.&lt;/p&gt;
&lt;h3 id="iskazhaem-tekst"&gt;Искажаем текст&lt;/h3&gt;
&lt;p&gt;Вот списочек тех, способов искажения текста, которыми я буду
пользоваться в примере, пользоваться всеми сразу естественно никто не
заставляет, да и включив воображение можно придумать много модификаций
приведенных мной способов или абсолютно других:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;использование нестандартных шрифтов&lt;/em&gt; - функция imagettftext
    позволяет использовать произвольный шрифт в формате Truetype, чем и
    необходимо воспользоваться. В Сети можно найти огромное количество
    бесплатных шрифтов в этом формате. По возможности стоит выбирать
    шрифты, максимально не похожие на любой стандартный, но при этом
    легко читающиеся.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;использование нескольких шрифтов&lt;/em&gt; - сделав подборку подходящих
    шрифтов, можно не останавливаться на каком-то одном, а сделать выбор
    текущего шрифта случайным из списка.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;случайный выбор цветов&lt;/em&gt; - усложняет работу OCR и в большинстве
    случаев не сильно мешает восприятию человеком.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;случайное расположение символов&lt;/em&gt; - еще один способ усложнить работу
    программам, пытающимся прочитать текст.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;неравномерный фон&lt;/em&gt; - изобразив на фоне какой-либо абстрактный набор
    любых фигур, можно заставить программу-посетителя подумать что
    какая-то часть из них является символом. Например, пересечение двух
    прямых линий часто распознается как буква T или L. Неплохим
    вариантом является написание на фоне других символов другим цветом,
    сильно отличающимся от основного и близким к цвету фона.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Для начала этого вполне хватит, перейдем к реализации, в комментариях
постараюсь указывать все особенности:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateImage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// генерация изображения&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="o"&gt;=@&lt;/span&gt;&lt;span class="nb"&gt;imagecreatetruecolor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// создаем пустое изображение&lt;/span&gt;
   &lt;span class="nv"&gt;$mcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;imagecolorallocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// выбираем случайным образом&lt;/span&gt;
   &lt;span class="nv"&gt;$kcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;imagecolorallocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// несколько цветов&lt;/span&gt;
   &lt;span class="nv"&gt;$lcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;imagecolorallocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nv"&gt;$bcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;imagecolorallocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
   &lt;span class="nv"&gt;$fcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;imagecolorallocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
   &lt;span class="nb"&gt;imagefill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$bcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// заполняем изображение фоном&lt;/span&gt;
   &lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// список названий подходящих шрифтов&lt;/span&gt;
   &lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;  &lt;span class="c1"&gt;// наугад выбираем из них один&lt;/span&gt;
   &lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nb"&gt;imageline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$lcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// создаем на фоне несколько линий&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nb"&gt;imageline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$lcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// и еще несколько&lt;/span&gt;
   &lt;span class="cm"&gt;/*&lt;/span&gt;
&lt;span class="cm"&gt;   Генерируем текст: две строки на фон, а также интересующие нас символы по одному.&lt;/span&gt;
&lt;span class="cm"&gt;   */&lt;/span&gt;
   &lt;span class="nb"&gt;imagettftext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$kcol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"./font/"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;".ttf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;randomString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
   &lt;span class="nb"&gt;imagettftext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$mcol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"./font/"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;".ttf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;randomString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$istring&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nb"&gt;imagettftext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;0.73&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$fcol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"./font/"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;".ttf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nb"&gt;imageline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$mcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// еще линии&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nb"&gt;imageline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$mcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// и еще немного&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nb"&gt;imageline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$fcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// и еще чуть-чуть&lt;/span&gt;
   &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type: image/png'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nb"&gt;imagepng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nb"&gt;imagedestroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;randomString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// генерируем случайный набор символов заданной длины&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$list&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ!@#$%^&amp;amp;**()-=_+.,&amp;lt;&amp;gt;/\|;:"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="o"&gt;.=&lt;/span&gt;&lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;mt_rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Стоит заметить, что конкретные цифры необходимо подбирать индивидуально,
в примере они указаны абсолютно произвольно. Использование конкретно
этих же цифр приведет к далеко не самым лучшим результатам.&lt;/p&gt;
&lt;h3 id="sborka"&gt;Сборка&lt;/h3&gt;
&lt;p&gt;Не стоит забывать, что помимо генерации самого изображения, необходимо
передать написанный текст другому скрипту, который будет сверять данные.
Удобнее всего это делать через глобальный массив &lt;code&gt;$_SESSION&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Собрав все написанное выше, и учтя передачу текста, можно получить
следующий класс:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Captcha&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// генерируемый текст&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$bgcol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// основной цвет фона&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$fgcol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// основной цвет текста&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// высота изображения&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ширина изображения&lt;/span&gt;
   &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bgcol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$fgcol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// конструктор, вызывается при создании экземпляра класса&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$fgcol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$bgcol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;generateSymbols&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;generateImage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateImage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// генерация изображения&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="o"&gt;=@&lt;/span&gt;&lt;span class="nb"&gt;imagecreatetruecolor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// создаем пустое изображение&lt;/span&gt;
      &lt;span class="nv"&gt;$mcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;imagecolorallocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// выбираем случайным образом&lt;/span&gt;
      &lt;span class="nv"&gt;$kcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;imagecolorallocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// несколько цветов&lt;/span&gt;
      &lt;span class="nv"&gt;$lcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;imagecolorallocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nv"&gt;$bcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;imagecolorallocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;bgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="nv"&gt;$fcol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;imagecolorallocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;fgcol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="nb"&gt;imagefill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$bcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// заполняем изображение фоном&lt;/span&gt;
      &lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// список названий подходящих шрифтов&lt;/span&gt;
      &lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;  &lt;span class="c1"&gt;// наугад выбираем из них один&lt;/span&gt;
      &lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;imageline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$lcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// создаем на фоне несколько линий&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;imageline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$lcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// и еще несколько&lt;/span&gt;
      &lt;span class="cm"&gt;/*&lt;/span&gt;
&lt;span class="cm"&gt;      Генерируем текст: две строки на фон, а также интересующие нас символы по одному.&lt;/span&gt;
&lt;span class="cm"&gt;      */&lt;/span&gt;
      &lt;span class="nb"&gt;imagettftext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$kcol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"./font/"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;".ttf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;randomString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="nb"&gt;imagettftext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$mcol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"./font/"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;".ttf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;randomString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$istring&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;imagettftext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;0.73&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$fcol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"./font/"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;".ttf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;imageline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$mcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// еще линии&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;imageline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$mcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// и еще немного&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;imageline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$fcol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// и еще чуть-чуть&lt;/span&gt;
      &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type: image/png'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nb"&gt;imagepng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nb"&gt;imagedestroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$im&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateSymbols&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="c1"&gt;// генерация четырех цифр&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;string&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;leadingZero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;leadingZero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// дополнения числа num лидирующими нулями&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;                        &lt;span class="c1"&gt;// до длины length&lt;/span&gt;
      &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;strrev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$num&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="o"&gt;.=&lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;strrev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;randomString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// генерируем случайный набор символов заданной длины&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$list&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ!@#$%^&amp;amp;**()-=_+.,&amp;lt;&amp;gt;/\|;:"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="o"&gt;.=&lt;/span&gt;&lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;mt_rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Слегка доработав его и приведя в более подходящий вид, можно добиться
генерации изображений, выглядящих например вот так:&lt;/p&gt;
&lt;p&gt;&lt;img alt="CAPTCHA Sample" class="responsive-img" src="https://www.insight-it.ru/images/captcha-sample.png" title="Пример получающегося изображения"/&gt;&lt;/p&gt;
&lt;p&gt;Специально не выкладываю уже доведенный до ума класс, чтобы у читателей
не возникало желания просто взять и воспользоваться им, это приведет
лишь к очередной серии captcha-клонов.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Sun, 13 Jan 2008 22:30:00 +0300</pubDate><guid>tag:www.insight-it.ru,2008-01-13:php/2008/zashhita-internet-resursov-v-kartinkax/</guid><category>captcha</category><category>OCR</category><category>online</category><category>PHP</category><category>защита интернет-ресурсов</category><category>интернет</category><category>класс</category><category>код</category><category>кодинг</category><category>ООП</category></item><item><title>Откуда возникает спам и как с ним бороться</title><link>https://www.insight-it.ru//security/2008/otkuda-voznikaet-spam-i-kak-s-nim-borotsya/</link><description>&lt;p&gt;На сегодняшний день далеко не каждый пользователь &lt;a href="/net"&gt;Сети&lt;/a&gt; является
человеком, возможно это покажется странным для не интересующегося ИТ
читателя, но существует множество программ, способных передвигаться по
сайтам, следуя по гипер-ссылкам, как внутри одного сайта, так и переходя
с одного сайта на другой (в целом такой тип программ называется
&lt;a href="/tag/spider/"&gt;spider&lt;/a&gt; или &lt;a href="/tag/crawler/"&gt;crawler&lt;/a&gt;). Такие программы
могут иметь совершенно разное предназначение, самый распространенный
пример: поисковые системы используют &lt;a href="/tag/crawler/"&gt;crawler&lt;/a&gt;'ов для
пополнения своих индексов, но, к сожалению, далеко не все программы
этого класса написаны для благих целей.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h3 id="good-vs-evil"&gt;Good vs Evil&lt;/h3&gt;
&lt;p&gt;Большая часть "хороших" &lt;a href="/tag/spider/"&gt;spider&lt;/a&gt;'ов используется лишь для
сбора информации о сайте и следуют пожеланиям владельцев сайтов,
оставленных ими в специальном файле под названием &lt;code&gt;robots.txt&lt;/code&gt;, либо
внутри &lt;a href="/tag/html/"&gt;HTML&lt;/a&gt;-разметки с помощью специально предназначенных
для этого тэгов (этот механизм выходит за рамки данного повествования,
так что позволю себе его пропустить, оставив как тему для отдельного
разговора).&lt;/p&gt;
&lt;p&gt;Но даже сбор информации во время автоматизированного путешествия
программы по сайтам можно использовать в корыстных целях - на многих
сайтах люди размещают свою контактную информацию для тех или иных целей,
и некоторые сайты эту информацию "публикуют". &lt;a href="/tag/spider/"&gt;Spider&lt;/a&gt;,
настроенный на сбор контактной информации (в основном адресов
электронной почты и номеров ICQ и прочих служб обмена сообщениями) может
в очень сжатые сроки насобирать длинный список адресов, пригодный,
например, для рассылки нежелательной рекламы, в простонародье называемой
&lt;em&gt;&lt;a href="/tag/spam/"&gt;спам&lt;/a&gt;&lt;/em&gt;. Избежать попадания своей контактной информации в
такие списки относительно просто - достаточно лишь следить за тем, чтобы
она либо не публиковалась, либо была защищена любым из простейших
способов защиты от такого рода программ, начиная от банального
требования регистрации для просмотра контактных данных пользователей,
заканчивая выводом адресов через изображения или шифрование посредством
&lt;em&gt;JavaScript&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Среди прочих функций, которые может выполнять такого рода программа,
одной из наиболее часто используемых является возможность заполнения
такой неотъемлемой составляющей практически любого сайта, как &lt;em&gt;формы&lt;/em&gt;.
Имея возможность заполнения существенно б&lt;em&gt;о&lt;/em&gt;льшего количества форм в
единицу времени, чем человек, такие программы служат основным источником
&lt;em&gt;&lt;a href="/tag/spam/"&gt;спама&lt;/a&gt;&lt;/em&gt; в гостевых книгах, форумах и блогах. Еще одним из
возможных применений автоматического заполнения форм является
регистрация на множестве интернет-ресурсов с целью получения какой-либо
выгоды, например регистрация сайтов в каталогах. Помимо этого
&lt;a href="/tag/crawler/"&gt;crawler&lt;/a&gt; перемещается по сайту с относительно высокой
скоростью, что резко увеличивает нагрузку на &lt;a href="/tag/server/"&gt;сервер&lt;/a&gt;,
особенно при недостаточно оптимизированном движке сайта и/или недостатке
ресурсов сервера, выделяемых на выполнение скриптов сайта.&lt;/p&gt;
&lt;h3 id="zashchita-form-ot-avtomaticheskogo-zapolneniia"&gt;Защита форм от автоматического заполнения&lt;/h3&gt;
&lt;p&gt;Наверняка многие из вас раньше слышали термин &lt;a href="/tag/captcha/"&gt;CAPTCHA&lt;/a&gt;,
но боялись спросить: что же он значит? Как не трудно догадаться этот
термин является аббревиатурой :). Расшифровывается она как
"&lt;strong&gt;C&lt;/strong&gt;ompletely &lt;strong&gt;A&lt;/strong&gt;utomated &lt;strong&gt;P&lt;/strong&gt;ublic &lt;strong&gt;T&lt;/strong&gt;uring test to tell
&lt;strong&gt;C&lt;/strong&gt;omputers and &lt;strong&gt;H&lt;/strong&gt;umans &lt;strong&gt;A&lt;/strong&gt;part". Для меня до сих пор остается
загадкой по какому принципу выбирались слова для составления этой
аббревиатуры, наверное тупо случайным образом :). Смысл этой фразы в
переводе на русский можно передать как "полностью автоматический способ
отличить человека от компьютера". Конечно же имеется ввиду не внешние
различия, а особенности их поведения на просторах сети Интернет. В роли
"компьютера" в данном случае как раз выступают программы, о которых шла
речь в самом начале. Эта технология позволяет владельцам сайтов,
желающих исключить (по крайней мере чисто теоретически, на практике же -
минимизировать) посещение своего ресурса "плохими" "компьютерами",
крайне затруднить их использование.&lt;/p&gt;
&lt;p&gt;В основе этой технологии лежит тот факт, что у программ в большинстве
случаев отсутствует даже какое-либо подобие образного мышления - они
следуют заранее четко определенному алгоритму. Существует множество
вариантов реализации защиты сайта с использованием этого недостатка
компьютерных программ, но все они представляют некоторую проверку,
предлагаемую пользователю и стремящуюся к удовлетворению следующего ряда
требований:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Современные компьютеры не должны иметь возможности точно ее пройти.&lt;/li&gt;
&lt;li&gt;Она должна быть "по зубам" большинству людей.&lt;/li&gt;
&lt;li&gt;Не должна полагаться на тот факт, что потенциальный "злоумышленник"
    просто не знаком с принципом работы данной проверки.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Более подробно о возможностях этой &lt;a href="/tag/tekhnologiya/"&gt;технологии&lt;/a&gt; можно
узнать, прочитав &lt;a href="https://www.insight-it.ru/security/2008/7-sposobov-zashhitit-svoj-internet-resurs-ot-nezhelatelnoj-informacii/"&gt;запись о нескольких вариантах ее реализации&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Thu, 03 Jan 2008 20:25:00 +0300</pubDate><guid>tag:www.insight-it.ru,2008-01-03:security/2008/otkuda-voznikaet-spam-i-kak-s-nim-borotsya/</guid><category>captcha</category><category>crawler</category><category>online</category><category>PHP</category><category>spider</category><category>защита интернет-ресурсов</category><category>интернет</category><category>проверка</category><category>Сеть</category><category>спам</category><category>электронная почта</category></item></channel></rss>