Пример разработки блога на Zend Framework 2. Часть 3. Работа с пользователями

Это третья (последняя) часть статьи, посвященной разработке простого приложения при помощи Zend Framework 2. В первой статье я рассмотрел структуру ZendSkeletonApplication, во второй части привел пример разработки простого модуля. Эта часть посвящена работе с пользователями.

Работа с пользователями

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

Zf Commons

Для Zend фреймворка написано достаточно много модулей, решающих стандартные задачи, найти их можно на специальном сайте: http://modules.zendframework.com/. Вместо разработки своих велосипедов для решения стандартных задач я считаю более правильным использовать/адаптировать под себя готовые решения (по крайней мере готовые решения нужно изучить прежде чем браться за разработку велосипеда).

Среди множества разработчиков модулей выделяется команда ZF Commons, ребятами из этой команды разработан ряд очень полезных модулей, которые мы будем использовать в этом проекте: https://github.com/ZF-Commons. Рассмотрим некоторые из них, которые необходимы нам на данном этапе.

Читать дальше ➠

Пример разработки блога на Zend Framework 2. Часть 2. Модуль MyBlog

Это вторая из трех частей статьи, посвященной разработке простого приложения при помощи Zend Framework 2. В первой статье я рассмотрел структуру ZendSkeletonApplication, а в этой части приведу пример разработки простого модуля. Третья часть будет посвящена работе с пользователями.

Установка и настройка дополнительных модулей

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

  1. добавляем соответствующую строчку в composer.json, чтобы сообщить Композеру о новом модуле,
  2. выполняем команду php composer.phar update, чтобы Композер загрузил новый модуль и при необходимости перегенерировал автолоад файлы,
  3. добавляем новый модуль в список modules в файле config/application.config.php,
  4. при необходимости, размещаем конфигурационный файл модуля (обычно пример такого файла находится в папке config модуля) в config/autoload и делаем в нем необходимые правки.

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

Давайте начнем с установки простого, но полезного модуля Zend Developer Tools.

Читать дальше ➠

Пример разработки блога на Zend Framework 2. Часть 1. ZendSkeletonApplication

В последние несколько лет моя работа связана с использованием CMS Drupal, но на досуге я изучал и just for fun запускал проекты на питоновских фреймворках Django, Flask и Twisted. Сейчас я решил освоить основы двух-трех популярных PHP-фреймфорков и первыми я решил изучить Zend Framework 2 и Yii.

В процессе ознакомления с Zend Framework 2 я изучил туториал с официального сайта (http://framework.zend.com/manual/2.2/en/user-guide/overview.html), просмотрел документацию фреймворка (http://framework.zend.com/manual/2.2/en/index.html), прочитал книгу Michael Romer “Web development with Zend Framework 2” и собрал собственное тестовое приложение.

Переварив всю эту информацию, я пришел к мысли, что официальный туториал к фреймворку суховат:

  • в нем не рассказывается о работе с пользователями, сессиями и правами доступа,
  • лишь вскользь рассматривается такая основополагающая часть фреймворка как ServiceManager,
  • в качестве интерфейса с базой данных безальтернативно используется паттерн Table Gateway (и соответствующая его реализация в фреймворке),
  • используется встроенный в фреймворк шаблонизатор, который после питоновского Jinja 2 кажется совершенно неудобным и примитивным,
  • и т.д.

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

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

  • в качестве шаблонизатора будет использоваться Twig,
  • для работы с БД — Doctrine ORM,
  • для авторизации/аутентификации и распределения прав доступа я буду использовать существующие модули ZfcUser (https://github.com/ZF-Commons/ZfcUser) и BjyAuthorize (https://github.com/bjyoungblood/BjyAuthorize),
  • также я рассмотрю вопросы разработки собственных валидаторов форм, View плагинов и другие.

Статья написана новичком (в Zend Framework) для новичков, по этому приветствуется любая критика по делу и советы по усовершенствованию приложения.

Найти код проекта можно на Гитхабе: https://github.com/romka/zend-blog-example.

Статья будет разбита на 3 части:

  • в первой (текущей) части я рассмотрю структуру ZendSkeletonApplication,
  • во второй части разберу разработку собственного модуля (формы, работа с БД при помощи Doctrine ORM, разработка View-плагина),
  • третья часть будет посвящена работе с пользователями.

Итак, приступим.

Читать дальше ➠

Проблема с кодом вне функций в Drupal 6 и 7

Столкнулся с интересной проблемой, которую удалось повторить и в Drupal 6, и в Drupal 7. В модуле вне тела функции была объявлена константа:

define('TEST_CONST', drupal_get_path('module', 'test_module'));

(тут дело даже не в самой константе, а в использовании апишной функции drupal_get_path() вне тела функции).

Этот модуль долгое время корректно работал, до тех пор пока в нем не был объявлен hook_exit(). После этого события Друпал начал отдавать ошибку "PHP Fatal error: Call to undefined function drupal_get_path() in...", где вместо drupal_get_path() можно поставить почти любую функцию из Drupal API, которая была использована в коде, расположенном вне тела функций.

Поиск и исправление причин ошибки осложнился тем, что она стала проявляться не сразу после добавления hook_exit(), а спустя некоторое время.

Суть ошибки оказалась в следующем:

  • Друпал в таблице system, модулям которые реализуют hook_exit(), проставляет значение 1 в колонке bootstrap, по умолчанию этот параметр равен нулю.
  • В свою очередь модули отмеченные таким образом начинают загружаться Друпалом раньше остальных (на этапе DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE в случае Drupal 6) когда API, в частности файл common.inc, содержащий функцию drupal_get_path(), еще не загружен полностью.
  • Загрузка модуля подразумевает include файла модуля и, что логично, исполнение кода, находящегося вне тела функций.

Таким образом, после добавления hook_exit() модуль стал загружаться до полной загрузки API и функция drupal_get_path() на этом этапе действительно еще не была определена. Решилась проблема переносом проблемного кода в hook_init().

Возникла ошибка не сразу от того, что обновление поля bootstrap в таблице system делается не сразу, а при обновлении кеша списка модулей (вызов функции module_rebuild_cache() в Drupal 6). Этот кеш очищается, например, при нажатии кнопки “Сохранить” на странице списка модулей или при срабатывании модуля Update status.

Похвастаюсь статистикой в день сообщения о разводе Путина

Средняя нагрузка на сайт днем в рабочий день 1000 запросов в минуту (масимум 30-40 в секунду), около 200 тысяч уников в день и около 800 тысяч просмотров. Трафик отдается на скорости до 10 мегабайт в секунду.

В пиках нагрузка раньше доходила до 4 тысяч запросов в минуту (до 120 в секунду), около 350 тысяч уников в день и 2,5 млн. просмотрв. В пиках трафик отдавался на скорости до 50 мегабайт в секунду.

Пятница, 7 июня. В пике сайт отдавал больше 12 тысяч страниц в минуту (250 в секунуду) на скорости около 110 мегабайт в секунду.

visitors Traffic by minutes First frontend Second frontend

Drupal + Varnish отработали на отлично. С учетом нашего канала и железа сайт может держать вдвое большую нагрузку подобного плана.

Интеграция системы статистики Piwik с Drupal 6

При работе над Drupal-проектом у меня возникла необходимость сбора детальной статистики о просмотрах материалов и вывода этой статистики в виде блоков, отображающих самые просматриваемые материалы за последние день, неделю и месяц. Стандартный друпальский модуль Statistics (и в шестом, и в седьмом Друпале) для решения такой задачи абсолютно неприменим:

  • во-первых он осуществляет сбор статистики на hook_exit() и не будет работать корректно со сколько-нибудь серьезными системами кеширования, например при использовании Varnish или модуля Boost.
  • во-вторых модуль Statistics собирает статистику за всё время существования материала, сделать выборку вида: “сколько раз был просмотрен материал за последний день” с этим модулем невозможно. По этому при использовании этого модуля исходную задачу пришлось бы модифицировать к виду: вывести список самых просматриваемых материалов созданных за последние день, неделю или месяц, но этот подход неприемлем, так как на сайте существуют материалы старше месяца, способные попасть в топ по просмотрам.

Таким образом, возникла необходимость внедрить полноценную систему статистики и интегрировать ее с Друпалом. Под полноценными системами я понимаю сервисы типа Google Analytics или Яндекс.Метрика, используя API которых можно запросить статистику просмотров материалов за любой диапазон дат. Достоинством этих сервисов является то, что они у себя хранят всю статистику, а она на посещаемом ресурсе может занимать немало места, их недостаток — все таки это внешние сервисы и неизвестно, что с ними случится завтра: сломаются, станут платными, не захотят внедрять нужную пользователю фичу и т.д.

В итоге я решил установить на собственном сервере open source систему статистики Piwik и интегрировать ее с Друпалом. Да, Пивик пока местами уступает GA и Я.М в гибкости отчетов (хотя есть в нем и отчеты, которых нет ни в ГА, ни в Я.М), но, во-первых, нужную мне задачу он решает, во-вторых он постоянно развивается своими разработчиками и может быть расширен с помощью самописных плагинов.

Для седьмого Друпала есть замечательный модуль Piwik stats, интегрирующий Друпал с Пивиком. Суть модуля состоит в том, что он создает специальное поле, которое может быть прикреплено к любой entity (материалу, пользователю). В настройках поля задается параметр, отвечающий за то, статистика за сколько дней должна быть в этом поле. Одна entity может содержать несколько полей с разными настройками, то есть, например, каждый материал может содержать три поля с информацией о просмотрах за последние день, неделю, месяц, как раз то что мне и требовалось.

Заполнение полей данными осуществляется через Drupal Queue API по крону, но при желании стандартный механизм обработки очереди может быть заменен чем угодно, например Rabbit MQ.

Плюсом хранения статистики в полях является то, что статистика становится прозрачно интегрированной со всем модулями Друпала, например с Views, с помощью которого, в том числе, можно строить блоки с контексто-зависимой статистикой. То есть, например, на главной странице сайта выводить список самых популярных статей со всего сайта, а на странице статьи или раздела — выводить самые популярные статьи текущего раздела.

Основная проблема для меня состояла в том, что модуль Piwik stats работает только под седьмым Друпалом и его разработчик не планировал делать бэкпорт под шестерку, а проект, в котором мне понадобился этот функционал, работает как раз на древней шестой версии Друпала. По этому я сам сделал бэкпорт этого модуля под шестерку. Забрать мою версию модуля можно из sandbox-проекта Piwik_stats, обсуждение этого бэкпорта с автором основного модуля тут: http://drupal.org/node/1736398.

Пока мейнтейнер модуля не хочет делать мою версию модуля официальной, из-за того, что требуется сделать детальный code review. Тем не менее, мой модуль без нареканий больше месяца работает в бою на двух Друпал 6 проектах.

Typo — Drupal-модуль для борьбы с опечатками на сайте

Разработал новый модуль для Drupal 7, который позволяет пользователю выделить мышью найденную в тексте опечатку и нажатием Ctrl + Enter отправить сообщение о ней администратору сайта. Модуль не зависит от внешних сервисов типа Орфуса и тесно интегрирован с популярными модулями Друпала такими как Rules, Token, Views и Views bulk operations.

Интеграция с модулями Rules и Token, позволяет, например, настроить отправку сообщений о найденных ошибках по е-мейлу, в системный журнал или вызвать любое другое действие, доступное в модуле Rules. С помощью токенов [typo:url], [typo:text] и [typo:comment] в текст сообщения можно включить информацию об опечатке.

Интеграция с Views позволяет сделать вывод списка ошибок на странице, в комплекте с модулем уже идет настроенное представление, а интеграция с Views bulk operations позволяет удалять из этого представления обработанные сообщения.

По умолчанию, все сообщения старше 3 дней автоматически удаляются, но это действие можно отключить в настройках модуля.

Popup-окно с формой отправки опечатки выводится модулем Ctools и его вид может быть изменён как правкой CSS-файла, так и правкой соответствующего tpl-файла. Ctools — это единственная зависимость модуля, остальные модули (Rules, Views, etc) нужны только если вы хотите использовать соответствующий функционал.

Скачать модуль можно на странице проекта: http://drupal.org/project/typo.

Испытать этот модуль вы можете прямо на этом сайте, список отправленных отчетов об опечатках доступен всем посетителям здесь: http://romka.eu/typo-reports (на реальном сайте к этому представлению анонимам лучше не давать).

upd Модуль упомянули в обзоре в блоге Cocomore, жду наплыва установок на агнлоязычных сайтах :)

Пример конфигурационного файла Varnish

Как обещал в докладе выкладываю пример и описание рабочего конфига для Варниша. Чтобы узнать подробнее о том, что такое Варниш и для чего он нужен ознакомьтесь с разделом Pressflow + Varnish.

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

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

Читать дальше ➠

Кеширование на Drupal-сайте. Сравнение встроенного в Drupal кеша, статического файлового кеша (модуль Boost) и Varnish

Публикую текст своего доклада для Друпалконфа, который прошел 4 июня 2012 года в Москве. Хочу акцентировать внимание на том, что этот текст не адаптирован под блогпост и публикуется в том виде, в котором я рассказывал его на конференции.

Введение

В текущем Drupal 6 проекте, над которым я работаю последние 2 года, в пике мы отдаем до 2 млн просмотров страниц в день и забиваем полностью наш 200-мегабитный интернет-канал. Судя по отчетам нашей системы мониторинга с текущей архитектурой и железом (6 серверов: 2 фронтэнда с nginx, 2 бэкенда с Varnish + Apache + Drupal и 2 MySQL-сервера с master-slave репликацией) мы можем выдерживать втрое большую нагрузку, если решим вопрос с каналом.

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

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

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

Читать дальше ➠

Любопытное поведение модуля syslog в шестом Друпале (баг?)

Описание проблемы

На сайте используется Drupal 6 и модуль theme key, который позволяет задавать разный дизайн для разных страниц. Столкнулся с неприятной и трудноуловимой проблемой: есть 2 полностью идентично настроенных (как казалось изначально) сервера, на одном из которых theme key отрабатывает корректно, а на втором нет — все время отображает контент в дефолтной теме оформления. Похожая проблема также встречалась в другом проекте, не использующем theme key, на странице управления блоками.

После некоторых экспериментов удалось выяснить, что сервера отличаются настройкой PHP error_reporting. На глючащем сервере она была задана так:

error_reporting = E_ALL & ~E_DEPRECATED

на работающем так:

error_reporting = E_ALL & ~E_DEPRECATED & ~E_NOTICE

Обновление этой настройки на некорректно работающем сервере решало проблему. Может показаться что это магия какая-то, мне так изначально и показалось: ну какое отношение имеют сообщения, выводимые в лог, к работе модулей и выбору темы оформления Друпала? В итоге, оказалось, имеют самое непосредственное отношение. Ниже описание причины проблемы и её решение.

Читать дальше ➠