Блоги

Обработка множества исключений: решение


Немного позже, чем думал (что простительно, ибо дни выдались хлопотные) привожу свои варианты решения упражнения, которое я озвучил в предыдущем посте. Условие повторять не буду, по приведенной ссылке все описано.

Решение 1, читерское.

В условии было сказано, что надо обрабатывать исключения в методах init(), run() и shutdown(). И хотя это явно не оговаривалось, по логике функционирования эти методы вызываются подряд. Следовательно, можно сделать так:

Ну или, чтобы не нарушать инкапсуляцию, этот код можно завернуть в статический метод в ApiDispatcher.

Достоинства решения:

  • простота;
  • минимум дублирования кода;
  • локальность модификации кода.

Недостатки:

  • жестко привязываемся к сценарию последовательного вызова методов init(), run(), shutdown(), в реальной жизни может потребоваться вызывать их из разных частей приложения в разные моменты времени;
  • метод-обработчик исключений должен очень многое знать обо всех возможных исключениях, при появлении нового исключения придется править метод-обработчик.

Решение 2, логичное.

Следующее решение приходит в голову практически сразу после прочтения условия, и оно действительно хорошо подходит для решения задачи.

Именно его в комментариях предложил Vladimir Rusinov, за что ему спасибо:

Достоинства решения:

  • простота,
  • независимость от мест вызова методов (то есть, их не обязательно вызывать подряд),
  • минимум дублирования кода,
  • локальность правок.

Недостатки решения:

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

Решение 3, хитрое.

Это решение в конечном итоге я и использовал в своем проекте. Сначала я приведу код, а потом дам дополнительные комментарии:

Здесь я явно использую класс Errors, в котором задефайнены константами все возможные коды ошибок. Кроме того, в нем есть метод, который позволяет по коду ошибки получить его текстовую расшифровку.

В PHP все исключения имеют по умолчанию два поля (есть и другие, но они нас не интересуют): $message = null и $code = 0, сообщение и код ошибки соответственно. Во всех наших исключениях мы переопределяем значение по умолчанию для кода ошибки, чтобы оно соответствовало типу исключения.

Теперь, когда мы создаем исключение, мы можем задать ему сообщение, которое описывает причину его возникновения, и, если надо, можем уточнить и код ошибки. Если же мы сообщение вручную не зададим - оно будет получено через класс Errors. В случае, если мы поймаем "не наше" исключение (типа простого Exception), у него код ошибки будет 0, и класс Errors успешно опишет его как "Unknown exception".

Достоинства решения:

  • гибкость;
  • простота добавления новых исключений (не надо модифицировать метод-обработчик ошибок),
  • независимость от мест вызова методов (то есть, их не обязательно вызывать подряд),
  • минимум дублирования кода.

Недостатки решения

  • требуется дополнительная логика для прерывания обработки, если упадет init(), либо проброс исключения дальше,
  • нелокальность правок при внедрении решения (надо проставить коды ошибок всем существующим исключениям),
  • менее тривиальное решение,
  • возможны сложности в случае, если есть исключения, которым нельзя переопределить код ошибки.

Итоги

Я привел целых три решения задачи и видно, что ни одно из них не является безоговорочно правильным. А это значит, что даже такая простая задача, как описано в этом упражнении, требует внимательного анализа требований к системе и ваших возможностей, как ее разработчика. В моем случае решение 3 оказалось наименьшим злом, и я использовал его. Но запросто может оказаться, что в другой ситуации оптимально "наивное" решение 1.

Упражнение: обработка множества исключений


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

Есть корневой класс, условно назовем его ApiDispatcher, который управляет основным потоком исполнения. В нем есть три основных метода:

  1. init() — инициализирует, необходимые для обработки запроса объекты.
  2. run() — запускает обработку бизнес-логики.
  3. shutdown() — освободжает ресурсы.

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

Так же будем считать, что нам на любом этапе доступен метод Client::reportError($error_code, $error_message), который обеспечивает вывод клиенту сообщения об ошибке и прекращение работы программы.

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

Естественно, решение "в лоб" не подходит, поскольку оно неизбежно приведет к большому количеству дублирующего кода.

Как бы вы решили такую задачу? Языки решения принимаются любые в пределах разумного: Java, PHP, C++. Не возбраняется использование языко-специфичных конструкций, если они действительно удобны — расширение кругозора никому лишним не будет ;-)

Я нашел два приемлемых решения, и опишу оба завтра, если их никто не назовет до меня.

Три top-подобных утилиты, которые должны быть на каждой Linux-машине


Практически всем пользователям знакома утилита top, показывающая интерактивный список процессов, отсортированный по нагрузке на процессор. История этой утилиты идет от 1984 года, когда Уильям ЛеФевр написал такую утилиту для BSD 4.1. С тех пор top или его аналог есть практически в каждой UNIX-подобной ОС.

Годами доказав свою практичность, top вдохновил многих других программистов на разработку похожих утилит, относящихся к разряду must-have на любой Linux-машине, поскольку они дают возможность быстро оценить ситуацию в системе.

htop

htop — это логическое развитие top. Его интерфейс сделан с помощью ncurses, и благодаря этому он предоставляет гораздо больше возможностей по визуализации процессов и общей загрузки системы, а так же значительно более интуитивный интерфейс настройки всего этого.

iotop

Эта замечательная утилита выручит вас, когда надо выяснить, кто же это так активно пишет на диск, что все остальные процессы едва ли не колом стоят: она выводит список процессов, отсортированный по скорости чтения/записи на диск. Полезно запускать ее с ключом -o, тогда она не будет засорять вывод процессами, которые на диск ничего не пишут.

iftop

Эта утилита аналогична предыдущей, но показывает она загрузку сетевого интерфейса. Надо заметить, что в отличие от двух предыдущих программ, она для работы требует прав root'а, и для запуска ей желательно указать имя интерфейса, который ей надо мониторить с помощью ключа -i.

Итог

Все три утилиты (htop, iotop и iftop) позволяют легко и быстро оценить положение дел на вашей машине даже в условиях отсутствия иксов или когда система бодро закапывается в своп и нужно срочно выяснить, почему: в такой ситуации дожидаться запуска графической утилиты — смерти подобно.

PS. Star One — Earth That Was

Аспекты реализации нового движка блога


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

Технические требования

  • Язык программирования: PHP 5.1+
    В принципе, я мог бы себе позволить и использовать PHP 5.3, мигрировав блог на мою VPS, но я не вижу в этом большой необходимости, тем более, что хостинг от EOMY за много лет показал себя с лучшей стороны.
  • СУБД: MySQL.
    Здесь выбор тоже практически очевиден: MySQL есть на любом хостинге и мне привычно с ним работать.
  • Фреймворк: Yii Framework
    Выбор фреймворка — тема не простая, так что ее я прокомментирую отдельно.
  • Миграция и обратная совместимость: весь контент должен быть перенесен и адаптирован к новому движку, все ссылки в постах должны остаться действующими, со старых URL постов должен быть редирект на соответствующие новые.

Yii Framework

На самом деле, для меня выбор фреймворка практически очевиден, поскольку я уже имел опыт разработки довольно сложного сайта на Yii Framework и он показал себя с лучшей стороны.

Помимо этого есть еще плюсы в его копилку:

  • Фреймворк сам по себе написан очень хорошо и работает быстро
  • Фреймворк быстро развивается и не отягощает себя поддержкой устаревших технологий (камешек в сторону CakePHP)
  • Есть внушительный централизованный репозиторий расширений, которые реализуют наиболее сложные фитчи "из коробки".
  • Наконец, есть базовый скелет блога на Yii, на написании которого построен официальный тьюториал. Кроме того, я нагуглил довольно много попыток развить эту основу до состояния нормального движка, которые надо внимательно изучить и, возможно, взять за основу.

Этот вечер я посвятил просмотру репозитория расширений и нашел там много полезного для себя и будущего движка :-)

С чего начать?

Перечислю для истории все проекты, которые я рассматриваю как потенциальную базу для движка:

  • Bliig — произвел на меня наиболее положительное впечатление, судя по всему более-менее серьезная разработка.
  • YiiBlog — развитие Yii Blog Demo с официального сайта. Суда по всему, автор, как и я, затачивает его исходя исключительно из собственных пожеланий, которые немного расходятся с моими. Но все же он заслуживает рассмотрения, хотя и во вторую очередь.
  • Yii BlogDemo Extended — другая попытка развить демо-приложение блога. К сожалению, развитие не имеет четкого направления и поэтому в нем появилась такая глупость как флеш-часы. Кроме того, проект уже год никак не развивается, поэтому он пригоден разве что для подсматривания идей решения разных задач, но не как полноценная основа.
  • YiiApp — попытка содать универсальный каркас приложения на Yii. Попытка, судя по всему, достойная, но для меня не очень подходящая.
  • yay-cms — простая CMS на базе Yii. В качестве основы для блога подходит плохо, но в отношении создания обычных сайтов проект интересный, поэтому заслуживает упоминания.

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

Внимательное изучение этих проектов мне, видимо, еще предстоит, но я вижу два наиболее вероятных варианта:

  1. Взять за основу Bliig и танцевать от него.
  2. Взать за основу официальное демо, а найденные проекты использовать как шпаргалки для подсматривания решений.

Open Source

Я до сих под еще не принял окончательного решения, но скорее всего движок будет приватным по двум причинам:

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

Техзадание для движка блога


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

За эти три года я достаточно много работал с Drupal'ом и могу с чистой совестью подтвердить: из него можно построить сайт абсолютно любой сложности и с любым мыслимим и немыслимым функционалом. Засада лишь в том, что такая мощь мне на этом блоге не нужна, тем более, что за нее приходится платить тяжеловесностью сайта. Поэтому в качестве отправной точки я решил составить список требований которые я предьявляю к движку блога. Этому и будет посвящен пост.

Общее назначение движка

Движок предназначен для ведения (преимущественно) однопользовательских персональных блогов.

Пользовательская система

  • Движок должен поддерживать многопользовательскую модель.
  • Контроль доступа с помощью механизма ролей. Набор ролей фиксирован: админ, блоггер, комментатор.
  • Анонимный пользователь имеет роль комментатора.
  • Желательна поддержка авторизации как по логину-паролю, так и через сторонние сайты (Twitter, Google, Open ID). Оптимально, если будет возможность привязки к одной учетке нескольких методов авторизации.

Размещение постов и создание статических страниц

  • Движок должен позволять публиковать посты и статические страницы, с разметкой в HTML.
  • Пост включает в себя следующие параметры:
    • Заголовок поста
    • Тело поста
    • Дата публикации
    • Автор
    • Состояние: опубликован или черновик
    • Имя поста для URL
    • Поле для традиционного видеоролика в конце поста
    • Теги
  • Визуальный редактор CKEditor полным набором панелей инструментов.
  • Загрузка файлов и изображений через интерфейс редактирования поста. Желательна интеграция с CKEditor.
  • Создание миниатюр изображений. Желательно иметь возможность задавать размер миниатюр, если "умолчальный" размер не подходил, либо просто отключать миниатюры для конкретных изображений.
  • Планирование постов. Если у поста статус "опубликован", но дата публикации — в будущем, то он не будет отображен до наступления даты публикации.
  • Блоггеры должны видеть неопубликованные записи, все остальные - нет.
  • Черновики и отложенные записи должны визуально отличаться от обычных постов и друг от друга.
  • Включение/отключение комментирования для конкретных постов.
  • Желательно автосохранение черновиков как в WP.
  • Посты должны быть доступны через RSS.
  • Разделение на тизер и основной текст не требуется.
  • Поддержка размещения листингов кода с соответствующей подсветкой.

Комментирование

  •  Возможность комментировать посты и страницы, для которых это не было запрещено.
  • Желательна поддержка древовидных комментариев
  • Комментатор может указать свой ник, сайт и email, все, кроме ника - не обязательно.
  • Желательна возможность логиниться прямо при комментировании.
  • Комментатору доступен ограниченный набор html тегов.
  • Форма добавления комментария имеет визуальный редактор с набором инструменов, ограниченным набором разрешенных тегов.
  • В форму комментирования встроен антиспам, невидимый для пользователей со включенным JavaScript.
  • Возможна подписка на комментарии по email.
  • Для блоггеров каждого комментария должны быть дополнительные опции:
    • Редактировать
    • Удалить
    • Удалить ссылку на сайт комментатора (для упрощения выноса спамовых ссылок)
    • Удалить все ссылки из текста поста.
  • Желательно иметь возможность детектирования скрытых ссылок в комментариях.
  • Желательно экспортировать комментарии в RSS

Многоязычность

  • Интерфейс должен поддерживать как минимум два языка: английский и русский.
  • Весь контент может быть привязан к одному из языков. Если привязка не задана, он отображается для любого языка.
  • Комментарии наследуют языковую привязку от поста или страницы, к которой они относятся.

Интерфейс

  • Шаблон должен быть отделен от логики и быть максимально простым.
  • Должна быть поддержка управления верхним меню и меню в сайдбаре.
  • Сайдбар справа.
  • Следующие виджеты должны присутствовать в сайдбаре:
    • Редактируемое меню
    • Облако тегов
    • Блогролл
    • Архив по месяцам
    • Последние комментарии и посты
  • Комментарии располагаются под постом.
  • Форма добавления комментария - под всеми комментариями.
  • У каждого комментария есть кнопки "Ответить" и "цитировать выделенное".
  • На главной и на страницах отображения тегов должно присутствовать разбиение на страницы.
  • Поддержка вида для печати
  • Желательно редактирование постов и комментариев в упрощенном режиме с помощью ajax
  • Желательно удаление комментариев через ajax
  • Интерфейс должен приемлемо вести себя при отключенном JS или CSS

Требования к ядру

  • Кэширование всего, чего можно.
  • Желательна оптимизация CSS и JavaScript.
  • Желательно правильное и автоматическое расставление мета-тегов , и
  • Генерация xml-карты сайта, содержащей ссылки на:
    • Главную
    • Посты
    • Статические страницы
    • Страницы тегов

Итог

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