#14, Сентябрь'2005 :: Управление тестированием

Page 1

Сентябрь '05 No 14

Управление тестированием Интервью с Ильей Альшанетским FAQ: Как помочь в развитии PHP?

издается с февраля 2004 года, www.phpinside.ru


PHP Inside №14

Содержание В фокусе Исправление ошибок: в нужное время и в нужном месте. Часть I............................................3 Исправление ошибок: в нужное время и в нужном месте. Часть II ........................................10 Функциональное тестирование при помощи Selenium.............................................................16 Люди Интервью с Ильей Альшанетским..............................................................................................37 FAQ: Как принять участие в развитие PHP?..............................................................................41 Идеи Библиотека dbtree для работы с деревьями Nested Sets............................................................48 Введение в разработку веб-приложений с Ajax........................................................................ 59 Множественное наследование в ОО модели PHP..................................................................... 64

От редактора В первом осеннем выпуске PHP Inside мы затронем много различных тем. Темой номера "В Фокусе" является "Управление тестированием" и в рамках темы представлена статья об управлении ошибками, а так же, Сергей Юдин расскажет об инструменте тестирования веб-приложений "Selenium". Еще одним интересным моментом этого номера стало интервью с Ильей Альшанетским - активным разработчиком PHP и некоторых проектов, таких как FUDforum и eGroupware. В интервью он поведал нам о своем опыте работы релиз-менеджером PHP и рассказал о "кухне" выпусков новых релизов этого языка. Так же мы не смогли пройти мимо материалов, посвященных интересным и полезным технологиям и алгоритмам: Ajax и Nested Sets. Для сторонников Nested Sets мы публикуем пример работы с классом dbtree, написанный автором самого класса - Кузьмой Феськовым. Dbtree получает признание за рубежом (Мануель Лемос, ведущий проекта phpclasses.org сыграл в этом не последнюю роль) и теперь пришло время познакомить и наших соотечественников (тех кто еще не успел) с этим действительно мощным инструментом управления иерархическими структурами в PHP. И в завершении, Денис Баженов в статье "Множественное наследование в ОО модели PHP" рассказал о своих идеях реализации множественного наследования.

Команда номера

Авторы и переводчики Кузьма Феськов Андрей Олищук Сергей Юдин Денис Баженов Антон Довгаль Алексей Борзов Александр Войцеховский Григорий Федоринов

Редакционная коллегия Александр Смирнов Александр Войцеховский Андрей Олищук [nw] Антон Чаплыгин Елена Тесля

Выпуск номера Андрей Олищук [nw] Денис Зенькович Антон Чаплыгин Андрей Махнач Денис Бесков-Доронин


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть I

В фокусе Исправление ошибок: в нужное время и в нужном месте. Часть I Автор: Scott Berkun Перевод: Андрей Олищук Управление ошибками – это целая наука, азы которой преподает Скотт Беркун, автор книги по управлению проектами В работе с ошибками есть одно золотое правило: исправлять ошибки нужно в том порядке, который вероятнее всего приближает к успеху. Звучит банально, не правда ли? Неправда. Могу поспорить, что свыше половины «глючных» и ненадёжных программ, которые вам приходилось использовать, были такими не потому, что разработчикам не хватило времени сделать их лучше. Просто они исправляли не те ошибки. Желание исправить «нужные» ошибки и знание того, каким образом это сделать, -две разные вещи. При попытке продуманного подхода к работе с ошибками возникает две основные трудности: первая - необходимо знать, как принять правильное решение по поводу исправления той или иной ошибки, вторая - создание и поддержание рабочего процесса, который обеспечивал бы реализацию этих решений даже в условиях "дедлайна". Опытные лидеры и команды знают, что усталость непременно накапливается к концу проекта, этапа или итерации. Им наверняка придётся меньше спать, больше работать и в жутких количествах поглощать кафеиносодержащие продукты. Думая загодя, умные лидеры вводят в действие простые правила и создают неприкосновенные запасы ресурсов на ранних стадиях, и когда становится очень трудно, у таких лидеров всегда находятся резервы для принятия правильных решений. Это эссе, состоящее из двух частей, является своеобразным учебником для использования таких правил и неприкосновенных запасов. Даже больше — я поделюсь с вами идеями, на основании которых вы сможете создавать свои собственные правила. Мои рекомендации состоят из четырех уровней — от отрывочных советов по оказанию "первой помощи" (Уровень 1) до крупномасштабного планирования (Уровень 4). Но перед тем как начать, рассмотрим список неправильных подходов, которых следует избегать при принятии решений о правке ошибок. 3


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть I

Вот десять худших способов принятия решений: 1. Править только те ошибки, которые раздражают вашего директора; 2.

Править все ошибки (никогда не успеете);

3. Не править ошибок вовсе (зато успеете сдать систему уже сегодня!); 4. Править только те ошибки, которые раздражают жену/дочь/хомяка вашего босса; 5. Требовать согласования каждого решения самым надоедливым и недалёким человеком в вашей организации (возможно, пересекается с подходом №1); 6. Начать править случайно выбранную ошибку и, остановившись на полпути, перейти к другой. По завершению повторить; 7. Бояться ошибок. Не править их и свалить все на когонибудь другого; 8. Расставить ошибки в алфавитном порядке и править их от А до Я, исключая гласные. При соответствующем подходе к именованию ошибок этот подход может превратиться в №3; 9. Создать сложную избирательную систему представителей, выбираемых двумя третями большинства для написания проекта регламента по формированию трёх двусторонних комитетов, наделённых полномочиями по модерированию будущих дисскуссий на тему стратегического управления ошибками; 10. Тратить все имеющееся время на споры о том, похож ли ваш текущий подход на что либо из только что приведённого списка. Будем надеятся, что вы не сталкивались с описанным выше подходами на практике. Теперь, когда вы предупреждены, будет легче избежать неправильных решений в будущем. Как только ваш менеджер предложит нечто подобное, то пожалуйста встаньте, тихо повернитесь и бегите изо всех сил.

1. Первая помощь В лучших веб-мастерских и программистских цехах процесс управления исправления ошибок очень напоминает медицинскую очередность оказания помощи. Кто-нибудь берёт на себя основную роль по просмотру всех поступающих ошибок и раскладыванию их на три или четыре кучи. Это называется сортировкой, разбором ошибок или управлением дефектами. Как и в случае с любыми другими вещами, которые у вас возможно есть, например CD-дисками, книгами, долгами, подружками, — с ошибками лучше всего справляться, объединяя их в высокоуровневые группы. Таким образом становится легче понять ситуацию, обсудить ее с другими и найти подходящего квалифицированного "врача".

4


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть I

Это универсальное правило — гораздо проще работать с тремя-четырьмя группами, чем с сотнями или тысячами отдельных вещей. Поэтому, если ошибки ставят весь проект вверх дном, то первым делом необходимо отказаться от всех действий, сесть и начать сортировку по степеням важности. Тест: если вы не помните, когда в последний раз занимались разборкой ошибок, то остановитесь и немедленно приступите к разбору. Вы не можете просто взмахнуть волшебной палочкой над базой ошибок, чтобы они самостоятельно упорядочились. Здесь нужен смельчак (вроде вас), который не побоится грязной работы, засучит рукава и отсортирует их. Можете мне поверить — иначе не получится. Если вы достаточно дисциплинированы, то сможете проводить разборку регулярно, ежедневно в течение всего хода проекта, не позволяя ситуации ни на минуту выйти из-под контроля. Как вариант - вы можете попытаться мотивировать своих программистов систематически разбирать собственные ошибки. Это здорово, но какой бы подход не применялся, дело должно быть сделано. Прежде чем вы пропустите эти строки со словами «Я знаю о сортировке, но не делаю её по такой-то и такой-то причине», учтите, что сортировка необходима для оздоровления в процессе оказания первой помощи в любом из смыслов — медицинском или техническом. Нет смысла лепить лейкопластырь на коленку больного, если у него из спины торчит полдюжины отравленных стрел. Без сортировки вы не будете точно знать, на что потратить энергию команды наилучшим образом. В отличие от пациента, исступленными жестами привлекающего ваше внимание к своей спине с незваными стрелами, ваша база кода не скажет, где у нее болит больше всего. Вам нужно определить это самостоятельно. Усилия по сортировке важности ошибок имеют еще и несколько полезных последствий. Разбираясь с ошибками, лидер вынуждает всех улучшать своё видение проекта вцелом. Он обнаруживает множество дублирующихся ошибок, уже исправленные ошибки, нехватку данных (которые нужно отправить на уточнение тестерам), ошибки, которыми можно пренебречь, и даже нечто нелепое, вроде жалобы на то, что веб-сайт не предсказывает номера выигрышных лотерейных билетов. Практика показывает, что после первой сортировки количество ошибок обычно уменьшается на 30 процентов, что, конечно же, здорово прибавляет настроения. Однако вам не удастся так легко добиться цели без выполнения всей работы.

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

5


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть I

Согласно простейшей схеме, все ошибки можно разделить на три группы: “необходимо исправить”, “надо бы исправить”, “не нужно исправлять”. Перебирая ошибки, относите их к одной из этих категорий. При этом чем больше ошибок будет отнесено в «надо бы исправить» и «не нужно исправлять», тем более эффективной будет сортировка, поскольку эти группы представляют собой чёткие решения. Чего вам точно не нужно - так это чтобы 99 процентов ваших ошибок попадало в «необходимо исправить». Такой подход к сортировке называется «трусливым». Если все ошибки нужно исправить, то это равносильно тому, что они равнозначны, но это бессмысленно. Иными словами, вы просто сбежали от решения проблемы. Запомните золотое правило: чтобы добиться успеха, расставьте ошибки в порядке их приоритета. Если приоритет у всех ошибок один, значит, нет порядка и, следовательно, нет успеха. Сортируя ошибки, стремитесь к тому, чтобы 50 процентов попало в «необходимо исправить», а все остальное в «надо бы исправить» и «не нужно править». Затем остановитесь и серьезно взгляните на ошибки. Оцените оставшиеся ресурсы (время и людей) и решите, что наиболее важно для проекта (заказчиков и бизнеса). В конце проекта нет задачи важнее, чем активное и упреждающее управление ошибками и обработка проблемных ситуаций. Стремитесь к 50 процентам «необходимо исправить» и жмите, пока есть силы. И только когда дальнейшее сжимание станет действительно невозможным, знайте: вы провели настоящую сортировку. Врачи первой помощи не выбрасывают белый флаг и не просят таймаутов, когда им приходится выбирать, кого из пострадавших спасать в первую очередь. Если они могут делать ТАКОЙ выбор ради человеческих жизней, то уж вы точно справитесь с расстановкой приоритетов для ошибок. Не прячьтесь за невежеством «трусливой сортировки» — будьте тверды, упрямы и ведите свою команду вперед!

Правила очень простой сортировки Вот основные правила для трех групп ошибок: 1. Если ошибку «должны исправить», то это означает, что она важнее любой ошибки из группы «надо бы исправить» и «не будем править»; 2. Если ошибку «надо бы исправить», значит, она менее важна, чем любая из группы «должны исправить», и более важна, чем из группы «не будем править». 3. Если ошибку «не будем править», значит, она менее важна, чем любая другая ошибка в категории «надо бы исправить». Несомненно, все решения по поводу правки ошибок являются относительными. Здесь нет абсолютов. Определение степени важности включает много факторов, и отнести ошибку к той или иной группе может быть очень сложно.

6


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть I

Сообразительные команды создают четко определенные маркеры (критерии), которые помогают выработать правильное решение (мы вернемся к этим критериям во второй части). Не переживайте, что у вас будет много спорных моментов. Я гарантирую, что вызывать споры о правильности размещения будет минимальное количество ошибок в каждой группе. Сфокусируйте коллектив на позитивном, на тех ошибках, с важностью которых все согласны. Если наберется 50 таких ошибок, то пройдет несколько рабочих дней или недель до тех пор, пока возникнет необходимость вернуться к дебатам по поводу оставшихся. Группа ошибок «должны исправить» — вот над чем нужно работать программистам. Они ничего не должны трогать в «надо бы исправить», за исключением тех случаев, когда они помогают с сортировкой. Смысл прост: не трогай того, что может понадобиться, до тех пор, пока это не понадобится на самом деле. Другими словами — не беспокойся о десерте, если еще не ясно, что будет на обед. Всегда будет возникать соблазн прихватить ошибок для исправления из группы «надо бы исправить». Никоим образом нельзя отступаться от принятых ранее решений, даже если среди отодвинутых на второй план есть ошибки, которые раздражают всю команду, портят любимый функционал или просто очень интересны для исправления. Приоритеты должны стать самой главной вещью для проекта и заказчика. Приверженность команды целям проекта, а не другим желаниям — это и есть разница между качественными и низкопробными проектами.

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

Уровень 2. Более детальная сортировка За исключением тех случаев, когда вы правите ошибки по мере их поступления (хорошая, но на удивление редко применяемая стратегия), обычно все решения касательно ошибок принимаются уже в самом конце, когда наиболее велико давление сроков. Следовательно, вам необходимо иметь сведения по каждой ошибке в достаточном количестве, чтобы принять правильное решение быстро. Если вы тратите половину своего времени на то, чтобы воспроизвести ошибку или даже просто понять ее суть, значит, вы уже теряете даром драгоценное время.

7


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть I

Качественные описания ошибок и ситуаций, при которых они возникали, могли быть сделаны еще несколько дней или недель раньше. Все, что вам для этого нужно, — большая база ошибок, которая переливается всеми цветами радуги, хорошо организованный кабинет, а не населенный крысами и привидениями паутинчатый чердак. Программисты могут входить в него, говорить, чего им не хватает, и возвращаться к работе. База ошибок требует постоянного сопровождения и усердия со стороны тех, кто открывает ошибки. Чем выше качество информации в базе ошибок, тем меньше времени и сил вы потратите на сортировку, и тем больше ваша команда проведет времени в реальной правке ошибок. Внимание: качественное описание ошибки (или отсутствие такового) могут быть первым критерием для сортировки ошибок по их важности. Чтобы улучшить качество информации об ошибке, прежде всего необходимо детализировать группы, на которые эти ошибки делятся. Помимо групп (Должны/Надо бы/Не будем), нужно ввести еще два понятия: «Приоритет» и «Серьезность». Смысл приоритетов прост: вместо «Должны сделать» назовите это «Приоритет 1», вместо «Надо бы сделать» — «Приоритет 2», и вместо «Не будем делать» — «Приоритет 3». Некоторые команды идут еще дальше и добавляют приоритет 4. Таким образом, Приоритетом 3 становится «вероятно, не будем делать», а Приоритетом 4 — «Не будем делать, пока не остынет преисподняя, потом снова не запылает и опять не остынет». Я не встречал успешных команд, у которых градация приоритетов имела бы более четырех уровней. Создание 15 различных приоритетов — это совершенно лишняя затея. «Серьезность» определяет, насколько серьезной будет ошибка для заказчика, если она случится. Отделение серьезности от приоритетности исправления дает лучшее видение ошибки, так как появляется возможность разделить последствия выполнения ошибки от возможности ее появления. К примеру, перед вами может быть ошибка, которая приводит к взрыву монитора пользователя (Серьезность 1), но возникает она только после тройного клика на пункт меню при напевании австралийского национального гимна на немецком языке, что маловероятно (Приоритет 3). Чтобы это заработало, кто-то должен сесть и определить разницу между уровнями серьезности 1, 2 и 3. Хорошо, если при этом будут приводиться примеры реальных ошибок, чтобы люди могли выбрать правильный уровень. После этого, как только будет открываться новая ошибка, степень серьезности будет заполняться надлежащим образом. Кому-то, вероятно, вам, придется добавить степени серьезности и к старым ошибкам.

8


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть I

Вот пример системы определения серьезности. Рекомендую вашей команде собраться вместе и оценить следующее: • Уровень серьезности 1. Потеря данных. Заказчик теряет данные или серьезно повреждает их. Восстановление данных может быть невозможно или требуется переустановка приложения (или нажатие кнопки «обновить» в браузере»); • Уровень серьезности 2. Часть функционала не работает или работает с большими проблемами. Большая часть возможностей не работает или работает не так, как ожидалось, поэтому рабочие задачи нельзя решить или приходится искать обходные пути; • Уровень серьезности 3. Раздражение. Второстепенные функции работают не так, как ожидалось. Обходные пути существуют, но их сложно найти или они раздражают и разочаровывают. Используя эти два критерия (приоритет и серьезность), вы можете отсортировать оставшиеся ошибки более мудро. Вместо того, чтобы работать просто с тремя большими группами ошибок, вы можете задать дополнительные уточняющие вопросы и отсортировать ошибки внутри каждой группы в зависимости от того, насколько они серьезны. Это один из быстрых способов расставить ошибки по местам внутри группы с одинаковым приоритетом. Третий важный критерий, который нужно учитывать при работе с ошибками, — это часть проекта, на которую ошибки воздействуют. Чем больше ваша команда, тем важнее учитывать этот критерий. Необходимо понять, какая часть проекта страдает от ошибки: возможность печати или поисковый механизм? Разбейте весь проект на четыре-пять частей и учитывайте их в базе ошибок. Это даст вам новый взгляд на проект — вы сможете определять, какие части проекта наиболее «сырые» или какие части наиболее важны для вас и ваших заказчиков. Если каждый программист отвечает за конкретную часть проекта, то такой подход позволит распределять ошибки для правки. Существует большое количество других критериев для сортировки ошибок. Наиболее часто встречаются такие, как качественное описание ошибки и способов ее воспроизведения, версия ПО, в которой ошибка была найдена, по уникальному ID и по человеку, ее обнаружившему. Каждый проект уникален и информация, которая вам может понадобиться, — тоже уникальна.

9


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть II Автор: Scott Berkun Перевод: Андрей Олищук Вторая часть статьи об управлении ошибками В предыдущей части, когда мы обсуждали ошибки в программном обеспечении, мы говорили о самых основах тактики: сортировке и расстановке приоритетов. В части 2 продвинемся еще дальше. Теперь ставки растут - требуется больше вложений, но и повышаются размеры полученных прибылей.

Уровень 3. Критерии выхода Как вы определяете, что завершили работу над чем либо? Легко определить когда кончаются шоколадные печенья или лазанья - их просто не остается на тарелке. Но более сложные вещи, такие как программное обеспечение, не так прозрачны (и не так вкусны). Вы должны заранее запланировать те критерии, по которым вы определите, что работа завершена. Если вы не сделаете этого, то потратите дополнительные часы на споры о том, все ли сделано, и на лишний кодинг. Если у вас достаточно трезвого ума, и вы заранее определите критерии выхода, то ваша команда израсходует время на достижение цели, а не на споры. Критерием выхода может являться любое утверждение об окончании разработки, которое будет истинным до того, как разработка программы будет завершена. Простейшим примером критерия выхода является время. Вы определяете точное время и дату завершения проекта не обращая внимания на оставшиеся ошибки или невыполненные задачи. Этот критерий наиболее прост для достижения, но он не имеет никакого отношения к качеству программного продукта. Вы уложитесь в сроки поставки вне зависимости от того, реализовано у вас 10 или 90 процентов функционала. Но если смотреть с точки зрения качества, то дата выпуска продукта - это всего лишь произвольная точка времени. Другой очевидный, но плохой критерий - мнение. К примеру, вице-президент вашей фирмы может решить, что программа готова, когда ему покажется, что она готова. 10


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть II

Даже если у вас золотой вице-президент, это все равно плохой фактор. Если он случайно попадет под автобус, то ваш проект последует за ним. Вам нужно нечто зафиксированное на бумаге, чтобы ускорить взаимодействие. Если все будет хранится в чьей-то голове, то за ней выстроится длинная очередь из ожидающих. Настоящие критерии выхода базируются прежде всего на измерениях качества. Какое количество дефектов, какого типа приемлемо для программы? Если вы читали первую часть, то должны быть знакомы с терминами "Приоритет" и "Серьезность" - простейшими способами классификации ошибок. Но что, если вы не определили, какие задачи должны иметь приоритет над другими? Должны ли быть исправлены все ошибки с приоритетом №1, или должны быть исправлены все ошибки с приоритетом №1, но только для какой -то конкретной части проекта? Если этого не знаете вы, то не знает и ваша команда, и вы обречены на споры по поводу каждой ошибки. Определение критериев выхода - это одно из проявлений лидерства. Для этого необходима постоянная забота о проекте, что сможет произвести эффект катализатора для всей организации. Ключевой вопрос таков: как много ошибок, какого типа, в каких частях проекта допустимы? Вот несколько наводящих вопросов, которые могут помочь: • Чем вы были заняты в последнее время? Вам необходимо выпустить несколько релизов. Их количество случайно, но это даст вам точку отсчета. Вы можете увеличивать или понижать качество относительно чего-либо, и это позволит вам выразить то, что вы имеете ввиду. Если вы никогда ранее не делали этого, то поставьте перед собой цели. Решите, какие действия вам необходимо проделать, чтобы выпустить версию 2.0. Периодически вы сможете определять качество в сравнении с предыдущими версиями и направлять работу в нужном русле. • Какие части проекта наиболее важны? Составьте упорядоченный список частей проекта/функциональных возможностей. Те элементы, которые наиболее важны для заказчика, должны иметь больший вес. Вы можете определить одни критерии выхода для функций А, Б и В и другие - для Г, Д и Е. Используйте основные ресурсы для наиболее используемых функций. • Какие тесты (тест-кейсы) вы используете? Критерии выхода не должны основываться на количестве ошибок. Если вы используете тест-кейсы для каждой функции своего ПО, то вы можете установить критерий выхода в процентном соотношении пройденных тестов. Если вы до сих пор не использовали тестов, то пришло время начать делать это (http://en.wikipedia.org/wiki/Test_case). Они могут инкапсулировать много различных критериев в один, зачастую автоматизированный, тест. Если вы будете их делать, то тесты помогут вам определить не только критерии выхода, но и просто помогут в завершении конкретной работы. Каждый чек-ин (занесение части кода в общую базу) может выполняться по прохождению теста. Это поможет определять проблемы до того, как они превратятся в большое бедствие.

11


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть II

• Какие параметры производительности должны быть достигнуты? Определены ли параметры производительности, к достижению которых вы стремитесь? Время загрузки, сохранения, закачки? Сфокусируйтесь на параметрах, которые очевидны для ваших заказчиков, - в таком случае вы будете править ошибки, имеющие значение именно для заказчика. Проблемы с производительностью - одни из самых неприятных для заказчика. В придачу ко всему, они имеют тенденцию быть сложными для правки (подсказка: пытайтесь определить их на ранних стадиях). Если вы не имеете понятия о том, какие показатели производительности должны быть у вашей прграммы, последуйте данному ранее совету - выстройте цепочку релизов и делайте лучше, чем в прошлый раз. Любой критерий выхода, который вы определите, должен иметь достаточную спецификацию. Каждая функциональная возможность или дизайн должны включать раздел с описанием, по которому разработчики смогут определить, завершена работа или нет. Запишите, какие тесты должны быть пройдены, какие параметры производительности должны быть достигнуты, каким критериям юзабилити программа должна соответствовать, или какие типы ошибок должны быть поправлены. Если вы не можете этого определить, значит, вы недостаточно осмыслили функционал, над которым работаете и который нужен заказчику. Чтобы критерии выхода оставались простыми, вам может понадобиться две совокупности: ряд нумерованных релизов и дополнительных критериев, специфичных для функционала. С таким подходом вам останется определить критерии выхода, большие или меньшие показателей из предыдущих релизов. Совет для лентяев: вся работа по определению параметров того или иного релиза может быть возложена на одного ответственного человека. Помните: вы всегда можете корректировать критерии выхода в течение проекта. Им не обязательно быть одинаковыми от начала и до конца проекта. Но помните, фиксируя их, вы даете команде инструмент для определения качества ее работы. Хорошо, когда критерии выхода периодически улучшаются (но не просто изменяются). Это может происходить после дополнительных переговоров с заказчиком и командой. Даже если аргументы возникают в процессе обсуждения этих изменений, люди будут спорить о влиянии на качество с точки зрения проекта (и заказчика), что более эффективно, чем обсуждение уровней ошибок.

Уровень 4. Раннее планирование Теперь вы готовы сделать серьезный шаг к правке ошибок. Если первых три уровня пройдены, то пришло время перейти от тактики к вопросам стратегии. Чтобы преодолеть посредственность, вам необходимо найти пути для траты вашего времени не на простые проблемы, а на продвинутые задачи. Работая с низкоуровневой тактикой, вы будете только подпирать проект и предотвращать переход проблем из разряда простых в серьезные. Конечно, если что-то из вышесказанного принесет свои плоды в работе вашей команды, то заработанное великим трудом время уйдет на раздумья о том, как пройдут следующие несколько дней, недель или месяцев.

12


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть II

Вам необходимо взглянуть на более фундаментальные вещи, которые вы можете улучшить для повышения качества вашего программного обеспечения. Простейший вид раннего планирования - взглянуть на предыдущий проект (или последнюю неделю текущего проекта). Спросите себя и других, что было сделано не так и продолжает вас преследовать в текущем проекте? Это плохие привычки, неэффективные инструменты или простое взаимонепонимание? Составьте список сфер для улучшения и разместите их в грубом порядке по принципу срочности или значимости. Решения проблем могут заключаться в покупке лучших систем управления ошибками, постановке конкретных задач по сортировке ошибок, обучении членов команды или улучшении способов управления ошибками. Возможно, хорошим решением будет назначение определенного человека для переговоров с заказчиком по поводу ошибок. Другой вариант: вы можете составить анкету для заказчика, которая поможет ему лучшим образом составлять отчеты об ошибках, что повысит качество описания ошибок, вводимых в систему. Подходящий способ обеспечить все это состоит в выделении человека для выполнения им задач по контролю качества. Если таковой человек в вашей команде уже есть, обеспечьте его большими возможностями (http://www.macdevcenter.com/pub/a/mac/2005/07/08/dev_team.html). Другой подход - сфокусироваться на раннем определении проектов. Если у вас есть возможность, наймите проектировщика или инженера по юзабилити, чтобы они помогли вам правильно организовать проект с самого начала. Они помогут вам определить наиболее важные направления и избавят вашу команду от траты времени на разработку второстепенных функций. Если вы вырабатываете список частей проекта для улучшения, не забудьте ранжировать их. Расставьте их в порядке наибольшей значимости для вас и проекта, а затем начните решение первой задачи из этого списка. Не первых пяти или десяти, а именно самой первой. До тех пор, пока вы и каждый член вашей команды не будут способны добиться позитивных изменений, вы должны добиваться этих изменений. Помогите всем объединиться вокруг улучшения ситуации, позвольте каждому высказывать свои предложения и участвовать в их реализации. И не забывайте о критериях выхода, иначе как вы узнаете, что какой-либо процесс уже улучшен?

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

13


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть II

• Мораль: правка ненавистных или очевидных ошибок, вне зависимости от их приоритета, может позволить программисту почувствовать гордость и повысить моральный дух. Это допустимо, если вы знаете, ради чего идете на это. Если такие вещи проделываются часто, то смените критерии для расстановки приоритетов 1, 2 и 3. Если вам приходится обходить правило, смените это правило. • Убедитесь, что программисты не путают свое время с рабочим временем. Низкоприоритетные ошибки и те ошибки, которые интересны лично им, они могут править только в свободное время. При этом убедитесь, что им известно, будут ли им начислены бонусы за работу над проектом в нерабочее время. • Качество: исправление ошибок схоже с любой другой работой - чем больше времени вы на это тратите, тем выше качество исправлений. При этом возьмите за правило, что время, затраченное на исправление, должно соответствовать весу ошибок. Вы ведь не будете навешивать пластмассовую дверь на золотые петли без веской причины?

Часто задаваемые вопросы • Кто должен быть вовлечен в сортировку ошибок (Уровень 1)? Тот, кто громче всех кричит. Не важно, разработчик ли это, тестер или менеджер проекта. Если один человек может взять на себя сортировку и фильтрацию базы ошибок, то не стоит поручать эту работу троим. Если у вас есть представитель заказчика, попросите его плотно поучаствовать в этом процессе (но будьте готовы переводить ошибки в его термины). Все специалисты, технические писатели, юзабилити-инженеры, маркетинг должны быть приглашены, но наилучший способ сэкономить время каждого из них - это договориться с ними о телефонных консультациях, когда их помощь будет нужна. Дайте им знать, какая из ошибок требует их внимания, и они подскажут вам, насколько серьезна данная ошибка. • Что мне делать, если я не могу добиться согласия босса на определение критерия выхода (окончания проекта)? Изучайте гипноз. Ну, хорошо, возьмите этот критерий для работы над пилотным проектом. Обратитесь к людям, которые уже участвовали в таких проектах и попробуйте выработать критерии вместе с ними. Затем, когда проект, по вашему мнению, подойдет к концу, совместно с боссом рассмотрите, насколько ваши критерии выхода приемлемы в данной ситуации. Если они приемлемы, босс все равно скажет нет. Если не приемлемы, то что вы еще ожидали от босса? Повторяйте эту процедуру, пока вы не найдете что-либо, позволяющее вам закончить проект. • Приведите пример самого неудачного случая в вашей практике управления ошибками. Что ж, это история одного менеджера по контролю качества, который сформулировал критерии выхода за два дня до срока окончания проекта.

14


PHP Inside №14

Исправление ошибок: в нужное время и в нужном месте. Часть II

Он был счастлив своему прогрессу, но когда я взглянул на то, что он записал, я не был удивлен, что он просто задокументировал текущий билд. Вы можете догадаться о качестве этого релиза. Правда, это была не совсем ошибка менеджера. Вице-президент оставил критерии выхода только у себя в памяти и не сообщил о них менеджеру. • Почему управление ошибками не преподается как часть информатики в университетах? Возможно, их учебные планы сами полны ошибок? Если быть честным, то здесь достаточно и меньшего времени, чем учеба в университете. Я полагаю, что исправление ошибок - это узкая прикладная дисциплина, которая с академической точки зрения больше является прикладной, нежели относится к теории вычислительной техники. В большинстве училищ (в американской системе образования - trade scool, прим. переводчика), где учебный процесс сфокусирован на конкретных прикладных науках, можно встретить факультативные занятия по контролю качества и искусству отладки.

Полезные ссылки Тестирование программного обеспечения. Это огромная тема. Начать можно отсюда: http://en.wikipedia.org/wiki/Software_testing Безболезненный учет ошибок: замечательное эссе от Джоеля о простом учете ошибок. http://www.joelonsoftware.com/articles/fog0000000029.html Примеры сортировки ошибок. Здесь есть примеры различной организации сортировки ошибок. Ссылки разнообразны: • Bugzilla: https://bugzilla.mozilla.org/page.cgi?id=etiquette.html • OSAF: http://wiki.osafoundation.org/bin/view/Journal/KatieParlante20040726 • Gnome: http://developer.gnome.org/projects/bugsquad/triage/faq.html • Microsoft video of a triage session: http://channel9.msdn.com/ShowPost.aspx?PostID=26948#26948 • Software quality assurance FAQ: http://www.softwareqatest.com/qatfaq1.html

Функциональное тестирование при помощи Selenium

15


PHP Inside №14

Функциональное тестирование при помощи Selenium

Функциональное тестирование при помощи Selenium Автор: Сергей Юдин Любое качественное веб-приложение нуждается в тестировании. Selenium — инструмент для тестирования веб-приложений Любое приложение нужно тестировать. Как говорил очень уважаемый программист: «программы, которые не протестированы, не работают». Мы пишем код и тестируем его, мы создаем страницы и смотрим, как они отображаются в браузере, мы создаем формы и проверяем, правильно ли работают на них все компоненты.

Что такое функциональное тестирование Чаще всего мы проводим тестирование наших приложений на соответствие требованиям заказчика (даже если в роли заказчика выступаем мы сами). Такое тестирование называется приемочным или функциональным. Например, у нас есть электронный магазин. Его функциональное тестирование будет заключаться в проверке работы каталога товаров, навигации по разделам, выбор товаров, формирование корзины, оформление заказа, просмотр состояния заказа, получение подтверждения о принятии заказа к обработке по почте и т.д. Грубо говоря, мы проверяем работоспособность приложения, а также правильность его работы. Зачастую функциональное тестирование проводится вручную, обычно для этого выделяют специального человека или несколько, которые обходят сайт раз за разом и сверяют поведение системы с тем, что прописано в техническом задании. На такое ручное тестирование уходит очень много времени, да и качество этого тестирования далеко не идеальное: человек может что-либо забыть, полениться сделать, пропустить что-то. Один из самых больших недостатков такого ручного тестирования, что моменты обнаружения ошибки в поведении системы и локализации этой ошибки очень удалены друг от друга. Иногда проходит неделя или даже больше времени, прежде чем ошибка выявляется, и на поиск ее причины и исправление уходит много времени. Мы не можем проводить ручное тестирование сколь угодно часто, так как это требует больших затрат. Мне могут возразить, что у них в компании есть люди, которые занимаются ручным функциональным тестирование весь день. 16


PHP Inside №14

Функциональное тестирование при помощи Selenium

Что ж, вы можете гарантировать, что один и тот же сайт можно обходить целиком 5-10 раз в день, проверять его до мельчайших деталей? Если ответ да, то мне просто жаль ваш отдел QA. Для того чтобы проводит такое тестирование быстро, часто и главное эффективно, существуют средства для автоматизации функционального тестирования. Далее мы будем вести речь только об автоматизированном функциональном тестировании, мы покажем, как и при помощи чего можно автоматизировать этот нелегкий и скучный труд.

Автоматизированное функциональное тестирование Автоматизированным функциональным тестированием называют такое тестирование системы (в нашем случае это будет webсайт), когда тестирование ведется в полностью автоматическом режиме, без непосредственного участия человека, без знания деталей реализации этой системы на соответствие требованиям заказчика. Функциональное тестирование поэтому называют также тестированием “черного ящика”. Автоматизированные функциональные тесты в идеале не должны знать ничего про архитектуру системы, из каких компонентов она состоит, на каком языке программирования реализована и т.д. Это не позволит менять архитектуру, язык приложения, базу данных, но гарантирует, что мы сможем всегда быстро проверить, что система все еще работоспособна.

Автоматизированные функциональные тесты в идеале не должны знать ничего про архитектуру системы, из каких компонентов она состоит, на каком языке программирования реализована

Тестирование должно проводится так, как если бы реальный пользователь работал с системой. Приемочные тесты можно использовать для любых приложений (размера и качества реализации). Их написание не требует много времени и серьезных навыков. Приемочные тесты можно писать как до реализации тестируемой системы, так и после. Функциональные тесты бывают очень полезны при рефакторинге legacy-систем (ранее написанных), если они не были до этого покрыты модульными тестами. В этом случае мы еще до рефакторинга производим создание набора функциональных тестов, чтобы у нас имелась возможность быстро проверить целостность системы и ее функциональности после внесения изменений. Функциональное тестирование для web-сайтов подразумевает эмуляцию работы пользователя с браузером, а именно: открытие страниц, переход по ссылкам, заполнение и отправка форм, проверка значений полей форм, наличие определенного текста на страницах, получение почты, отправка файлов и т.д. При этом все действия по осуществлению манипуляции браузером должны производить сами тесты, в автоматическом режиме, без участия человека.

При помощи чего производится функциональное тестирование. В настоящий момент в среде разработчиков ни один продукт не используется как стандарт для функционального тестирования web-приложений. Существует 2 типа продуктов для функционального тестирования:

17


PHP Inside №14

Функциональное тестирование при помощи Selenium

Продукты, эмулирующие поведение браузера, написанные на языке высокого уровня, обычно на том же языке, что и приложение (но не обязательно). В качестве примеров можно привести httpUnit, JWebUnit, WebTester из SimpleTest и другие.

Продукты реализованные на JavaScript и реализующие проверки непосредственно средствами браузера. В качестве примеров можно привести Watir и Selenium.

Эмуляторы хорошо себя зарекомендовали и стали достаточно популярны. В первую очередь из-за того, что они появились раньше программ второго типа, кроме этого эмуляторы могут использовать код, который написан для приложения, правильно и оптимально создавать фикстуры (формировать среду для правильной работы тестов), немаловажен и такой показатель как высокая скорость исполнения. Но эмуляторы браузеров не могут на 100% справиться с одной задачей – они не могут выполнять JavaScript код, который используется на страницах сайтов. Это накладывает ограничения или на процесс тестирования, или на процесс создания сайтов. То есть невозможно проверить работу сайта, как если бы это делал реальный пользователь, так как эмуляция работы браузера – вовсе не эквивалентна реальной работе браузера. Из-за этого автоматизированное тестирование приходится дополнять тестированием ручным. Этого недостатка лишены продукты для функционального тестирования второго типа. Они контролируют работу браузера, выполняют команды и делают проверки при помощи JavaScript-кода, что практически гарантирует 100% адекватность автоматизированного тестирования ручному. К сожалению, до недавнего времени у разработчиков не было возможности пользоваться хорошим, удобным, а главное бесплатным продуктом второго типа. Но теперь есть Selenium!

Selenium – краткое описание Selenium как проект был начат относительно недавно (в июне 2004 года, стал открытым в декабре 2004 года) и ведется под патронажем компании ThoughtWorks. Кстати в этой компании работает Мартин Фаулер, а это о многом говорит. Selenium выпускается под лиценцией Apache License, Version 2.0. Selenium доступен по этому адресу: http://selenium.thoughtworks.com/ По этому находится JIRA для Selenium (аналог Trac): http://jira.public.thoughtworks.org/browse/SEL Что собой представляет Selenium? Это объектно-ориентированное JavaScript приложение, которое может анализировать файлы определенной структуры для того, чтобы находить в них команды для манипуляции браузером и команды для выполнения определенных проверок.

18


PHP Inside №14

Функциональное тестирование при помощи Selenium

В поставке Selenium входит документация, которая может помочь быстро разобраться в использовании этой системы для функционального тестирования. Для того чтобы попробовать Selenium в работе, его необходимо скачать, разархивировать, положить в папку, доступную для web-сервера и набрать в строке браузера приблизительно такой путь: http:/localhost/selenium/TestRunner.htm. Никаких дополнительных настроек не требуется. Вот так должно выглядеть окно вашего браузера после запуска Selenium-а:

Рабочее окно Selenium поделено на несколько областей. Снизу находится рабочая область браузера. Здесь будут отображаться страницы тестируемого сайта. В левом верхнем углу находится список тестовых случаев (TestCase-ов), которые могут быть выполнены для текущего приложения. Это список тестовых случаев называется TestSuite. В центральной верхней части находится содержимое текущего TestCase-а, в правой верхней части – управляющие кнопки для запуска тестов, а также статистика выполнения тестов. Для запуска тестов нажмите кнопку AllTests и Selenium начнет проверку самого себя при помощи набора тестов, который поставляется вместе с ним. Selenium может выполнять тесты в трех скоростных режимах: run, walk и step. Run выполняет тесты с максимальной скоростью. Walk делает паузу в 0.5 сек (по-умолчанию) на каждой команде, что может быть очень удобно при визуальной проверке правильности работы сайта. Step – вы самостоятельно приказываете Selenium-у выполнить следующую команду.

19


PHP Inside №14

Функциональное тестирование при помощи Selenium

Содержимое TestCase-а TestCase-ы Selenium – это обычные html-страницы с одной таблицей, содержащей команды. Каждая строка таблицы содержит 3 колонки. Первая из них является действием или проверкой (action и assertion/check), вторая – именем элемента (target), к которому применяется команда, и третья – значением (value). Вот пример файла TestCase-а Selenium-а, назовем его Test Login: <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta content="text/html; charset=win-1251" http-equiv="content-type"> <title>Test Login</title> </head> <body> <table cellpadding="1" cellspacing="1" border="1"> <tbody> <tr> <td rowspan="1" colspan="3">Test Login<br> </td> </tr> <tr> <td>open</td> <td>/login</td> <td> </td> </tr> <tr> <td>type</td> <td>login</td> <td>vasa</td> </tr> <tr> <td>type</td> <td>password</td> <td>super_admin</td> </tr> <tr> <td>clickAndWait</td> <td>login_button</td> <td> </td> </tr> <tr> <td>verifyLocation</td> <td>/</td> <td> </td> </tr> <tr> <td>verifyTextPresent</td> <td>Wellcome, Vasa Pupkin</td> <td> </td> </tr> </tbody> </table> </body> </html>

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

20


PHP Inside №14

Функциональное тестирование при помощи Selenium

Мы покажем ниже, как можно вставлять комментарии в тестовый код другими способами. Итак, <tr> <td>open</td> <td>/login</td> <td> </td> </tr>

Выполняет действие open. Selenium дает команду браузеру перейти на указанную страницу - /login. Колонка для значения здесь не используется. Допустим в результате выполнения у нас открылась страница, содержащая форму авторизации: <form id='login_form' name='login_form' action='' method='post'> Login : <input id='login' name='login' type='text' size='40'><br> Password : <input id='password' name='password' type='password' size='40'> <input id='login_button' type='submit' value='Login'> </form>

Теперь Selenium при помощи действий type заполнит поля формы определенными значениями. Вы визуально увидите, что поля получили соответствующие значения. <tr> <td>type</td> <td>login</td> <td>vasa</td> </tr> <tr> <td>type</td> <td>password</td> <td>super_admin</td> </tr>

После этого действием clickAndWait Selenium нажмет кнопку для отправки формы и будет дожидаться ответа. <tr> <td>clickAndWait</td> <td>login_button</td> <td> </td> </tr>

После этого при помощи проверки verifyLocation Selenium проверит текущее положение браузера. После аутентификации мы должны оказаться на главной странице сайта <tr> <td>verifyLocation</td> <td>/</td> <td> </td> </tr>

И, наконец, после успешной аутентификации на должны увидеть приглашение на главной странице.

21


PHP Inside №14

Функциональное тестирование при помощи Selenium

Selenium проверяет наличие соответствующего текста на странице при помощи проверки verityTextPresent <tr> <td>verifyTextPresent</td> <td>Wellcome, Vasa Pupkin</td> <td> </td> </tr>

Чуть ниже мы разберем, какие команды и проверки есть в Selenium, как указывать определенные элементы на странице, а также как расширить набор доступных команд и проверок.

Организация тестов Как уже было указано, TestCase-ы организованы в список, который называется TestSuite. TestSuite также является обыкновенной html страницей, приблизительно такого содержания: <html> <head> <meta content="text/html; charset=win-1251" http-equiv="content-type"> <title>Test Suite</title> </head> <body> <table cellpadding="1" cellspacing="1" border="1"> <tbody> <tr><td><b>Test Suite</b></td></tr> <tr><td><a href="./TestOpen.html">TestOpen</a></td></tr> <tr><td><a href="./TestLogin.html">TestLogin</a></td></tr> </tbody> </table> </body> </html>

Теперь объясним чуть поподробнее то, как организуются тесты и как они хранятся на диске. В базовой поставке все файлы TestCase-ов и TestSuite-ов являются обычными текстовыми html файлами и лежат в папке /selenium/tests. При запуске Selenium пытается найти файл TestSuite.html в этой папке и загружает список тестов из TestSuite в левое верхнее окно. При щелчке на одну из ссылок файла TestSuite.html в окно текущего TestCase-а загружается содержимого соответствующего файла с командами. Вот в принципе и все. Для расширения набора тестов какоголибо приложения мы должны создать соответствующий TestCase html файл и поместить на него ссылку в TestSuite.html. Это, правда, самый простейший вариант использования Selenium-а, однако большинство разработчиков он устроит. Отметим здесь же, что хотя Selenium воспринимает TestCaseы только в виде html страниц, это вовсе не значит, что сами TestCase-ы должны быть html файлами. Они могут генериться при помощи PHP, JSP, CGI, в общем, при помощи любой технологии, которая вам может показаться удобной.

22


PHP Inside №14

Функциональное тестирование при помощи Selenium

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

Инсталляция Selenium для тестирования сайтов Существует 2 способа инсталляции Selenium: •

Отдельный web-сервер, который тестирует другие сайты.

Инсталляция Selenium-а по тому же адресу, что и тестируемый сайт.

Мы не будем рассматривать первый случай, так как здесь возникают некоторые проблемы с так называемыми cross-site scripting limitations, то есть отказ браузера исполнять код, если он требует модификации содержимого страницы, пришедшей с другого хоста. Вы можете самостоятельно поискать решение этих проблем, благо в списке рассылки Selenium-а такие вопросы время от времени появляются. Самым простым способ установки Selenium является распаковка его в папку проекта, удаление родных тестов, подчистка TestSuite.html – вот и все. Но данный метод связан с копированием файлов Selenium в каждый проект, кому это может понравиться? Ниже мы покажем, как можно легко и быстро настроить Selenium на использование одной инсталляции несколькими проектами. Итак, допустим, мы разархивировали selenium в папку / var/external/selenium/. При помощи Selenium мы желаем протестировать 2 проекта: / var/dev/project1/ и /var/dev/project2/. Что нам нужно сделать, что того, чтобы использовать одну инсталляцию на работы с обоими проектами. При запуске Selenium-а мы указывали в пути файл TestRunner.html в папке /var/external/selenium/. Посмотрим на его содержимое: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <html> <head> <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type" /> <title>Selenium Functional Test Runner</title> <script language="JavaScript" type="text/javascript" patched.js"></script> <script language="JavaScript" type="text/javascript" browserbot.js"></script> <script language="JavaScript" type="text/javascript" api.js"></script> <script language="JavaScript" type="text/javascript" commandhandlers.js"></script> <script language="JavaScript" type="text/javascript" executionloop.js"></script> <script language="JavaScript" type="text/javascript" fitrunner.js"></script> <script language="JavaScript" type="text/javascript" logging.js"></script> <script language="JavaScript" type="text/javascript" src="htmlutils.js"></script> <script language="JavaScript" type="text/javascript"

src="html-xpath/html-xpathsrc="seleniumsrc="seleniumsrc="seleniumsrc="seleniumsrc="seleniumsrc="selenium-

src="xpath.js"></script>

23


PHP Inside №14

Функциональное тестирование при помощи Selenium

<script language="JavaScript" type="text/javascript" src="userextensions.js"></script> <script language="JavaScript" type="text/javascript"> function openDomViewer() { var autFrame = document.getElementById('myiframe'); var autFrameDocument = getIframeDocument(autFrame); var domViewer = window.open('domviewer.html'); domViewer.rootDocument = autFrameDocument; return false; } </script> <link rel="stylesheet" type="text/css" href="selenium.css" /> </head> <body onload="start();"> <table class="layout"> <form action="" name="controlPanel"> <!-- Suite, Test, Control Panel --> <tr class="selenium"> <td width="25%" height="30%" rowspan="2"><iframe name="testSuiteFrame" id="testSuiteFrame" src="./tests/TestSuite.html"></iframe></td> [… опустим остальную часть …] </body> </html>

Наша задача – сделать копию этого файла в наших проектах и немного изменить его содержимое. Создадим в наших проектах папки: * /var/external/project1/tests/selenium/ * /var/external/project1/tests/selenium/cases * /var/external/project2/tests/selenium/ * /var/external/project2/tests/selenium/cases Скопируем в /var/external/project1/tests/selenium/ файл TestRunner.html и немного поменяем его содержимое: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <html> <head> <meta content="text/html; charset=win-1251" http-equiv="content-type" /> <title>Selenium Functional Test Runner</title> <script language="JavaScript" type="text/javascript" xpath/html-xpath-patched.js"></script> <script language="JavaScript" type="text/javascript" browserbot.js"></script> <script language="JavaScript" type="text/javascript" api.js"></script> <script language="JavaScript" type="text/javascript" commandhandlers.js"></script> <script language="JavaScript" type="text/javascript" executionloop.js"></script> <script language="JavaScript" type="text/javascript" fitrunner.js"></script> <script language="JavaScript" type="text/javascript" logging.js"></script> <script language="JavaScript" type="text/javascript" src="/seleniumSrc/htmlutils.js"></script> <script language="JavaScript" type="text/javascript"

src="/seleniumSrc/htmlsrc="/seleniumSrc/seleniumsrc="/seleniumSrc/seleniumsrc="/seleniumSrc/seleniumsrc="/seleniumSrc/seleniumsrc="/seleniumSrc/seleniumsrc="/seleniumSrc/selenium-

24


PHP Inside №14

Функциональное тестирование при помощи Selenium

src="/seleniumSrc/xpath.js"></script> <script language="JavaScript" type="text/javascript" src="userextensions.js"></script> <script language="JavaScript" type="text/javascript"> function openDomViewer() { var autFrame = document.getElementById('myiframe'); var autFrameDocument = getIframeDocument(autFrame); var domViewer = window.open('/seleniumSRC/domviewer.html'); domViewer.rootDocument = autFrameDocument; return false; } </script> <link rel="stylesheet" type="text/css" href="/seleniumSRC/selenium.css" /> </head> <body onload="start();"> <table class="layout"> <form action="" name="controlPanel"> <!-- Suite, Test, Control Panel --> <tr class="selenium"> <td width="25%" height="30%" rowspan="2"> <iframe name="testSuiteFrame" id="testSuiteFrame" src="./cases/TestSuite.html"></iframe></td> [… опустим остальную часть …] </body> </html>

Основная идея должна быть ясна – все инклюды JavaScript осуществлять из папки /seleniumSrc/, а тесты искать в папке ./cases/. Теперь в настройках web-сервера (Apache) нужно настроить алиас на /seleniumSrc/, который бы указывал на путь до инсталляции Selenium, например, так: #project1 <VirtualHost 127.0.0.1> DocumentRoot /var/dev/project1 ServerName project1 ErrorLog logs/ project1-error_log CustomLog logs/ project1-access_log common Alias /seleniumSrc/ "/var/external/selenium/" </VirtualHost>

Теперь нужно создать файл TestSuite.html (будем считать, что мы пока не используем никаких технологий для генерации TestSuite-ов и TestCase-ов), а также необходимое количество TestCase.html-файлов. Примеры смотрите выше. Ту же самую процедуру нужно сделать и над другим проектом. Вот в принципе и все. Обратите внимание, что мы не меняли путь до файла user-extenstions.js – обычно в этом файле хранятся расширения к Selenium, характерные для текущего проекта. Мы уделим внимание расширениям Selenium-а чуть позже.

Составляющие команд Selenium-а В Selenium существует несколько основных понятий: •

действия 25


PHP Inside №14 •

проверки

локаторы

Функциональное тестирование при помощи Selenium

Действия Действия (actions) используются для того, чтобы управлять браузером из-под Selenium. Набор действий достаточно широк. Ниже приведем список наиболее часто используемых действий. В скобках будет дана форма применения в формате wiki-table. Итак вот некоторые из действий Selenium: •

open – указывает браузеру открыть страницу по определенному адресу (|open|location||)

click – указывает браузеру щелкнуть по ссылке или по элементу формы, например, для пометки checkbox-а (|click|target||)

type – указывает браузеру ввести новое значение в элемент формы (|type|element|value|)

select – указывает браузеру выбрать определенное значение <option> внутри <select> тега формы(|select|element|value)

selectWindow – указывает браузеру переключить фокус на другое окно (|selectWindow|window_name||)

goBack – указывает браузеру вернуться на предыдущую страницу

close – указывает браузеру закрыть текущее окно

Полный список и примеры использования действий смотрите в документации, поставляемой вместе с Selenium.

Проверки Проверки (checks) используются для проверки правильности поведения приложения. Любая проверка в Selenium представлена двумя типами – assert и verify. Если в тесте стоит проверка assertSomething и она не выполняется, тест прекращает свою работы, а если verifySomething – то выдается сообщение об ошибке, но тест продолжает работу. Вот список наиболее часто используемых проверок: •

verifyLocation / assertLocation – проверяет текущий URL окна браузера (|verifyLocation|url_needed||)

verifyTitle / assertTitle – проверяет заголовок окна (|verifyTitle| title_needed||)

verifyValue / assertValue – проверяет, что поле формы имеет указанное значение (|verifyValue|field|value_needed|)

verifyTextPresent / assertTextPresent – проверяет, что текст страницы содержит указанный текст (|verifyTextPresent|text_needed||)

verifyElementPresent / assertElementPresent – проверяет, что элемент присутствует на текущей странице (|verifyElementPresent| element_needed||)

26


PHP Inside №14

Функциональное тестирование при помощи Selenium

Полный список и примеры использования проверок смотрите в документации, поставляемой вместе с Selenium.

Локаторы Selenium или как находятся элементы, к которым применяются команды Локаторы используются для нахождения элементов, к которым относятся команды. Список локаторов Selenium достаточно большой: •

id – используется атрибут id (идентификатор) элемента

name – используется атрибут name элемента

identifier – используется атрибут id элемента, если по id-у элемент не найден, то поиск будет вестись по атрибуту name

dom – используется для поиска элемента по DOM выражению, которое должно начинаться с document.

xpath – используется для поиска элемента по XPath выражению, которые должно начинаться с //

link – используется для нахождения ссылок с указанным текстом.

Разберем, как используются локаторы в командах, на примере, чтобы все стало понятно. Возьмем знакомую форму аутентификации. <form id='login_form' name='login_form' action='' method='post'> Login : <input id='login' name='login' type='text' size='40'><br> Password : <input id='password' name='password' type='password' size='40'> <input id='login_button' type='submit' value='Login'> </form>

Для заполнения поля login формы login_form можно воспользоваться следующими командами: <tr> <td>type</td> <td>id=login</td> <td>vasa</td> </tr>

Использовали локатор id. <tr> <td>type</td> <td>name=login</td> <td>vasa</td> </tr>

Использовали локатор name. <tr> <td>type</td> <td>identifier=login</td> <td>vasa</td> </tr>

27


PHP Inside №14

Функциональное тестирование при помощи Selenium

Использовали локатор identifier. <tr> <td>type</td> <td>xpath=//input[@name=’login’]</td> <td>vasa</td> </tr>

Использовали локатор xpath. <tr> <td>type</td> <td>dom=document.forms[‘login_form’].login</td> <td>vasa</td> </tr>

Использовали локатор dom. Если в команде не указано явно название типа локатора, например не dom=document.forms[‘login_form’].login, а просто document.forms[‘login_form’].login , тогда Selenium последовательно пытается найти элемент при помощи следующих типов локаторов: •

identifier

dom

xpath

Обратите внимание, что для тегов <select> есть особые локаторы, впрочем, как и специальные действия и проверки. Мы не будем уделять этому внимание, так как все это достаточно хорошо описано в документации.

Использование XPath локаторов Использование XPath локаторов может значительно упростить жизнь тестировщику, особенно если у него нет прав доступа менять исходный код web-приложения, которое он тестирует. Стоит сразу отметить, что при работе Selenium с IE даже версии 6.0 при использовании XPath выражений, были замечены некоторые проблемы, однако в FireFox никаких нареканий нет. Вот некоторые примеры использования XPath локаторов: •

//a[contains(text(), 'partial text')] – поиск ссылки по частичному совпадению

//input[@type='submit'] – поиск кнопки для отправления формы по типу

//input[@value='Button Description'] - поиск кнопки по надписи на ней

//table[@id='yourTable']//tr[td='uniqueCustomerCode']/td//a/img [@alt='Edit'] – поиск ссылки по картинки, которая находится в определенной ячейке таблицы.

28


PHP Inside №14

Функциональное тестирование при помощи Selenium

Расширение Selenium-а Добавление новых действий, проверок и локаторов Так как Selenium сделан в отличном ООП стиле на JavaScriptе, то ничего не стоит его расширить. Разработчики приложили максимум усилий, чтобы это было очень легко. Даже разработчику с минимальными знаниями о DOM и JavaScript удастся быстро разобраться, что к чему. Действия (Actions) Итак, действия (actions) Selenium-а – это определенные методы класса Selenium (правильнее будет сказать прототипа). Все методы, вида doSomething интерпретируются как действия. То есть прототип Selenium содержит методы doOpen, doClose, doClick и т.д. Здесь и далее привены примеры расширения Selenium, взятые из поставляемой документации. Например, мы добавим действие typeRepeated, которое дублирует текущее значение указанного элемента: Selenium.prototype.doTypeRepeated = function(locator, text) { // All locator-strategies are automatically handled by "findElement" var element = this.page().findElement(locator); // Create the text to type var valueToType = text + text;

};

// Replace the element text with the new text this.page().replaceText(element, valueToType);

Проверки (Asserts/Checks) Методы прототипа Selenium вида assertSomething становятся проверками (assert-ами). Вот пример добавления нового assert-а, который удостоверяется, что указанный текст повторяется в значении элемента два раза (дублируется). Selenium.prototype.assertValueRepeated = function(locator, text) { // All locator-strategies are automatically handled by "findElement" var element = this.page().findElement(locator); // Create the text to verify var expectedValue = text + text; // Get the actual element value var actualValue = element.value;

};

// Make sure the actual value matches the expected this.assertMatches(expectedValue, actualValue);

Теперь в тестах можно использовать verifyValueRepeated или assertValueRepeated.

29


PHP Inside №14

Функциональное тестирование при помощи Selenium

Локаторы (locators) Локаторы формируются из методов прототипа PageBot или его потомков, например, MozillaPageBot, IEPageBot, вида localeSomething. Ниже дан пример введения нового локатора для поиска элемента, у которого есть повторяющийся текст. // The "inDocument" is a the document you are searching. PageBot.prototype.locateElementByValueRepeated = function(text, inDocument) { // Create the text to search for var expectedValue = text + text; // Loop through all elements, looking for ones that have a value === our expected value var allElements = inDocument.getElementsByTagName("*"); for (var i = 0; i < allElements.length; i++) { var testElement = allElements[i]; if (testElement.value && testElement.value === expectedValue) { return testElement; } } return null; };

user-extensions.js Когда мы модифицировали файл TestRunner.html для того, чтобы использовать одну инсталляцию Selenium для нескольких проектов, мы оставили user-extentions.js подключаться непосредственно из проекта. Обычно в файл user-extentions.js кладут расширения Selenium, характерные для текущего проекта. Никто не мешает вам сделать свои «user-extentions.js» и подключать их в TestRunner.html по мере необходимости.

Некоторые моменты использования Selenium Вставка комментариев в TestCase Иногда бывает очень полезным вставить в код комментарий, чтобы было понятно, что же на самом деле тестируется. Первая идея, которая приходит на ум – это использование html комментариев, то есть: <!-- описание теста -->

Однако у этого подхода есть один недостаток – он не отображается в окне текущего TestCase-а. Еще одной идеей было использование четвертой колонки тестовой таблицы для комментирования тестовых команд. В настоящее время Selenium игнорирует все остальные колонки, кроме первых трех, поэтому вы можете использовать дополнительные колонки по своему разумению. <tr>

<td>open</td> <td>/index.php?id=10</td> <td> </td> <td>Open Login Page</td>

30


PHP Inside №14

Функциональное тестирование при помощи Selenium

</tr>

Но это не совсем удобно, к тому же разработчики Selenium не обещают, что четвертая колонка не будет задействована в очередном релизе. Наконец, еще одним вариантом будет добавить в Selenium через user-extension.js команду comment: /** * Allow addition of comments to tests */ Selenium.prototype.doComment = function() { // Don't do anything. This is to facilitate comments in tests. }

Тогда в TestCase-е можно будет пользоваться такими конструкциями: <tr>

<td>comment</td> <td>Создадим нового пользователя и попытаемся залогиниться под ним</td> <td> </td> </tr>

Этот вариант выглядит наиболее предпочтительно.

Работа с popup-окнами По умолчанию Selenium применяет все команды к текущему окну браузера. Если у вас в приложении есть ссылки на popup-окна, то для того, чтобы команды применялись к новому окну – используйте действие selectWindow(name). После того, как вы выполнили команду, которая закрывает popup-окно, выполните команду selectWindow(null) для того, чтобы вернуться в главное окно. Допустим, у вас есть такой html код: <html> <head> <script type="text/javascript"> function openWindow() { myPopupWindow = window.open('/path_to_popup_window', 'popup_window', 'height=200,width=500,top=400,left=50'); } </script> </head> <body> <button id="popup_link" onclick="openWindow();">Click here to open a popup window</button> </body> </html>

Тогда ваш тест будет выглядеть приблизительно так: <tr>

<td>click</td> <td>popup_link</td> <td>  </td> </tr> <tr>

31


PHP Inside №14

Функциональное тестирование при помощи Selenium

<td>selectWindow</td> <td>popup_window</td> <td>  </td> </tr> [… команды для popup-окна] <tr> <td>click</td> <td>close_popup</td> <td>  </td> </tr> <tr> <td>selectWindow</td> <td>null</td> <td>  </td> </tr> [… команды для главного окна]

Однако при тестировании реальных приложений с popup-окнами можно столкнулся с проблемой, что Selenium не ждет, пока новое окно полностью загрузится, а сразу начинает выполнять команды для нового окна. Конечно, можно попробовать использовать действие pause, например, так: <tr>

<td>click</td> <td>popup_link</td> <td>  </td> </tr> <tr> <td>selectWindow</td> <td>popup_window</td> <td>  </td> </tr> <tr> <td>pause</td> <td>2000</td> <td>  </td> </tr> [… команды для popup-окна]

Но это сильно замедляет общее выполнение тестов, если таких окон много. В списке рассылки было предложено следующее решение данной проблемы. Суть ее сводится к введению новой команды waitForElementPresent. Сразу оговорюсь, что это решение не работает с версией 0.5.0, только с SVN версией Selenium (см. ссылку на репозиторий ниже). Итак, вот код для нового действия: Selenium.prototype.doWaitForElementPresent = function (locator) { var self = this; testLoop.waitForCondition = function () { try { var pagebot = self.page(); pagebot.findElement(locator); return true; } catch (e) { return false; } }; };

32


PHP Inside №14

Функциональное тестирование при помощи Selenium

Теперь можно выполнять такие команды: <tr>

<td>click</td> <td>popup_link</td> <td>  </td> </tr> <tr> <td>selectWindow</td> <td>popup_window</td> <td>  </td> </tr> <tr> <td>waitForElementPresent</td> <td>create_form</td> <td>  </td> </tr> [… команды для popup-окна]

Работа с iframe В настоящее время Selenium не поддерживает работу с frameами и iframe-ами на достаточном уровне. Несколько раз в рассылке появлялись сообщения, что кто-то реализовал поддержку фреймов, но до коммитов в репозиторий дело пока не дошло. Поэтому для тех приложений, которые содержат фреймы, Вам придется написать свои расширения.

Иерархия тестов и сборка тестов из компонентов Как уже было сказано выше, для написания тестов для Selenium можно использовать любые средства, которые в конечном итоге дают простую html таблицу команд. Вы можете писать тесты в XML, а затем при помощи XSLT приводить их в понятный для Selenium-а формат. Немного поразмыслив, мы, например, решили попробовать использовать шаблонизатор WACT с простейшим php-скриптом, который выбирает указанный в запросе шаблон, компилирует, исполняет его и выдает Selenium-у. Также планируется написать несколько тегов для того, чтобы описывать таблицу команд при помощи простой wiki-разметки и иметь возможность вставить комментарии в тесты, так, чтобы шаблонизатор автоматически преобразовывал их в comment действия. Если говорить об более высокой степени вложенности тестов, то здесь нужно сказать, что TestRunner.html может принимать атрибут test для безусловной загрузки TestSuite, например: http://yoursite/selenium/TestRunner.html?test=suites/AllTheTests. html Таким образом, вы сможете создавать у себя группы тестов и при помощи какой-нибудь технологии, PHP например, формировать любой набор тестов, который вам необходим и выдать Selenium-у тот TestSuite, какой вам нужен в данный момент. Вам только нужно будет сделать index.html страницу, где бы располагались ссылки на различные наборы тестов. Вот в принципе и все. 33


PHP Inside №14

Функциональное тестирование при помощи Selenium

setUp/tearDown В Selenium нет отдельных setUp и tearDown команд для создания и удаления фикстур. Первое решение, которое приходит в голову, это использование обычных команд open для посещения определенных страниц. Для фикстур можно создать отдельную папку в проекте и положить туда 2 php скрипта. Тест будет выглядеть приблизительно так: Некоторый тест Open

./fixtures/Project1SetUp.php

[Другие команды] Open

./fixtures/Project1TearDown.php

Внутри Project1SetUp.php мы заполняем базу данных данными, которые могут потребоваться при тестировании, а в Project2TearDown.php – очищаем ее. Единственная проблема, которая есть при таком подходе – это отсутствие какой-либо гарантии, что tearDown все-таки будет выполнен, поэтому в Project1SetUp.php приходится предварительно очищать базу данных, чтобы не возникало ситуации дублирования данных.

Рекомендации по формированию html-кода страниц тестируемого web-приложения для облегчения тестирования Функциональные тесты могут быть довольно хрупкими. Все зависит от того, как вы пишите ваши тесты. Сравните: <tr>

с

<td>click</td> <td>login</td> <td> </td> </tr> <tr>

<td>click</td> <td>document.links[4]</td> <td>  </td> </tr>

Первый пример намного более понятный, гораздо стабильнее, так как не зависит от дизайна (до определенной степени). Основная проблема web-приложений заключается в том, что их делают очень сложными для автоматизированного тестировани. То есть эти приложения делаются для того, чтобы с ними хорошо работал человека, а не машина, например, в лице Selenium. После введения в рабочий процесс функционального тестирования, вы будете уделять этому гораздо большее внимание. Большинство значимых тегов получат уникальные идентификаторы, гораздо больше станет таких атрибутов, как id, title, alt и т.д.

34


PHP Inside №14

Функциональное тестирование при помощи Selenium

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

Что полезно знать о Selenium Автоматическое выполнение тестов и сохранение результатов тестирования. TestRunner.html понимает такой атрибут, как auto=true. Если этот параметр указан, то есть строка запроса выглядит следующим образом http://project1/tests/selenium/TestRunner.html?auto=true, то Selenium сразу же начнет выполнение всех тестов в режиме run. После выполнения тестов он попытается сделать запрос по пути /postResults, куда он передаст результаты выполнения тестов. Значение /postResults можно изменить, передав в запрос параметр resultsUrl. Вы можете написать свое приложение на php, которое будет сохранять и интерпретировать результаты выполнения тестов. Все это позволяет использовать Selenium как часть автоматизированного build-а приложения.

Driven-mode или Selenium может управляться из стороннего приложения Driven Selenium – это режим выполнения тестов, когда браузер находится под контролем другого приложения. Это может быть, например, xUnit тест, который посылает браузеру определенные команды и получает на них ответы. В настоящее время такое возможно только для языков Java, . Net, Ruby и Python. К сожалению PHP пока в этом списке нет, хотя разработчики утверждают, что driven-mode может использоваться с любым языком, который умеет работать с http. Возможно, что в скором времени кто-то и возьмется за написание модуля для работы с Selenium из php. При использовании Driven-mode мы можем успешно решить все проблемы с фикстурами, однако это затрудняет написание тестов людьми, не знакомыми с языками программирования. Вот пример использования Java Driven Selenium: public void testOKClick() { selenium.verifyTitle("First Page"); selenium.open("/TestPage.html"); selenium.click("OKButton"); selenium.verifyTitle("Another Page"); }

Так как журнал у нас называется PHP-inside, на этом разговоры о Driven-mode пока прекратим.

35


PHP Inside №14

Функциональное тестирование при помощи Selenium

Полезные ссылки, касающиеся Selenium и функциональных тестов. http://selenium.thoughtworks.com/ - основной сайт Selenium. http://jira.public.thoughtworks.org/browse/SEL - здесь находятся все feature requests и tickets. svn://selenium.codehaus.org/selenium/scm/trunk/code/javascript – javascript репозиторий Selenium. Вы можете выкачать весь репозиторий этого проекта, но это около 30 мегабайт. http://www.oslutions.com/docs/selenium/user-extensions.js здесь можно найти довольно таки интересный extension к Selenium, который упрощает процесс написания тестов людьми, далекими от программирования. http://www.oslutions.com/blog/archives/2005/06/selenium_extent. html - здесь находится описание вышеуказанного расширения. http://www.geocities.com/softwarepr/projects/seleniumassist.html - небольшой проект, который позволяет частично автоматизировать процесс написания тестов для больших форм. http://www.sqa-test.com/w_paper1.html - полезная статья насчет функционального тестирования. http://selenium.opiumpost.org/wiki/published/ - здесь test-case-ы, которые генерятся из instiki-wiki. http://www.xprogramming.ru/XPRules/AcceptanceTests.html немного информации насчет тестирования и функционального тестирования на русском языке. http://wtr.rubyforge.org/ - альтернативный проект, который работает только под IE. Написан на Ruby.

Интервью с Ильей Альшанетским

36


PHP Inside №14

Интервью с Ильей Альшанетским

Люди Интервью с Ильей Альшанетским Интервьюировал: Андрей Олищук Один из активистов разработки PHP, автор FUDforum и eGroupware дал эксклюзивное интервью нашему журналу Илья Альшанетский (Ilia Alshanetsky) известен нам больше всего как один из ведущих разработчиков PHP, автор набирающего популярность движка для форумов FUDforum (http://fudforum.org), участник многих открытых проектов. Илья любезно согласился дать интервью специально для нашего журнала. nw: Как вы пришли в PHP? Это произошло по чистой случайности или было вашим обоснованным выбором? Ilia: Я начал использовать PHP в начале 1998 года, когда пришло время для перехода в веб. Тем не менее, мой опыт веб-программирования начался отнюдь не с PHP. Вначале я был ярым поклонником Perl и терроризировал провайдера своими скриптами, которые нагружали процессор и работали с натугой. В попытке справиться со мной администратор посоветовал обратить внимание на PHP, который был предположительно более быстрым и лучше структурированным. Администратор знал о моих жалобах на то, что Perl ужасно беспорядочен, и поэтому надеялся, что его доводы меня убедят. Таким образом я и попробовал эту вещь, называемую PHP, и с тех пор не возвращался к старому. Если мне не изменяет память, тогда это была версия 3.0.3. Примерно в 1999 – 2000 годах я начал ковыряться уже в исходниках – писал патчи, собственные функции, и так продолжалось до тех пор, пока я не написал первое расширение под названием shmop. После этого я в основном участвовал в коммерческих PHPразработках, которые заключались в написании новых расширений, функций и “перебирании” PHP в целях увеличения его производительности. В 2003 году я снова вернулся к активной работе над самим языком и с тех пор являюсь одним из нескольких активных разработчиков. nw: Вы были релиз-менеджером в PHP 4.3.3+. Каковы функции релиз-менеджера в PHP? Расскажите также о процессе разработки и функциях команды контроля качества. В целом разработчики PHP - это хаотичное сообщество или хорошо организованная команда?

37


PHP Inside №14

Интервью с Ильей Альшанетским

Ilia: В задачу релиз-менеджера PHP входит, как это понятно уже из названия, выпуск новых версий PHP в свет. Звучит довольно просто, но на самом деле это очень комплексный процесс, самое сложное в котором – это социальная составляющая, а вовсе не техническая. Линус очень метко сказал по этому поводу, что управлять командой разработчиков, а тем более, волонтеров, работающих над проектом Open Source, это словно пасти стаю котов. В PHP нет специальной политики по выпуску новых релизов, поэтому каждый релиз-менеджер имеет свой подход и собственное видение, в зависимости от ситуации. Именно поэтому я могу описать процесс только со своей точки зрения. С PHP 4.3 я работал по простой схеме. Решения, стоящие за «давайте выпустим новый релиз», принимались на основании количества и природы исправленных ошибок. Если количество исправлений приближалось к тридцати, я начинал пинать разработчиков, чтобы они посмотрели, нет ли каких-либо критических или важных исправлений, которые еще ждут своего часа. Если таковых не оказывалось, то обычно в течение недели появлялся RC1 (релиз кандидат 1) и далее выходило еще несколько последующих RC, пока не достигалась определенная стабильность версии.

Управлять командой разработчиков, а тем более, волонтеров, работающих над проектом Open Source, это словно пасти стаю котов

О выходе каждого релиз-кандидата сообщалось в лист рассылки с той целью, чтобы люди могли потестировать новый релиз и дать возможность поправить ошибки по горячим следам. Самый последний релиз-кандидат выходил в режиме «CVS lock», который означал «НИЧЕГО НЕ ДОБАВЛЯТЬ». Таким образом новая функциональность приносилась в жертву стабильности релиза. Позволялось добавлять только патчи для повышения уровня безопасности или улучшения обратной совместимости. Если такие патчи вносились, то выпускался очередной релиз-кандидат. Ветка 4.3 была закрыта для добавления новых возможностей – принимались только изменения, состоящие из исправлений ошибок или улучшений в оптимизации. Все остальное отклонялось. Обычно новые возможности обсуждаются во внутреннем листе рассылки, но иногда просто добавляются в CVS и, если никто не будет протестовать, остаются там. Очевидный факт – в последнее время лист рассылки становится все менее продуктивным. Большинство предпочитает обсуждать новые возможности между собой по электронной почте, интернет-пейджеру или даже IRC. И если там они приходят к согласию, то новые возможности добавляются в CVS.

PHP - это, скорее, контролируемый хаос, который до сих пор показывал себя довольно работоспособным

Функции QA Team (команды контроля качества), которая насчитывает примерно пять активных участников, очень многочисленны. Сюда входит и тестирование релизов, правка ошибок, удаление ложных отчетов из системы управления ошибками (система организована в основном стараниями Jani Taskinen), раздача пинков разработчикам, которые что-либо поломали. В придачу к этому мы также поддерживаем работу тестового комплекта для PHP. Будучи одним из управляющих проектом, могу сказать, что PHP - это, скорее, контролируемый хаос, который до сих пор показывал себя довольно работоспособным.

38


PHP Inside №14

Интервью с Ильей Альшанетским

nw: Популярность вашего FUDforum в настоящее время растет. Но в самом начале почему вы взялись за создание нового движка для форумов, ведь тогда уже существовали многие популярные скрипты? Ilia: Причины для разработки своего собственного форума были довольно просты: я работал над проектом, которому был нужен движок форума, способный выдерживать ОЧЕНЬ большой траффик. Я не планировал браться за самостоятельную разработку и хотел подобрать готовое решение. Однако после продолжительных тестов я определил, что подходящих мне форумов не существует, так как одни были ни к черту медленными, вторые имели очень ограниченный функционал, в исходниках третьих можно было сломать ногу, а четвертые представляли собой комбинацию всего выше перечисленного. Тогда я и решил написать что-то свое, что соответствовало бы моим требованиям, а по завершению работ было решено выложить проект в публичный доступ.

Причины для разработки своего собственного форума были довольно просты: я работал над проектом, которому был нужен движок форума, способный выдерживать ОЧЕНЬ большой траффик

nw: В чем заключается изюминка FUDforum? Почему люди выбирают именно его? Ilia: FUDforum расшифровывается как Fast (быстрый) Uncompromising (устойчивый) Discussion Forum (форум для дискуссий). Собственно, это он собой и представляет. Я думаю, что привлекательность FUDforum для многих людей кроется в его широком функционале, который включает такие возможности, как встроенная проверка орфографии, интеграция с NNTP/листами рассылки, поддержка RSS/XML, возможность генерировать из топиков форума PDF-документы без установки дополнительных расширений, гибкая система контроля доступа, поддержка различных СУБД, i18n (интернационализация) и многое другое. Несмотря на обилие функций, FUDforum остается крайне быстрым – гораздо быстрее других форумов, о которых я знаю. Он выдерживает 100+ запросов в секунду на большинстве страниц. И в конце концов, FUDforum очень безопасен – за три года его существования было найдено только 4 ошибки в системе безопасности, и только одна из них, обнаруженная в 2002 году, была критической. nw: Как много времени вы тратите на проекты с открытым кодом? Если представить диаграмму pie-chart (пирог) вашего рабочего времени, то какую часть будут занимать PHP, FUDforum, eGroupware и другие Open Source проекты?

FUDforum очень безопасен – за три года его существования было найдено только 4 ошибки в системе безопасности

Ilia: Что ж, я трачу ОЧЕНЬ много своего времени на FUDforum, так как большая часть моей оплачиваемой работы связана с оказанием техподдержки и услуг по его настройке и кастомизации. Если представить мой усредненный день, то около шести часов я трачу на работу, связанную с FUDforum'ом, в то время как на PHP у меня уходит примерно час такого дня. Над eGroupware я сейчас практически не работаю, поэтому здесь поставим ноль. Другие открытые проекты занимают примерно 20-30 минут, в зависимости от ситуации. В основном мое участие в проектах Open Source заключается в работе с багами и выпуске патчей.

39


PHP Inside №14

Интервью с Ильей Альшанетским

nw: Как такой успешный IT-специалист, как вы, проводит свободное время? Ilia: Свободное время? Что это? В несколько часов, которые удается ухватить, я стараюсь куда-нибудь выбраться из дома – сходить в кино с друзьями, немного пофотографировать и позаниматься в спортзале. nw: Я не могу не спросить: у вас такие русские имя и фамилия, ваши корни идут из России, или это какая-то случайность? Ilia: Я родился в стране, которая раньше называлась СССР, а точнее, теперь в независимом государстве – на Украине. Я немного говорю по-русски, но большую часть жизни я прожил вне Украины/СССР.

Ссылки Сайт движка форумов: FUDforum: http://fudforum.org Домашняя страничка Ильи: http://ilia.ws

FAQ: Как принять участие в развитии PHP?

40


PHP Inside №14

FAQ: Как принять участие в развитии PHP?

FAQ: Как принять участие в развитии PHP? Авторы: Антон Довгаль [tony2001] Алексей Борзов [SadSpirit] Александр Войцеховский [young] Андрей Олищук [nw] Если вы хотите помочь PHP, у вас могут возникнуть вопросы. Пришло время на них ответить PHP, как и практически любой другой Open Source проект, не развивается сам по себе. Ядро языка, его модули, классы PEAR, документация - ничего не появляется из ниоткуда по мановению волшебной палочки. Все это - труд множества людей, труд, результатами которого мы с вами можем пользоваться бесплатно во свое благо. Многие из нас с вами могут помочь в развитии языка. В этой статье, построенной в виде списка часто задаваемых вопросов (FAQ), мы расскажем, как лично ВЫ можете принять участие. PHP ждет вас!

1. Тестирование ядра и модулей (расширений) 1.1. Я хочу принять участие в тестировании. Где взять нужный релиз для тестирования? http://snaps.php.net, либо из CVS ( http://php.net/anoncvs.php). 1.2. Я нашел баги, куда мне сообщать? http://bugs.php.net. 1.3. Что я должен указать в баг-репорте (отчете о найденой ошибке)? http://bugs.php.net/how-to-report.php - тут все указано. Язык сообщений - только английский.

41


PHP Inside №14

FAQ: Как принять участие в развитии PHP?

2. Разработка ядра и модулей (расширений) 2.1. У меня есть опыт программирования на C. Я хочу помочь в развитии PHP, но не знаю, с чего начать (что именно писать). Куда мне обратиться за советом? Можно взять любой открытый баг-репорт и начать его исследовать. Заодно можно немного узнать внутренности PHP и понять причины бага. Возможно, даже сделать патч или хотя бы найти дополнительную информацию (уточнение по ошибке) и добавить в репорт. Это самый лучший путь для начала. 2.2. У меня есть патч или новое расширение. Куда я могу его отправить? Смотря какой патч и какое расширение. Патч для core (ядра) в лист рассылки php-internals, патч для модулей PECL - в pecl-dev. Важное замечание: не чините то, что не поломано! Новые модули тоже в pecl-dev, но тут важный вопрос - лицензирование. PECL не может распространять GPL-licensed модули или модули, которые линкуются с GPL-либами (что, на самом деле, одно и то же). Модуль желательно подробно описать и приложить к письму URL на его исходники. Подписаться на листы рассылки можно здесь: http://www.php.net/mailing-lists.php

Можно взять любой открытый баг-репорт и начать его исследовать это самый лучший путь для начала

2.3. Нужно ли мне предварительно обсуждать мои патчи или расширения в сообществе и доводить их до стабильного состояния? Стадию обсуждения обойти никак нельзя. Что касается стабильности, то она не обязательна, но, конечно же, для начала нужен какой-то работающий код. 2.4. Есть ли какие-то требования к новому коду? Да, есть стандарты кодирования, которые можно найти здесь: http://cvs.php.net/php-src/CODING_STANDARDS 2.5. Кроссплатформенность нового кода обязательна? Если есть возможность, то код желательно сделать таковым. С другой стороны, существуют такие модули, которые работают только на одной ОС (к примеру - COM).

3. Пакеты PEAR 3.1. У меня есть полезный класс / пакет классов, и я хотел бы включить его в PEAR. Каким требованиям он должен соответствовать? Какова процедура предложения нового пакета? Для того, чтобы пакет был принят, он должен набрать сумму не менее 5 голосов при голосовании в системе PEPr (PEAR Proposal System, http://pear.php.net/pepr/pepr-overview.php). Прежде чем дело дойдёт до голосования, впрочем, пакет придётся подготовить. • Выберите ему название: не стоит слишком долго раздумывать, если вы выберете неудачное название, вам на это укажут в процессе обсуждения более матёрые разработчики. 42


PHP Inside №14

FAQ: Как принять участие в развитии PHP?

• Выберите лицензию: список возможных лицензий опубликован на сайте PEAR: http://pear.php.net/group/docs/20040402-la.php. Отмечу: все эти лицензии позволяют использовать пакеты в коммерческих приложениях. • Учтите следующую очевидную вещь: мало кто будет с лупой в руках изучать, как работает ваш код. Но для того, чтобы увидеть, что код неряшлив и плохо документирован, лупа не нужна. • Приведите код в соответствие со стандартами кодирования PEAR: http://pear.php.net/manual/en/standards.php. Не стоит начинать свою карьеру разработчика PEAR с флейма о том, что стандарты кодирования PEAR - отстой. Менять их всё равно не будут, а осадок останется. • Ваш код должен содержать комментарии в формате phpDocumentor. Краткое описание их синтаксиса есть в документации PEAR: http://pear.php.net/manual/en/standards.header.php, значительно более подробное - в документации самого phpDocumentor: http://manual.phpdoc.org/

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

• Подготовьте примеры использования вашего пакета. Запустить пару скриптов-примеров гораздо проще, чем разбираться с тем, как пакет теоретически должен работать. • Опубликуйте свой пакет в интернете. Желательно в двух экземплярах: в архиве, который можно установить инсталлятором pear, и в виде файлов .phps Всё, теперь можно вносить в PEPr новое предложение. Прежде всего, если у вас нет учётной записи на сайте PEAR, вам потребуется зарегистрироваться на http://pear.php.net/account-request.php и дождаться одобрения регистрации. Сам процесс внесения предложения хорошо описан в документации http://pear.php.net/manual/en/newmaint.proposal.php, добавлю лишь следующее: если вы решите облегчить себе задачу и не выполнить какой-либо из вышеописанных пунктов, то в процессе обсуждения вашего предложения вам обязательно на это укажут и настойчиво попросят исправиться. Теперь, наконец, перейдём к вопросу о функциональности и о том, какие пакеты будут иметь проблемы с прохождением голосования. В первую очередь: пакеты PEAR должны предоставлять достаточно абстрактное решение достаточно общей задачи.

Задача, которую вы решаете, должна возникать в различных приложениях, а не только в одном конкретном вашем!

Другими словами, задача, которую вы решаете, должна возникать в различных приложениях (а не только в одном конкретном вашем!), а решение её должно быть легко интегрируемо в чужие приложения (если ваш пакет выводит HTML, он не должен выводить его только красными буквами и только на зелёном фоне). Если ваш пакет не удовлетворяет этим условиям, у него практически нет шансов быть принятым в PEAR. Вторая проблема заключается в том, что разработчики PEAR (которые и будут голосовать) традиционно не любят дублирование функциональности.

43


PHP Inside №14

FAQ: Как принять участие в развитии PHP?

Поэтому, если ваш пакет дублирует функциональность уже существующего, будьте готовы к вопросам: • Чем ваш пакет лучше существующего? • Что мешает встроить дополнительную функциональность в существующий пакет? Если у вас нет удовлетворительных ответов на эти вопросы, то у вашего пакета, опять же, не слишком много шансов пройти голосование. Подумайте лучше о том, как скооперироваться с разработчиком / разработчиками имеющегося пакета. Напоследок, как водится, проповедь. Включение вашего пакета в PEAR - лишь первый шаг. Дальше потребуется его поддержка и развитие, поэтому оцените, будет ли у вас на это время? Если нет, то лучше не предлагать пакет вообще. 3.2. Я использовал пакет PEAR в своём проекте и накопил неплохой опыт. Как я могу помочь с расширением и улучшением документации этого пакета? В первую очередь как разработчик должен сказать, что написание документации является одним из последних по увлекательности занятием, поэтому любая помощь в этом вопросе будет принята с благодарностью. Перечислю варианты этой помощи в порядке увеличения трудоёмкости. • Написание примеров использования; • Написание статей о пакете;

Написание документации является одним из последних по увлекательности занятием, поэтому любая помощь в этом вопросе будет принята с благодарностью

• Обновление официальной документации. Под примерами использования здесь понимаются файлы .php, устанавливаемые вместе с пакетом. Естественно, предполагается, что вы не вырвете с мясом кусок из своего приложения (который будет невозможно понять вне контекста этого приложения), а придумаете некий характерный пример, представляющий одну из граней функциональности пакета. Что касается статей, здесь нельзя сказать ничего определённого, всё зависит от вас: вы можете опубликовать их где угодно и в каком угодно формате. С официальной документацией несколько сложнее; документация PEAR (как и документация языка PHP) пишется в формате DocBook XML ( http://www.docbook.org), и для того, чтобы сгенерировать HTML-версию документации и соответственно проверить правильность своих изменений, вам придётся установить набор утилит для работы с DocBook. Это тривиально под *nix, под Windows вам придётся использовать Cygwin, http://cygwin.com/. Если вас всё же не пугает изучение нового формата и работа с ним, то рекомендую в первую очередь ознакомиться с PHP Documentation Howto, http://www.php.net/manual/howto/

44


PHP Inside №14

FAQ: Как принять участие в развитии PHP?

Вне зависимости от того, какую документацию вы подготовили, для связи с разработчиками пакетов лучше всего использовать баг-трекер PEAR http://pear.php.net/bugs/, который позволяет кроме сообщений об ошибках заводить и предложения по улучшению пакетов. Заведите новое предложение, указав в нём ссылку на подготовленную вами документацию (баг-трекер PEAR не позволяет присоединять файлы, а вставленный в сообщение код не слишком легко читать). Для обсуждения вопросов, связанных с документацией PEAR, существует специальный список рассылки pear-doc@lists.php.net. Если вы хотите посвятить документированию более-менее продолжительное время, в первую очередь подпишитесь на него. Там вам ответят на все возникающие вопросы.

4. Написание и перевод документации и статей 4.1. Я имею опыт в определенной области PHP-разработки и готов им поделиться, написав/переведя статью. Что необходимо сделать, чтобы мою статью прочитало много заинтересованных людей? Если ваша статья действительно представляет интерес, то она не пропадет. На начальной стадии, вы можете обсудить ее на какомлибо популярном PHP-форуме (к примеру, на http://phpclub.ru или http://php.com.ua). Затем предложите опубликовать статью в проекте «Детали» PHPClub (для этого напишите письмо с URL статьи на young@phpclub.net или постучитесь в ICQ: 86887211). Также вы можете обратиться в редакцию нашего журнала «PHP Inside» и предложить свою статью для публикации в одном из ближайших номеров. Для этого напишите письмо с URL статьи (или приложите архив со статьей к письму) на адрес: nw@phpinside.ru или постучитесь в ICQ: 223-658-175. Конечно, в сети существует и множество других проектов, которым вы можете предложить написанный вами текст, но указанных двух будет достаточно для того, что бы о вашем материале узнало подавляющее большинство отечественных PHP-разработчиков. 4.2. Я хочу помочь с переводом официальной документации PHP (мануала) на русский язык. К кому мне обратиться и как происходит процесс работы?

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

Обратиться можно в список рассылки: doc-ru@lists.php.net, при этом необходимо учитывать, что все обсуждения ведутся исключительно на английском языке, не смотря на присутствие среди подписчиков большого количества русскоязычных разработчиков. Чтобы узнать, как происходит процесс работы по подготовке официальной документации, ознакомтесь с информацией отсюда: http://doc.php.net/php/dochowto/ — там вы найдете ответы на большинство вопросов. 4.3. Я могу переводить тематические статьи. Как мне выбрать статью для перевода?

45


PHP Inside №14

FAQ: Как принять участие в развитии PHP?

Это не сложно. Для выбора статьи можно придерживаться нескольких правил: • Перевести статью, которую вы читаете для своих нужд. Если вам интересна та или иная статья на английском языке и вы читаете ее для решения своих задач, запишите перевод и поделитесь им с сообществом. В этом случае рекомендуем предварительно списаться с администратором проекта, на котором вы планируете разместить перевод. В любом случае не забывайте про авторские права, которые распространяются не только на перепечатку оригинала, но и на его перевод. • Многие проекты имеют список материалов, которые они хотели бы опубликовать в переведенном виде. К примеру, у нашего журнала существует соответствующий раздел форума: http://phpinside.ru/?q=node/113. Здесь вы сможете не только обсудить перевод, но и предложить ссылку на интересную статью для перевода другими. • Напишите администратору проекта, на котором вы хотите опубликовать будущий перевод. Он вам наверняка предложит несколько интересных вариантов.

5. Разработка PHP-приложений с открытым кодом 5.1. Как мой открытый проект, написанный на PHP, может помочь в развитии самого языка? Помните, что разрабатывая и продвигая свои открытые проекты, вы положительно влияете и на продвижение самого PHP. Операционная система Windows сейчас пользуется огромной популярностью не потому, что она гениально написана, а потому что именно к этой ОС существует множество разнообразных приложений. То же самое и с PHP. Подробнее о том, как организовать и продвигать свой Open Source проект, можно прочитать в одной из статей PHP Inside #13 (http://phpclub.ru/detail/magazine/2005/08/)

Помните, что разрабатывая и продвигая свои открытые проекты, вы положительно влияете и на продвижение самого PHP

5.2. У меня нет собственного открытого проекта, но я готов помочь в разработке/тестировании/локализации/документировании других проектов. Как мне присоединиться к одному из них? Здесь существует несколько путей. • Попробуйте начать с тех проектов, которые вы уже используете. К примеру, если вы активно используете какую либо CMS, то наверняка находили в ней ошибки или недоработки, которые можно поправить. Или вам уже приходилось писать к ней модули и расширения? Поделитесь ими с авторами проекта.

46


PHP Inside №14

FAQ: Как принять участие в развитии PHP?

В любом случае желательно не просто писать разработчикам этого проекта: «Чем я могу помочь?», - а сразу прислать патч/модуль/языковой файл или прочее. В исходниках большинства открытых проектов уже имеются файлы ToDo или Roadmap, в которых указаны направления желаемого развития проекта. • Посетите раздел «Project Help Wanted» на сайте http://sourceforge.net/people/. Там можно подобрать посильную для вас задачу. Участие в таких проектах позволяет накопить некоторый опыт и, возможно, известность в определенных кругах.

Библиотека dbtree для работы с деревьями Nested Sets

47


PHP Inside №14

Библиотека dbtree для работы с деревьями Nested Sets

Идеи Библиотека dbtree для работы с деревьями Nested Sets Автор: Кузьма Феськов При разработке веб-приложений программисты часто сталкиваются с управлением иерархическими структурами. Dbtree облегчит жизнь Класс базируется на основе библиотеки Максима Полторака phpDBTree 1.4 (http://dev.e-taller.net/dbtree/). Версию dbtree 2.1. можно загрузить отсюда: http://php.russofile.ru/resources/archives/0000/dbtree_2_01.zip. Последнюю версию библиотеки и документации всегда можно найти на сайте http://php.russofile.ru/ru/authors/sql/nestedsets01/. В приложении к данному номеру журнала вы сможете найти как саму библиотеку, так и демонстрационный скрипт, запустив который, вы сможете попробовать работу библиотеки в действии.

Особенности библиотеки Основной особенностью библиотеки является, то, что все запросы в методах переписаны согласно стандартам ANSI и работают без изменений на подавляющем большинстве баз данных. Поддержка транзакций на основе класса ADODB. Если вы не хотите использовать ADODB, но хотите поддерживать механизм транзакций на основе используемого вами драйвера базы данных, реализуйте их самостоятельно, переписав соответствующие функции в прилагаемом драйвере-примере для базы MySql. Библиотека поддерживает кэширование SQL запросов на на основе класса ADODB, но если хотите поддержать кэширование на основе используемого вами драйвера базы данных, реализуйте его самостоятельно, переписав соответствующие функции в прилагаемом драйвере-примере для базы MySql. Библиотека работает с поддержкой технологии GetText. Эта возможность опциональна. Класс сам определит подключено ли соответствующее расширение. Если GetText отсутствует, класс будет выводить сообщения средствами PHP.

48


PHP Inside №14

Библиотека dbtree для работы с деревьями Nested Sets

Примеры работы Инициализация класса Синтаксис использования библиотеки очень прост: <?php requare_once('dbtree.class.php'); $dbtree = new dbtree($table, $prefix, $db); ?>

После чего, переменная $dbtree содержит объект для работы с деревьями. Давайте рассмотрим параметры конструктора: $table – содержит название таблицы, в которой хранится интересующее нас дерево; $prefix - содержит уникальный префикс для всех полей таблицы с данными, например, если ваше поле называется user_id, то уникальный префикс будет user; $db – это объект ADODB или объект прилагаемого к классу демонстрационного драйвера, или объект написанного вами драйвера.

Конкретные примеры В качестве практического примера я рассмотрю возможность хранения в рамках одной таблицы нескольких деревьев. Также мы предположим, что структура нашего сайта хранится в виде дерева Nested Sets. Мы посмотрим, какие возможности предоставляет нам библиотека для работы с сайтом и для его обслуживания. Итак, приступим. Исходные данные: мы будем использовать дерево для хранения структуры сайта (то есть перечень его разделов). Чтобы усложнить задачу, предположим, что сайт у нас поддерживает 2 языка. Это значит, что структур сайта у нас будет 2. Чтобы не множить таблицы, мы оба дерева (русское и английское) будем хранить в одной таблице. В этом конкретном примере мы с вами посмотрим, какие возможности библиотека предоставляет создателю сайта, и каким образом вы можете облегчить свою жизнь. Структура таблицы Table sections CREATE TABLE `sections` ( `section_id` bigint(20) NOT NULL default '0', `section_left` bigint(20) NOT NULL default '0', `section_right` bigint(20) NOT NULL default '0', `section_level` int(11) default NULL, `section_lang` varchar(2) NOT NULL default '', `section_name` varchar(255) NOT NULL default '', `section_path` varchar(255) NOT NULL default '', `section_full_path` varchar(255) default NULL, PRIMARY KEY (`section_id`), UNIQUE KEY `section_id` (`section_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8

49


PHP Inside №14

Библиотека dbtree для работы с деревьями Nested Sets

Первые 4 поля имеют отношения непосредственно к структуре дерева и напрямую в них вмешиваться не следует. section_lang служит семафором, указывая на принадлежность элемента к определенному дереву. section_name – имя раздела для вывода его пользователю. section_path – минимальный элемент пути к разделу, например, sample. section_full_path будет содержать полный путь по сайту до указанного элемента, например, my_site/sample/. Настраиваем драйвер базы данных Я приведу свои настройки для примера. В дальнейшем буду считать, что вы эти действия уже выполнили и буду их опускать, считая, что объект драйвера бызы данных будет у вас в переменной $db. Настраиваем ADODB: <?php /** Указываем путь до библиотеки ADODB */ define('ADODB_DIR', $_SERVER['DOCUMENT_ROOT'] . '/libs/adodb'); /** Название драйвера базы данных ADODB */ define('DB_DRIVER', 'mysql'); /** DB Host */ define('DB_HOST', 'localhost'); /** DB имя пользователя */ $DB_USER = 'user'; /** DB пароль пользователя */ $DB_PASSWORD = 'password'; /** DB название базы данных */ define('DB_BASE_NAME', 'dbtree_test'); /** Глобально управляем кэшированием */ define('DB_CACHE', TRUE); /** Подключаем ADODB библиотеку */ require_once(ADODB_DIR . '/adodb.inc.php'); /** Настраиваем ADODB */ $ADODB_FETCH_MODE = 2; // ASSOC $ADODB_CACHE_DIR = ADODB_DIR . '/ADOdbcache'; $db = &ADONewConnection(DB_DRIVER); $db->Connect(DB_HOST, $DB_USER, $DB_PASSWORD, DB_BASE_NAME); /** Настраиваем клиента на использование кодировки UTF-8 */ if ('mysql' == DB_DRIVER) { $sql = 'SET NAMES utf8'; $res = $db->Execute($sql); } ?>

Настраиваем драйвер-пример для MySql: <?php /** DB Host */ define('DB_HOST', 'localhost'); /** DB user name */

50


PHP Inside №14

Библиотека dbtree для работы с деревьями Nested Sets

$DB_USER = 'root'; /** DB user password */ $DB_PASSWORD = ''; /** DB name */ define('DB_BASE_NAME', 'russofileru_php'); /** Управление кэшированием класса */ define('DB_CACHE', FALSE); require_once('db_mysql.class.php'); $db = new db(DB_HOST, $DB_USER, $DB_PASSWORD, DB_BASE_NAME); $sql = 'SET NAMES utf8'; $db->Execute($sql); ?>

Первоначальная инициализация таблицы Далее, нам необходимо заполнить таблицу первоначальными инициализационными данными. <?php $id = $db->GenID('sections_seq', 1); $sql = 'INSERT INTO sections (section_id, section_left, section_right, section_level, section_lang, section_name, section_path, section_full_path) VALUES (' . $id . ', 1, 2, 0, 'ru', 'Root', '', '')'; $db->Execute($sql); $id = $db->GenID('sections_seq', 1); $sql = 'INSERT INTO sections (section_id, section_left, section_right, section_level, section_lang, section_name, section_path, section_full_path) VALUES (' . $id . ', 1, 2, 0, 'en', 'Root', '', '')'; $db->Execute($sql); ?>

Теперь наша таблица содержит все первоначальные данные и мы можем начинать работу. Добавление элементов в структуру сайта Какие этапы нам нужно пройти? Во-первых, получить все имеющееся дерево для нужной нам языковой версии сайта. Во-вторых, вывести его на экран пользователю, чтобы он выбрал, у какого раздела сайта мы создаем подраздел. В-третьих, получить данные о новом разделе от пользователя. В-четвертых, создать новый раздел в структуре дерева сайта. Разумеется, есть еще подэтапы проверки введенных пользователем данных, но мы их опустим, как опустим 3 этап, потому что он не имеет отношения к решаемой проблеме. Мы могли бы опустить и 2 этап, но так как нам он часто будет нужен, я приведу его 1 раз и далее буду ссылаться на этот пример. <?php // Первый и второй этапы // Инициализируем класс

51


PHP Inside №14

Библиотека dbtree для работы с деревьями Nested Sets

$dbtree = new dbtree('sections', 'section', $db); // Получаем все дерево для русской версии сайта $dbtree->Full(array('section_id', 'section_level', 'section_name', 'section_lang'), array('and' => array('section_lang = 'ru''))); // Проверяем, возникли ли какие-либо ошибки в процессе работы класса if (!empty($dbtree->ERRORS_MES)) { // обрабатываем ошибки } else { // Обрабатываем полученное дерево while ($item = $dbtree->NextRow()) { // Делаем отступы (лесенка) согласно уровню вложенности $item['spacer'] = str_repeat(' ', 6 * $item['section_level']; $sections[] = $item; } } // Выводим массив $sections на экран, // чтобы дать возможность пользователю выбрать, // у какого раздела создать подраздел. ....... ?>

Третий этап мы пропускаем, потому что принятие данных от пользователя или из другого источника не является предметом материала, это вы и сами прекрасно реализуете. А мы переходим к 4 этапу. Данные получены, проверены и нам надо на их основе добавить новый раздел. Давайте перечислим, какие данные нам нужны, чтобы добавить новый раздел: section_id – номер раздела К КОТОРОМУ добавляем новый подраздел, section_name – имя раздела, section_lang – идентификатор языковой версии дерева, section_path – отрезок пути для данного конкретного раздела. При добавлении нового раздела нам необходимо рассчитать section_full_path. Для этого нам необходимо получить всех родителей добавляемого раздела и сложить их section_path в единый путь. Разместим все эти данные для удобства в массиве $section: $section['section_id'], и так далее. <?php $dbtree = new dbtree('sections', 'section', $db); // Получаем всех родителей раздела $dbtree->Parents($section['section_id'], array('section_level', 'section_path'), array('and' => array('section_lang = '' . $section['section_lang'] . '''))); if (!empty($dbtree->ERRORS_MES)) { // обрабатываем ошибки } else { // Обрабатываем полученную часть дерева while ($item = $dbtree->NextRow()) { // Получаем section_full_path if (0 <> $item['section_level']) { $path .= $item['section_path'] . '/'; } } // Заносим данные в массив нового раздела $section['section_full_path'] = $path . $section['section_path'] . '/'; // Добавляем новый раздел $id = $section['section_id'];

52


PHP Inside №14

Библиотека dbtree для работы с деревьями Nested Sets

unset($section['section_id']); $id = $dbtree->Insert($id, array('and' => array('section_lang = '' . $section['section_lang'] . ''')), $section); if (!empty($dbtree->ERRORS_MES)) { // обрабатываем ошибки } } ?>

К этому моменту, если не произошло ошибок, у нас в русском языковом дереве появился новый раздел. Давайте заодно рассмотрим удаление раздела. <?php // Удаляем раздел. На входе section_id удаляемого раздела // и section_lang $dbtree = new dbtree('sections', 'section', $db); $dbtree->Delete($section_id, array('and' => array('section_lang = '' . $section_lang . '''))); ?>

Перемещение раздела Перемещение раздела к другому родителю с данным классом также не представляет особой проблемы. Какие этапы нам необходимо пройти? Во-первых, выбрать узел (также будут перемещаться и все его дети), который хотим переместить. Во-вторых, требуется выбрать нового родителя (родитель не может находиться в пределах ветки, выбранной для перемещения). В-третьих, на основании этих данных переместить ветку к новому родителю. Давайте посмотрим как это делается на практике. Первые два этапа мы опустим, поскольку они сводятся к получению всего дерева и выводу его пользователю для определения нужных section_id разделов. Замечу, что перемещать узлы можно только в пределах одного и того же дерева, то есть, если вы выбрали русскую версию дерева, то и новый родитель должен принадлежать этому же дереву. Когда section_id перемещаемого узла и section_id нового родителя получены, действуем следующим образом: <?php $dbtree = new dbtree('sections', 'section', $db); $dbtree->MoveAll($old_id, $new_id, array('and' => array('section_lang = '' . $section_lang . '''))); if (!empty($dbtree->ERRORS_MES)) { // обрабатываем ошибки } ?>

Все, ваш раздел успешно перенесен. Не забывайте обрабатывать section_full_path, задавая им новые значения с учетом изменившегося пути.

53


PHP Inside №14

Библиотека dbtree для работы с деревьями Nested Sets

Таким же образом легко поменять позицию у двух узлов (при этом дети не перемещаются). <?php $dbtree = new dbtree('sections', 'section', $db); $dbtree->ChangePosition($id1, $id2, array('and' => array('section_lang = '' . $section_lang . '''))); if (!empty($dbtree->ERRORS_MES)) { // обрабатываем ошибки } ?>

Таким образом мы поменяем местами узлы с номерами $id1 и $id2 соответственно. Как получить номер раздела, в котором находится пользователь? В общем случае, у нас есть URL, который показывает нам путь по которому шел пользователь (скрипту совершенно не важно – реальный он или виртуальный). На основе URL мы попробуем вычислить номер текущего раздела. И так, у нас есть URL следующего вида: http://mysite.ru/en/food/fruits/apples/2005-09-30/ Где http://mysite.ru/ - это название вашего сайта, en/ – это код языка, с которым в данный момент работает пользователь, food/fruits/apples/ - это собственно путь раздела, а 2005-09-30/ - это дополнительный параметр для скрипта, не имеющий отношения к пути следования пользователя. Наша задача по пути раздела получить его section_id, а параметр 2005-09-30 отдать на обработку соответствующего скрипта. <?php // Получаем код языка $lang = substr($_SERVER['REQUEST_URI'], 0, 2); // Получаем URL без языка $uri = substr($_SERVER['REQUEST_URI'], 4, strlen($_SERVER['REQUEST_URI']) - 4) . '/'; // Преобразуем URL в массив $uri_array = explode('/', $uri); // По отрезку пути получаем section_id родителя текущего раздела if (empty($uri_array[0])) { $sql = 'SELECT section_id FROM sections WHERE section_level = 0 AND section_lang = '' . $lang . '''; } else { $sql = 'SELECT section_id FROM sections WHERE section_level = 1 AND section_path = '' . addslashes($section_path) . '' AND section_lang = '' . $lang . '' ORDER_BY section_left'; } $section_id = $db->GetOne($sql); if (FALSE === $section_id) { // Ошибка 404 – страница не найдена } else { $dbtree = new dbtree('sections', 'section', $db); // Получаем всю ветку текущего главного раздела $dbtree->Branch($section['section_id'], '', array('and' => array

54


PHP Inside №14

Библиотека dbtree для работы с деревьями Nested Sets

('section_lang = '' . $section['section_lang'] . '''))); if (!empty($dbtree->ERRORS_MES)) { // обрабатываем ошибки } // Организуем цикл для подбора текущего раздела по section_full_path $flag = FALSE; $rewrite_params = array(); while (FALSE === $flag) { // Вычисляем section_id нужного раздела while ($item = $dbtree->NextRow()) { if ($uri == $item['section_full_path']) { $current_id = $item['section_id']; $section = $item; $flag = TRUE; } } // Все дополнительные параметры URL складываем // В отдельный массив if (!isset($current_id)) { $rewrite_params[] = $uri_array[count($uri_array) - 2]; unset($uri_array[count($uri_array) - 2]); $uri = implode('/', $uri_array); } } if (!empty($rewrite_params)) { $max = count($rewrite_params); for ($i = 0;$i < $max;$i++) { $page_params[] = $rewrite_params[count($rewrite_params) - $i - 1]; } } } ?>

В результате работы указанного выше кода вы получаете: $current_id – номер раздела, в котором сейчас находится пользователь, $page_params содержит массив с дополнительными параметрами, переданными через URL (в нашем случае - 2005-09-30). Отмечу, что этот механизм позволяет игнорировать в URL ошибки пользователя. Это значит, что если пользователь ошибется в любом элементе пути, ему будет показана страница, которая следовала до этого ошибочного этапа. И только если URL вообще не будет подобран, будет выдана ошибка 404. Разные вкусности для пользователя сайта Современный сайт не мыслим без таких удобств, как навигатор (когда пользователь видит дорожку следования по сайту) или подробного меню, предлагающего ему все разнообразие возможных действий. В этой части нашего курса мы с вами посмотрим, какие возможности для реализации подобного предлагает моя библиотека. Давайте начнем с простого – построим навигатор по сайту. Чно нам для этого понадобится? Да ничего особенного, все данные к этому моменту у нас уже должны быть. Нам нужен только номер раздела, в котором сейчас находится пользователь. По идее, вы должны были его получить, когда определяли, какую страницу показывать пользователю на основании section_full_path. Раз все данные у нас уже есть, остается только построить навигатор и вывести его на эран в нужном месте. 55


PHP Inside №14

Библиотека dbtree для работы с деревьями Nested Sets

<?php $dbtree = new dbtree('sections', 'section', $db); // Получаем всех родителей раздела $dbtree->Parents($section['section_id'], array('section_name', 'section_full_path'), array('and' => array('section_lang = '' . $section ['section_lang'] . '''))); if (!empty($dbtree->ERRORS_MES)) { // обрабатываем ошибки } else { // Обрабатываем полученную часть дерева while ($item = $dbtree->NextRow()) { // Строим цепочку навигатора } } ?>

Перейдем к построению меню, для подсказки пользователю возможных действий. Библиотека предлагает вам минимум 3 варианта построения меню. Первый вариант – самый простой, мы считываем с вами все дерево и показываем его пользователю в виде «Карты сайта», отмечая, скажем, жирным шрифтом, тот узел, где пользователь в данный момент находится. Получать полное дерево сайта мы с вами уже умеем. Согласитесь, этот прием очень не гибок, особенно, если у вас слишком много разделов. Также он не совсем подходит, если вы в дереве храните не структуру сайта, а, скажем, рубрикатор товаров вашего магазина. Поэтому мы переходим ко второму варианту меню. Второй вариант меню – это меню в виде приоткрытого дерева. Что представляет из себя приоткрытое дерево? Это когда в данный конкретный момент пользователь видит только цепочку, ведущую до ближайших детей раздела, в котором находится пользователь. Все остальные цепочки разделов в это время «закрыты» и представлены только своими первоначальными родителями. Для иллюстрации приведу пример. Вот так выглядит все наше дерево: MainSection 1 Section Section Section Section MainSection 2 Section MainSection 3 Section Section <<<<< Section Section MainSection 4 Section Section

Приоткрытое дерево для выделенного элемента будет выглядеть так: MainSection 1 MainSection 2

56


PHP Inside №14

Библиотека dbtree для работы с деревьями Nested Sets

MainSection 3 Section Section Section Section MainSection 4

Надеюсь, что эти примеры достаточно наглядны. Итак, давайте построим подобное дерево. В исходных данных нам требуется все тот же section_id текущего раздела, в котором находится в данный момент пользователь. <?php $dbtree = new dbtree('sections', 'section', $db); // Получаем всех родителей раздела $dbtree->Ajar($section['section_id'], array('section_name', 'section_level', 'section_full_path'), array('and' => array('section_lang = '' . $section ['section_lang'] . '''))); if (!empty($dbtree->ERRORS_MES)) { // обрабатываем ошибки } ?>

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

Давайте посмотрим, каким образом построить такое дерево. Исходные данные – теже, что и в предыдущих примерах. <?php $dbtree = new dbtree('sections', 'section', $db); // Получаем всех родителей раздела $dbtree->Branch($section['section_id'], array('section_name', 'section_level', 'section_full_path'), array('and' => array('section_lang = '' . $section ['section_lang'] . '''))); if (!empty($dbtree->ERRORS_MES)) { // обрабатываем ошибки } ?>

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

57


PHP Inside №14

Библиотека dbtree для работы с деревьями Nested Sets

Как правило, дерево сайта меняется не так уж и часто, а потому у нас есть реальная возможность снять нагрузку с сервера, производя кэширование запросов к базе данных. ADODB предлагает, а мой класс поддерживает, кэширование запросов к базе. Если ADODB вам по каким-то пречинам не подходит, вы вполне можете реализовать кэширование сами. Для этого вам необходимо переписать соответствующие функции в предлагаемом с библиотекой драйвере базы данных. Разумеется, вам не стоит использовать кэширование в административном интерфейсе вашего сайта, чтобы видеть все изменения в реальном времени, но для показа сайта пользователю кэширование оправдано и полезно! Для глобального включения кэширования константе DB_CACHE задать значение TRUE.

необходимо

При вызове функций класса, которые поддерживают кэширование (смотрите ниже полное описание всех функций библиотеки), вам необходимо последним параметром передавать время жизни кэша в микросекундах. Например, 3600, составляет 1 час. В течение указанного срока, ADODB будет обращаться к базе данных, только если данный запрос проходит впервые. Если запрос уже был сделан раньше – ADODB возьмет его результаты из кэша, что значительно ускоряет работу вашего сайта и снимает нагрузку с сервера базы данных.

58


PHP Inside №14

Библиотека dbtree для работы с деревьями Nested Sets

Введение в разработку вебприложений с Ajax Автор: Jonathan Fenocchi Перевод: Григорий Федоринов С Ajax получать новые данные можно без перезагрузки страницы Раньше веб-приложения были ограничены в своих возможностях, так как для добавления новых данных приходилось перезагружать веб-страницу (или загружать на ее место другую). Другие методы были доступны (без перезагрузки страницы), но техника оставляла желать лучшего и имелась тенденция к появлению ошибок при использовании таких приложений. В последние месяцы техника, которая широко не поддерживалась в прошлом, стала доступна большинству серферов, дав тем самым свободу программистам для разработки приложений. Приложения могут асинхронно получать XML-данные посредством JavaScript. Такие приложения известны как "Ajax-приложения" (Асинхронные Javascript и XML-приложения). В этой статье я объясню, как получить удаленный XML-файл через технологию Ajax для обновления информации на веб-странице. В последующих статьях я расскажу о более продвинутой методике использования Ajax, что позволит вывести ваши веб-приложения на более высокий уровень. Первым шагом будет создание XML-файла с данными. Мы назовем этот файл data.xml (прим. переводчика – файл нужно сохранять в кодировке UTF-8). Это простой XML-файл, в реальных приложениях они, конечно, намного сложнее и ухищреннее, но для ясности примеры будут простыми и краткими. <?xml version="1.0" encoding="UTF-8"?> <root> <data> Это некоторые данные. Они хранятся в XML-файле и будут получены с помощью JavaScript. </data> </root>

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

59


PHP Inside №14

Введение в разработку веб-приложений с Ajax

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html lang="en" dir="ltr"> <head> <meta http-equiv="Content-Type" content="text/html; charset=windows-1251"> <title> Разработка веб-приложений с помощью технологии Ajax </title> </head> <body> <h1>Разработка веб-приложений с помощью технологии Ajax</h1> <p>Эта страница демонстрирует использование асинхронных Javascript и XML (Ajax) технологий для обновления содержания страницы посредством чтения удаленного файла динамически – без перезагрузки страницы. Обратите внимание: эта операция невозможна при отключенном JavaScript</p> <p id="xmlObj"> Это некоторые данные. Они являются данными по умолчанию <a href="data.xml" title="Обзор данных XML-файла." onclick="ajaxRead('data.xml'); this.style.display='none'; return false">Обзор XML данных.</a> </p> </body> </html>

Обратите внимание: мы вставили ссылку к файлу data.xml для пользователей с отключенным JavaScript. Для пользователей с включенным JavaScript будет вызвана функция "ajaxRead", ссылка является скрытой и не переадресовывает к файлу data.xml. Функция "ajaxRead" не описана, так что, если вы запустите этот пример, он выдаст ошибку в JavaScript. Давайте продолжим наши эксперименты и в заголовке опишем эту функцию. Теперь вы сможете увидеть, как работает технология Ajax. Следующий скрипт должен быть помещен между тегов : <script type="text/javascript"><!-function ajaxRead(file){ var xmlObj = null; if(window.XMLHttpRequest){ xmlObj = new XMLHttpRequest(); } else if(window.ActiveXObject){ xmlObj = new ActiveXObject("Microsoft.XMLHTTP"); } else { return; } xmlObj.onreadystatechange = function(){ if(xmlObj.readyState == 4){ updateObj('xmlObj', xmlObj.responseXML.getElementsByTagName('data')[0]. firstChild.data); } } xmlObj.open ('GET', file, true); xmlObj.send (''); } function updateObj(obj, data){ document.getElementById(obj).firstChild.data = data; } //--></script>

Так как скрипт небольшой, то сразу же опишу все функции. Первую функцию "ajaxRead" мы вызываем из нашей ссылки "Обзор данных XML-файла".

60


PHP Inside №14

Введение в разработку веб-приложений с Ajax

В функции мы описываем переменную xmlObj – это будет прослойкой между клиентом (пользователь, просматривающий вебстраницу) и сервером (вашим сайтом). Мы описываем этот объект в структуре if/else: if(window.XMLHttpRequest){ xmlObj = new XMLHttpRequest(); } else if(window.ActiveXObject){ xmlObj = new ActiveXObject("Microsoft.XMLHTTP"); } else { return; }

Это проверка доступности методов, которые могут использовать браузеры, так как различные браузеры описывают объект XMLHttpRequest по-разному. Теперь, когда мы описали "xmlObj" как объект XMLHttpRequest, мы можем реализовать его в зависимости от версии браузера. Если невозможно создать объект XMLHttpRequest, мы выходим из функции командой "return" чтобы избежать ошибок. В большинстве случаев этот код работает и создает объект XMLHttpRequest, однако могут быть исключения для более ранних версий браузеров (например, это работает в IE 5.01, но не работает в Netscape 4). Следующим идет этот блок: xmlObj.onreadystatechange = function(){ if(xmlObj.readyState == 4){ updateObj('xmlObj', xmlObj.responseXML.getElementsByTagName('data')[0]. firstChild.data);

Каждый раз при изменении состояний объекта XMLHttpRequest вызывается событие "onreadystatechange". Используя конструкцию "xmlObj.onreadystatechange = function(){ … }", мы строим и запускаем функцию, "исполняемую на лету" каждый раз, когда состояние объекта XMLHttpRequest изменяется. Всего может быть 5 состояний, которые нумеруются от 0 до 4. 0. uninitialized (не инициализирован) (перед началом работы объекта XMLHttpRequest) 1. loading (загрузка) (однажды, когда идет инициализация объекта XMLHttpRequest) 2. loaded (загружен) (однажды XMLHttpRequest, когда получен ответ от сервера) 3. interactive (доступен) (пока объект XMLHttpRequest соединен с сервером) 4. complete (завершен) (после того, XMLHttpRequest завершил выполнение всех задач)

как

объект

Пятое состояние (номер 4) наступает, когда мы уверены что данные доступны, однако мы проверяем xmlObj.readyState на значение "4", для того чтобы убедиться, что данные доступны. Если это так, мы запускаем функцию обновления. Эта функция имеет 2 параметра: ID элемента в веб-странице (обновляемый элемент в текущей веб-странице) и данные для заполнения этого элемента. О том, как работает эта функция, будет описано позднее.

61


PHP Inside №14

Введение в разработку веб-приложений с Ajax

Наша веб-страница содержит тег P, который имеет ID = "xmlData". Этот параграф и будет обновляться. Данные берутся из XML-файла, но это немного сложнее. Ниже описано, как это работает. Свойство xmlObj.responseXML является объектом DOM – это немного похоже на "document" объект, за исключением того, что оно используется для удаленного XML-файла. Другими словами, xmlObj.responseXML - это объект "document", если бы вы запускали скрипт в файле data.xml. Так как мы знаем это, мы можем отыскать любой XML-узел через "getElementsByTagName" метод.

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

Данные, содержащиеся в узле XML, как раз названы "<data>" поэтому наша задача проста: получить первый узел с данными (и только один). Это делает конструкция xmlObject.responseXML.getElementsByTagName("data")[0], возвращающая первый узел <data> из XML-файла. Обратите внимание: эта конструкция возвращает только узел из XML-файла, а не данные – данные должны быть выбраны посредством обращения к свойствам этого узла, это является следующим шагом. После этого надо просто обратиться к свойству этого объекта через конструкцию "firstChild.data" (firstChild обращается к тексту узла, который содержится в узле <data> , и данными является фактически текст этого узла). xmlObj.open ('GET', file, true); xmlObj.send ('');

Это последний кусок в нашей функции ajaxRead. Что он делает? Метод "open" объекта xmlObj открывает соединение на сервер (в данном примере указан метод "GET" – вы можете использовать как "POST" так и другие методы, это допускается), получает файл (в нашем случае переменная "file", которая была отослана как параметр функции ajaxRead – data.xml), и JavaScript должен обработать этот запрос синхронно (false) или асинхронно (true по умолчанию). Так как мы используем технологию асинхронных Javascript и XML, мы будем использовать асинхронный метод – синхронный метод в нашем случае не будет работать. В последней строке нашей функции мы просто отсылаем пустую строку на сервер. Без этой строчки состояние объекта xmlObj никогда не достигнет состояния «завершено», то есть не будет равным 4, и вы больше не сможете обновить вашу страничку. Метод send можно использовать и для других вещей, но в этом примере мы только получаем данные с сервера и не отсылаем их. Не хотелось бы вдаваться в подробности метода в этом примере. function updateObj(obj, data){ document.getElementById(obj).firstChild.data = data; }

Теперь настало время объяснить функцию updateObj немного подробнее: эта функция записывает в любой специфицированный элемент на данной странице новое значение.

62


PHP Inside №14

Введение в разработку веб-приложений с Ajax

Ее первый параметр "obj," просто описывает ID элемента на данной веб-странице – объекта, который будет обновляться; второй параметр - "data," это строка, которая описывает новые данные, которые должны быть помещены в обновляемый объект ("obj"). Обычно делают проверку того, присутствует ли на текущей странице элемент ID, указанный в "obj", но эта проверка в данном случае не является необходимостью. Функция updates работает подобно способу получения данных из узла "data" в XML-файле – она ищет элемент, который хочет обновить (на этот раз вместо ID элемента используется имя тега и индекс на странице), и устанавливает данные первого потомка (текст узла) этого элемента в новую данную. Если вы захотите обновить объект с HTML, надо использовать следующую конструкцию: document.getElementById(obj). innerHTML = data. Это все, что есть по этому примеру. Концепция проста, и код не очень сложный. Вы хотите читать файл откуда-либо без возможности перезагружать страницу. Эта технология достаточно гибка и позволяет вам делать различные вещи, включая отправку данных из форм без перезагрузки страницы и использовать скрипты на стороне сервера для формирования XML-файлов динамически. Если вы хотите сделать шаг вперед, запомните что Google - ваш друг. В следующей статье я объясню, как вы можете использовать Ajax вместе с сервер-исполняемыми скриптами для того, чтобы сделать более мощные веб-приложения.

Об авторе Джонатан Феноччи - веб-разработчик, специализирующийся в области веб-дизайна, написания скриптов, исполняемых на клиентской стороне, и PHP-программирования. Его сайт расположен здесь: www.slightlyremarkable.com Оригинал статьи: http://www.webreference.com/programming/javascript/jf/column12/

Множественное наследование в ОО модели PHP

63


PHP Inside №14

Множественное наследование в ОО модели PHP

Множественное наследование в ОО модели PHP Автор: Денис Баженов Множественное наследование в объектной модели PHP может решить некоторые проблемы Подсунь свою мечту врагам, может быть, они погибнут при ее реализации Возможно, у кого-то название этой статьи вызвало «отторжение», а в голове пронеслась мысль, о том, что ее (статью) надо было бы назвать «Мартышка и очки». Действительно, а зачем оно нам надо? В последнее время многие языки (но не все) отказываются от множественного наследования реализации (НЕ интерфейса!) как от рудимента ООП. Я не стану брать на себя ответственность и судить о том, верно ли это. Однако я знаю, что в некоторых случаях множественное наследование позволяет предотвратить комбинаторный рост числа классов, когда вы пытаетесь, например, охарактеризовать объекты с точки зрения принадлежности категориям. Возьмем такой пример. В частности я как человек подпадаю под множество категорий (или, как говорят социологи, социальных ролей). Рассмотрим несколько из них. Я сын своих родителей, я программист, я гражданин РФ и т.д. В контексте каждой категории я могу выполнить различные действия. Скажем, как сын родителей я могу требовать от отца алименты, если родители в разводе. А как гражданин Российской Федерации я могу голосовать на выборах.

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

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

От теории к практике Код, который реализует некоторое подобие множественного наследования, вы сможете найти в приложении к журналу. Это класс multipleInheritance, который, по сути, является прокси-объектом к набору классов.

64


PHP Inside №14

Множественное наследование в ОО модели PHP

Наверное, именно так и стоило бы назвать решение, так как истинное множественное наследование - это гораздо более сложный инструмент, который должен быть реализован на уровне языка. Давайте посмотрим, что можно «выжать» из этого класса. Вы можете использовать оператор -> для вызова методов как дочернего класса, так и методов родительских классов. То же самое относится к свойствам классов. Если метод реализуется более чем одним родительским классом, а в дочернем классе этот метод не перегружается, то генерируется исключительная ситуация. Методы дочерних классов могут использовать оператор :: для вызова методов родительского класса. При этом метод будет вызван с контекстом дочернего класса. Это позволяет родительскому классу иметь доступ к элементам дочернего. Количество наследуемых классов и их имена могут определяться на стадии выполнения. На этом плюсы заканчиваются и начинаются минусы! Необходимо понимать, что создаваемые объекты – это разные объекты со своими контекстами. Отсюда вытекает следующее. При использовании оператора -> вы не сможете вызвать из родительского класса методы дочернего, потому как они находятся в разных контекстах. При использовании оператора :: $this будет указывать на контекст дочернего класса, а не родительского, что может привести к самым интересным и непредсказуемым последствиям. Если несколько родительских классов (скажем, два) наследуют реализацию от одного класса (дедушки), то в итоге у вас будут два дедушки по одному на каждый родительский объект. То есть, изъясняясь терминами C++, нет поддержки виртуальных функций. Методы и свойства родительского класса должны быть объявлены как public, иначе вы не сможете получить к ним доступ из-за «разности» контекстов. Несмотря на все эти недочеты, в простых случаях данный класс все же может быть применен. Особенно для реализации «подмешанных» (mix-in) классов.

Использование Возьмем для примера два класса, которые не связаны между собой по смыслу предметной логики. Первый класс (forumMessage) – это класс, управляющий сообщениями в форуме. Второй класс (Observable) является реализацией паттерна Observer (наблюдатель) (более подробно об этом паттерне вы можете узнать на phpPatterns). Необходимо связать эти два класса так, чтобы не пришлось нарушать текущие зависимости классов (то есть мы не можем наследовать forumMessage от Observer по ряду причин), и при этом использовать их как один объект. class forumMessage

{

public $sender; public $text; public function add($sender, $text)

65


PHP Inside №14 {

}

}

Множественное наследование в ОО модели PHP

$this->sender = $sender; $this->text = $text; // Добавление сообщения в хранилище

class Observable { // Зарегистрированные наблюдатели public $observers; public function addObserver(Observer $observer) { // Регистрируем наблюдателя } public function notify($event) { // Генерируем уведомление и передаем его всем зарегистрированным наблюдателям } }

Реализации подобного решения требует создать класс, который при создании экземпляра будет являться прокси-объектом для двух наших классов (forumMessage и Observable). Такой класс можно создать, наследуя его от класса multipleInheritance. Делается это следующим образом. class observableForumMessage extends multipleInheritance { public function __construct() { multipleInheritance::__construct('forumMessage', 'Observable'); }

() }

public function addMessage($sender, $text) { $this->add($sender, $text); }

$this->notify('new message');

// Вызывается forumMessage::add

// Вызывается Observable::notify()

Я думаю, исходный код не нуждается в комментариях. Если вы взглянете на код класса multipleInheritance, то поймете, что там все до смешного просто. Используются средства overloading, которые начали нормально работать на «пятерке».

Вывод Да, безусловно, этот метод - это dirty hack (а может быть, он даже и этого звания не достоин :) ).


PHP Inside №14

Множественное наследование в ОО модели PHP

Однако он может отлично подойти для реализации, скажем, совмещения некоторых паттернов с классами бизнес логики. Аналогичное решение можно получить, используя функции семейства Object Aggregation, но они свойственны только «четверке». В PHP5 функции Object Aggregation и ClassKit были замещены расширением RunKit, которое позволяет реализовать то же самое, но более качественно и гибко. Однако я не смог заставить работать это расширение на своей площадке. При обработке запроса Apache сообщал о критической ошибке и умирал, ссылаясь на ошибку в ntdll.dll. Похоже, сказывается злополучная «сырость» PHP 5.1 (Статья была написана на этапе бета-версии PHP 5.1 - прим. редактора). Думаю, на 5.0 все должно работать.


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.