#16, Декабрь'2005 :: Ajax и PHP

Page 1

Ноябрь - Декабрь '05 № 16

с1 у н о

Б

Учебник по созданию компонент Joomla!

с2 у н о

Б

Вакансии от PHPClub.ru

Ajax и PHP Новогодний выпуск

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


Биржа труда....................................................................................................................................................... 2 Тема с обложки Ошибки при использовании Ajax................................................................................................................8 Sajax - реализация Ajax в PHP................................................................................................................... 10 Ajax и PHP без использования XmlHttpRequest...................................................................................... 16 Идеи Ошибки начинающих TDD-практиков..................................................................................................... 21 Основы использования PHP для работы с FTP........................................................................................28 Сборка PHP 5 с драйверами mysql и mysqli одновременно....................................................................33 Учебник по созданию компонент Joomla!................................................................................................35

Биржа труда В этом номере вместо слова редактора мы решили обратить ваше внимание на ситуацию на рынке труда PHP-разработчиков. Огромное спасибо клубу разработчиков http://phpclub.ru за организованный опрос, целью которого является определение тенденций на рынке нашего труда. Результаты опроса на 16 декабря 2005 мы приводим здесь.

PHP Inside #16

Результаты голосования Хотим обратить внимание, что опрос продолжается и мы убедительно просим и вас принять в нем участие. Конечно в том случае, если программирование на PHP приносит вам какой либо доход. Данные по ответам каждого участника не разглашаются. Если вы решите оставить комментарии – все в ваших руках. Голосование находится здесь: http://phpclub.ru/talk/showthread.php?s=&threadid=77541&rand=18 А сейчас, промежуточные результаты: Повысился мой суммарный доход по сравнению с 2004 годом

100

86

80 Процентов от 100%

60

да

14

40

нет

нет

20 да

0

Содержание / Биржа труда

Содержание

1

2


Биржа труда

Менял работу в течении последнего года

80 62

60

38 да

Процентов от 40 100%

нет

20

нет

PHP Inside #16

да

0 1

Наша фирма набирает еще webпрограммистов

66

80 60 Процентов от 40 100% 20

да

34 нет

нет

да

0 1

У нас жесткое разделение по должностям программист,верстальщик,тестировщик 68

80 60 Процентов от 40 100% 20

да

32 нет

нет

да

0 1

3


Биржа труда

Стало больше проектов по сравнению с 2004годом

47 53

нет

да нет

да

PHP Inside #16

100 80 Процентов от 60 40 100% 20 0

1

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

50 50

нет 50

Процентов от 100%

да нет

да 0

1

4


Биржа труда

Вакансии от phpclub.ru Web - Программист, Москва ($1500)

PHP Inside #16

В компанию "Компания "Рутек"" (Russia,Москва) требуется Web - Программист Зарплата от $1500; Занятость Полная (основное место работы); Возраст:от 22 до 35; Пол:не имеет значения; Образование:Незаконченное высшее; Опыт работы:1 год Языки и технологии: Perl, CGI, PHP, Операционные системы: Unix, Прочие требования: В российскую компанию приглашается Web-программист. Требования: Знание Perl, PHP, SQL; Опыт работы в среде UNIX; Опыт совместной работы над проектами в коллективе; Умение разбираться в чужом коде; Высшее (или неполное высшее) техническое образование; Технический английский. Обязанности: Разработка и сопровождение программного обеспечения для Web-интерфейсов. Условия работы: 1200$ на испытательный срок, 1500$ после испытательного срока, мед. страховка, возможность обучения. Контактная информация: Елена Тёлина, (495) 10 10 789, . WEB-сайт компании : http://www.rutech.ru/ Вакансия предоставлена прямым работодателем 15-12-2005 и будет актуальна до 14-01-2006

Cпециалист, Москва ($1600) В компанию "Inform-Mobil" (Russia,Москва) требуется специалист Зарплата от $1600; Занятость Полная (основное место работы); Возраст:от 25 до 40; Пол:не имеет значения; Образование:не имеет значения; Опыт работы:не имеет значения Языки и технологии: C, C++, .Net, Perl, CGI, PHP, Python, UML, XML, XSL, XSLT, СУБД: MS SQL Server, MySQL, PostgreSQL, Операционные системы: Linux, Прочие требования: Все перечисленные технологии являются 'желательными'. С произвольным их набором придётся сталкиваться в рамках должности. Единственными обязательными требованием является профессиональное владение парадигмой ООП. Обязанности: Исследование и разработка инфраструктурных средств организации и автоматизаци разработки программного обеспечения Условия работы: Постоянно растущий соц. пакет (питание, спорт, ...), возможность карьерного роста, интересные задачи. Контактная информация: Борис Безруков, (096)995-8-995 доб., . WEB-сайт компании : http://www.immo.ru/ Вакансия предоставлена прямым работодателем 15-12-2005 и будет актуальна до 14-01-2006

5


Web разработчик / архитектор, Санкт-Петербург ($700) В компанию "TRADEBOX.RU" (Russia,Санкт-Петербург) требуется Web разработчик / архитектор Зарплата от $700; Занятость Полная (основное место работы); Возраст:от 24 до 40; Пол:не имеет значения; Образование:не имеет значения; Опыт работы:2 года Языки и технологии: C, C++, HTML, DHTML, CSS, Java, JScript, VBScript, PHP, XML, XSL, XSLT, СУБД: MySQL, Операционные системы: Windows, Прочие требования: Серьезный опыт работы с PHP и C++. Обязанности: Разработка новых сервисов для проекта TRADEBOX.RU, поддержка проекта. Условия работы: Офис находится в 5 минутах от м. Горьковская. Контактная информация: Дмитрий, (812) 336-16-90, . WEB-сайт компании : http://www.tradebox.ru/ Вакансия предоставлена прямым работодателем 14-12-2005 и будет актуальна до 13-01-2006

6

Биржа труда

В компанию "Прямой работодатель" (Russia,Москва) требуется Руководитель Отдела тестирования Зарплата от $1500; Занятость Полная (основное место работы); Возраст:от 23 до 40; Пол:не имеет значения; Образование:не имеет значения; Опыт работы:1 год Языки и технологии: C, C++, Java, Perl, CGI, PHP, СУБД: PostgreSQL, Операционные системы: Unix, Прочие требования: Общие представления обо всех основных средствах и методиках тестирования, глубокое знание какой-либо из систем тестирования, понимание принципов построения ПО, опыт на руководящей позиции от 1 года, опыт внедрения тестирования и развития его процессов, желателен опыт создания и руководства командами, отвечающими за тестирование, общее представление о PHP, Perl, MySQL, PosgreeSQL, Java, C ++, разработке Web-сервисов, продвинутый пользователь * nix. Обязанности: Hуководство, планирование и контроль за тестировщиками, переход от приемочного тестирования к многоаспектному тестированию продуктов, создаваемых компанией, внедрение средств и методик автоматизированного тестирования, обучение тестировщиков методологии и инструментальным средствам Условия работы: Работа в г.Юбилейном Московской области /15-20 мин от м.ВДНХ/, соц.пакет / корпоративный транспорт, бесплатное питание, спорт/. Контактная информация: Марина, Светлана, 739-94-17, 995-8-995, . Вакансия предоставлена прямым работодателем 14-12-2005 и будет актуальна до 13-01-2006

PHP Inside #16

Руководитель Отдела тестирования, Москва ($1500)


Ведущий веб-программист, Минск ($600) В компанию "АДТ" (Belarus,Минск) требуется ведущий веб-программист Зарплата от $600; Занятость Полная (основное место работы); Возраст:от 25 до 30; Пол:мужчина; Образование:Высшее; Опыт работы:3 года Языки и технологии: HTML, DHTML, CSS, JScript, VBScript, Perl, CGI, PHP, XML, XSL, XSLT, СУБД: MySQL, Oracle, Прочие требования: В московскую компанию разработчик веб-приложений (холдинговая структура), в связи с открытием филиала компании в г. Минске требуется ведущий вебпрограммист, требования: опыт руководства группой программистов от 1 года, программирование под веб от 2 лет, PHP, Javascript, CSS, MySQL, наличие портфолио, желателен опыт работы с Oracle. Приветствуется опыт работы с UNIX на уровне пользователя. Обязанности: Руководство группой разработчиков, написание приложение и поддержка под веб Условия работы: З/п 500-600$ от кандидата, полная занятость, график работы с 10-19.00, вариант удаленной работы не рассматривается, место работы г. Минск. При заинтересованности резюме на may_hr@inbox.ru Контактная информация: Ходакова Анастасия, 095-000-00-00, . Вакансия предоставлена прямым работодателем 15-12-2005 и будет актуальна до 04-01-2006

7

Биржа труда

В компанию "ВОЙП" (Ukraine,Киев) требуется Программист Зарплата от $500; Занятость Полная (основное место работы); Возраст:от 22 до 40; Пол:не имеет значения; Образование:Высшее; Опыт работы:2 года Языки и технологии: C, C++, Perl, CGI, PHP, СУБД: MySQL, Операционные системы: Linux, Прочие требования: Профессиональные навыки: • Уверенные практические знания двух из перечисленных языков программирования: PHP5, Perl или С/C++ • MySQL 4.0+/5, умение строить и оптимизировать сложные запросы • Опыт работы и разработки приложений под Linux/FreeBSD • Знание концепций ООП в целом и умение применять их на практике • Уверенное знание письменного английского языка Желательно: • Навыки тестирование ПО • Навыки работы с системой контроля версий (Subversion, CVS) • Базовые знания стека протоколов TCP/IP и протоколов уровня приложений в частности • Базовые знания Apache • Базовые знания HTML/XHTML Личностные качества: • умение работать в команде • способность оценивать временные затраты на работу • умение вести документацию по написанному коду • способность придерживаться еди Контактная информация: Олег Витальевич, . Вакансия предоставлена прямым работодателем 15-12-2005 и будет актуальна до 20-12-2005

PHP Inside #16

Программист, Киев ($500)


Тема с обложки

Ошибки при использовании Ajax

Ошибки при использовании Ajax Автор: Алекс Босворт Перевод: Григорий Федоринов В последнее время мы много слышим об Ajax. В основном это хвалебные оды. Но есть и другая сторона медали...

PHP Inside #16

Ajax это удивительная технология, позволяющая создавать вебприложения нового поколения такие как maps.google.com, colr.org и backpackit.com. Но Ajax также опасная технология для вебразработчиков, она добавляет множество UI (прим. пер. пользовательский интерфейс) проблем, и проблем загрузки сервера. Я собрал список наиболее часто совершаемых ошибок при разработке на Ajax :

Не создаются визуальные эффекты произведенного действия Если я произвел действие, которое должно быть отработано AJAX, вы должны мне дать визуальное подтверждение произведенного действия. Как пример этого индикатор загрузки GMail, расположенный в верхнем правом углу. Когда я произвожу действие, малый красный квадрат показывает мне, что страница грузится, это сделано для того что AJAX не использует стандартный уникальный идентификатор для каждой новой загруженной страницы.

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

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

Мелькание и замена частей страницы неожиданно для пользователя Первая буква в аббревиатуре АJAX обозначает asynchronous (асинхронный). Трудности с асинхронными сообщениями в этом возникают, когда они появляются неожиданно. Асинхронные изменения страниц должны происходить в строго определенном месте и должны использоваться с умом, сверкающие и мерцающие области в сообщениях напоминают использование старого HTML-тэга, к которому я бы не хотел возвращаться.

8


Тема с обложки

Ошибки при использовании Ajax

Не использовать ссылки «Послать другу» и «Добавить в избранное» Возможность «Послать другу» для того чтобы он увидел то, что вижу я и возможность добавить в избранное страницу и вернуться назад по этой закладке в дальнейшем. Javascript, и так называемые Ajax приложения, могут вызвать большие проблемы для такой модели использования. Т.к. Javascript динамически генерирует страницы вместо сервера, то URL уже не влияет на навигацию по сайту. Чтобы не потерять эти возможности многие Ajax приложения содержат постоянные ссылки на страницы.

Большой программный код замедляет браузер

PHP Inside #16

Ajax это путь для того чтобы сделать приложения javascript более интересными, к сожалению эта интересность обходится более громоздким программным кодом. Больше кода – больше работы для браузера, это касается некоторых интенсивно использующих javascript сайтов, особенно для плохо кодированных, вам нужен мощный CPU для сохранения скорости исполнения функциональных возможностей. Сейчас правда такая проблема не стоит, т.к. по сравнению с прошлыми сейчас компьюетры достаточно быстрые.

Изобретение новых приемов управления для пользователя Большая ошибка которую легко совершить при использовании Ajax это: 'Кликнув мышью на объекте, не всегда вызывается результат, который ты ожидаешь'. Конечно, продвинутые пользователи знают что если нажать и удерживать курсор на div то его можно перенести в другое место, но остальные пользователи знают об этом потому что это не используется в общей практике пользователей, таким образом вы увеличиваете сложность и время обучения пользователя, это всегда негативный фактор для любого приложения.

Не каскадные изменения других частей страницы Ajax/Javascript дает вам специфическое управление над содержанием страницы, это позволяет легко получить фокус над одной областью и теряет управление над другими встроенными картинками. В качестве примера это Backpackit заголовок (прим. Переводчика - Все эти возможности на сайте http://www.backpackit.com/, который позволяет печатать свои статьи "). Если вы изменяете заголовок страницы, то незамедлительно меняется заголовок, они даже меняют заголовок справа, но не меняют тег title. C Ajax вы должны менять полную картину, когда делаете локальные изменения.

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

Прокрутка страницы дезориентирует пользователя Другая проблема показа текста в уже загруженной странице происходит при прокрутке длинной страницы. Это может случиться когда читаешь длинный текст и асинхронный javascript запрос решит скрыть уже прочтенные части. Это будет раздражать т.к. будет теряться время для поиска места, где я читаю в данный момент. В построении списка ошибок я использовал Backpackit. Вы можете проверить это на сайте Backpackit. И если вы хотите помочь мне, ответ напишите на форуме и я вас добавлю в список пользователей которые могут изменять список ошибок. http://sourcelabs.com/ajb/archives/2005/05/ajax_mistakes.html 9


Тема с обложки

Sajax – реализация Ajax в PHP

Sajax – реализация Ajax в PHP Андрей Олищук На данный момент в сети можно найти множество фреймворков и библиотек для работы с Ajax в PHP. Sajax – одна из них

PHP Inside #16

Технология Ajax начала плотно входить в реалии PHP-разработки. В сети стали появляться различные библиотеки и даже фреймворки для работы с Ajax. Об одной из таких библиотек сейчас и пойдет речь.

Что такое Sajax Sajax – это инструмент для разработки веб-приложений с использованием технологии Ajax, который имеет реализации практически для всех языков веб-программирования, в том числе и PHP. Основная цель данного инструмента в нашем контексте – предоставить доступ из JavaScript к функциям, написанным на PHP без перезагрузки страницы. Области применения Sajax совпадают с областями применения любых других Ajaxбиблиотек. В основном это веб-формы, которые благодаря новому подходу могут работать быстрее за счет экономии времени на перезагрузку страницы, и их графический интерфейс сможет больше приблизится к интерфейсу обычных программ. Вариант Sajax для PHP работает как обычная библиотека, которую можно подключать к вашему коду посредствами команд include и require. Библиотеку Sajax можно скачать с официального сайта: http://www.modernmethod.com/sajax/download.phtml.

Пример для быстрого старта Рассмотрим простейший пример применения Sajax, чтобы понять как это работает. Допустим, перед нами стоит задача вывести на экран пользователя содержимое некоторого файла с сервера, после того, как пользователь нажмет кнопку «Прочесть файл». К тому же, задача должна быть выполнена без перезагрузки страницы. Этот пример будет простейшим, поэтому в нем, во избежание усложнения материала прибегнем к смешению PHP, JavaScript и HTML кода в одном пробном файле, назовем его test1.php. Для начала, подключим библиотеку Sajax и создадим PHP-функцию, которая будет считывать данные из файла. <? require("Sajax.php"); function gettext_php() { if ($text = file_get_contents("data.txt")) { return $text; } else { return "Ошибка. Возможно файл пуст или отсутствует"; } } ?>

10


Тема с обложки

Sajax – реализация Ajax в PHP

Далее, добавим вызовы функций Sajax, которые необходимы для того, чтобы наша функция gettext_php() стала видна для JavaScript. Теперь код в test1.php будет выглядеть вот так:

PHP Inside #16

<? require("Sajax.php"); function gettext_php() { if ($text = file_get_contents("data.txt")) { return $text; } else { return "Ошибка. Возможно файл пуст или отсутствует"; } } //Инициализируем Sajax sajax_init(); //Экспортируем в JavaScript следующие PHP-функции sajax_export("gettext_php"); sajax_handle_client_request(); ?>

На этом «серверная» часть кода заканчивается, но так как мы для простоты решили все писать в одном файле, то «клиентскую» часть запишем сюда же. После закрывающего тега ?> необходимо дописать обычный HTML-код с небольшой вставкой. <html> <head> <title>Пример 1</title> <script type="text/javascript"> <?php sajax_show_javascript(); ?> function AjaxDo() { x_gettext_php(set_result); } function set_result(result) { document.getElementById('_show_file_').innerHTML = result; } </script> </head> <body> <input type="button" value="Прочесть файл" x_onClick="AjaxDo()"> <div name="_show_file_" id="_show_file_"> </body> </html>

Что содержится в этом коде? Сам HTML, я думаю, будет понятен без объяснений – это обычная кнопка «Прочесть файл» и пустой тег <div> с именем «_show_file_». Все самое интересное содержится в JavaScript коде. Как мы видим, после тега <script> производится вызов -PHP-функции sajax_show_javascript() - она необходима для генерации служебных функций-врапперов. Далее в коде JavaScript идут две функции: AjaxDo() и set_result(), написанные нами. Функция AjaxDo() срабатывает по нажатию кнопки «Прочесть файл» (она прописана в ее атрибуте x_onClick). Тут же производится вызов функции x_gettext_php(). Это враппер (JavaScript-оболочка) той самой функции, которую мы написали на PHP немного ранее и назвали gettext_php. В Sajax, для вызова функции-враппера берется название PHP-функции и подставляется префикс «x_», т.е. если наша PHP-функция называлась gettext_php(), то ее враппер в JavaScript будет называться x_gettext_php(). 11


Тема с обложки

Sajax – реализация Ajax в PHP

В качестве параметра передается наименование JS-функции set_result(). Все что делает эта функция – добавляет на страничку новый HTML, посредством атрибута innerHTML для элемента _show_file_. Т.е. просто дописывает новый HTML-код на страничку. Перед тем как попробовать выше приведенный код в действии, необходимо будет создать текстовый файл data.txt, в который можно записать любой текст. Именно этот текст и должен быть выведен в результате выполнения скрипта. Теперь, если мы запустим файл test1.php и нажмем на кнопку «Прочесть файл», то ниже этой кнопки появится текст, записаный вами ранее в data.txt, либо фраза "Ошибка. Возможно файл пуст или отсутствует" в том случае, если вы забыли его создать или создали, но не записали в него какой либо текст.

PHP Inside #16

Это довольно простой, но уже вполне убедительный пример.

Чат средствами PHP5, Sajax и Smarty Все что мы рассмотрели выше – это довольно полезно, но не демонстрирует способов применения Sajax в реальных условиях. Предлагаю рассмотреть вариант применения Sajax в связке со Smarty – мощным и популярным шаблонным движком на примере создания простейшего чата, работающего без перезагрузки страниц. За основу взят пример из официальной документации к Sajax. Для работы этого примера нам соответственно понадобится дистрибутив Smarty, который можно взять с сайта http://smarty.php.net. Будем считать, что с этим шаблонизатором вы уже знакомы и он установлен в вашей системе. В процессе работы нам потребуется создать два файла – шаблон (назовем его chat.tpl) и, так называемый, контроллер, т.е. PHP-файл который будет оперировать данными и передавать их в шаблон Smarty. Назовем этот файл chat.php. Начнем с создания контроллера: <? //Подключаем Sajax require "Sajax.php"; //Подключаем Smarty. Не забудьте указать ваши пути require "D:/programms/apache/htdocs/smarty/Smarty.class.php"; //Инициализируем Smarty //Обратите внимание на то, что во избежание конфликтов мы //будем использовать ограничители тегов Smarty <{ и }> вместо стандартных { и } $smarty = new Smarty; $smarty->Smarty(); $smarty->template_dir = "D:/programms/apache/htdocs/templates/"; $smarty->compile_dir = "D:/programms/apache/htdocs/templates_c/"; $smarty->config_dir = "D:/programms/apache/htdocs/config/"; $smarty->cache_dir = "D:/programms/apache/htdocs/cache/"; $smarty->left_delimiter = "<{"; $smarty->right_delimiter = "}>"; //Функция записи ника и сообщения в общий файл function add_line($msg_text) { //обрезаем теги и добавляем перенос строки и перевод каретки $msg = strip_tags($msg_text) . "\r\n"; //Пишем в конец файла file_put_contents("data.txt",$msg,FILE_APPEND); } //Функция вывода содержимого файла на экран

12


Тема с обложки

Sajax – реализация Ajax в PHP function refresh() { $lines = file("data.txt"); return join(" ", array_slice($lines, -25)); } //Инициализация Ajax $sajax_request_type = "GET"; sajax_init(); //Перечисляем функции для экспорта в JavaScript sajax_export("add_line", "refresh"); sajax_handle_client_request();

PHP Inside #16

//Получаем JS-враппер PHP-функций в виде строки $sajax_wrappers = sajax_get_javascript(); //Присваеваем эту строку Smarty-переменной $WRAPPERS $smarty->assign("WRAPPERS",$sajax_wrappers); //Выводим шаблон chat.tpl $smarty->display("chat.tpl"); ?>

Итак, чем отличается этот код от того, который был рассмотрен ранее, в примере с выводом текста из файла на экран? Прежде всего, здесь появился код для работы со Smarty. Мы инициализировали объект $smarty и указали его первичные настройки – пути к служебным директориям. Затем мы поменяли стандартные делимитеры (теги) Smarty на <{ и }>. Это сделано на всякий случай, во избежание конфликтов между символами { } в синтаксисе Smarty и JavaScript. Вы можете решить эту проблему иным образом. После этого были созданы две функции (записи и чтения из файла) и инициализирован Sajax. Насколько мы помним из предыдущего примера, JavaScript-врапперы для PHPфункций генерируются динамически функцией sajax_set_javascript(). Однако, данная функция выводит JavaScript на экран, а в нашем случае необходимо этот код передать в шаблон в Smarty-переменную $WRAPPER. Для этого, вместо sajax_set_javascript() нужно использовать sajax_get_javascript(). Разница между sajax_set_javascript() и sajax_get_javascript() примерно та же, что и между методами Smarty – display() и fetch(). В первом случае функция выводит код на экран, а во втором случае она возвращает его для присвоения переменной. Теперь посмотрим на шаблон chat.tpl: <html> <head> <title>Пример 2</title> <script> <{$WRAPPERS}> function refresh_cb(new_data) { document.getElementById("wall").innerHTML = new_data; setTimeout("refresh_page()", 1000); } function refresh_page() { x_refresh(refresh_cb); } function add_cb() { // we don't care.. }

13


Тема с обложки

Sajax – реализация Ajax в PHP function add() { var line; var handle; handle = document.getElementById("handle").value; line = document.getElementById("line").value; if (line == "") return; x_add_line("[" + handle + "] " + line, add_cb); document.getElementById("line").value = ""; } </script> </head> <body x_onLoad="refresh_page();">

PHP Inside #16

<form name="f" action="#" x_onSubmit="add();return false;"> <input type="text" name="handle" id="handle" value="Ник" x_onFocus="this.select()" style="width:130px;"> <input type="text" name="line" id="line" value="Ваше сообщение" x_onFocus="this.select()" style="width:300px;"> <input type="button" name="check" value="Отправить" x_onClick="add(); return false;"> <div id="wall"></div> </form> </body> </html>

В примере были созданы два поля для ввода ника и сообщения, а так же кнопка «Отправить». При нажатии этой кнопки, ник и текст сообщения записываются в файл data.txt. Вывод на экран данных этого файла осуществляется один раз в секунду без перезагрузки страницы. Это делается функцией x_refresh(), враппером нашей PHP-функции refresh().

Sajax и кириллические кодировки Если запустить предыдущий пример на выполнение и в качестве ника или сообщения ввести кириллический текст, то вы имеете шанс получить «кракозябры» вместо этого текста. Это обусловлено тем, что Sajax использует при передаче данных метод JavaScript escape(), который превращает специальные символы (в их число входят и киррилические буквы в данном случае) в символы вроде %u0431. Это происходит по причине «любви» escape() к юникоду. Для поправки положения достаточно немного изменить наш шаблон chat.tpl и заменить в нем восьмую строку: document.getElementById("wall").innerHTML = new_data;

на следующую, подставив функцию декодирования unescape(): document.getElementById("wall").innerHTML = unescape(new_data);

Теперь в браузере будет выводиться информация в правильной кодировке, однако в файле data.txt все данные будут по прежнему хранится в закодированном виде (это относится к кириллице). Если такой вариант вас не устраивает, то достаточно открыть файл Sajax.php (собственно файл библиотеки Sajax), найти там сто тридцать пятую строку: 14


Тема с обложки

Sajax – реализация Ajax в PHP x.open(sajax_request_type, uri, true);

И заменить ее на следующую, подставив unescape(): x.open(sajax_request_type, unescape(uri), true);

Теперь и в файл data.txt все будет записано в правильном виде.

PHP Inside #16

Если при разработке своих приложений вы столкнетесь с тем, что кириллические символы из вашего PHP-скрипта неверно отображаются на экране (к примеру, если в одной из PHP-функций вы будете использовать прямой вывод кириллицы на экран), то попробуйте их перед выводом сконвертировать в UTF-8 посредством PHP-функции iconv(). В целом, проблема кириллицы, будучи однажды решенной больше не встает и неудобств не доставляет.

15


Тема с обложки

Ajax и PHP без использования XmlHttpRequest

Ajax и PHP без использования XmlHttpRequest Автор: Деннис Паллетт Перевод: Андрей Олищук Еще один способ динамической загрузки контента без

PHP Inside #16

обновления веб-страницы Ajax стал одним из самых больших «открытий» за последний год и успел стать «умным словечком», что-то вроде Web 2.0. Как известно, Ajax может использоваться для разных целей и он на самом деле ускоряет работу веб-приложений. Эта технология уже сейчас используется многими популярными сайтами, один из которых GMail (почтовый сервис Google). Помимо него можно привести примеры таких сайтов как Ta-Da List (http://www.tadalist.com/) и Fliсkr (http://www.flickr.com/). Даже Microsoft поддается ветру перемен и пытается перевести свои веб-приложения на эту технологию.

Введение Сам способ реализации Ajax-приложений очень завязан на объекте XmlHttpRequest. Большинство современных браузеров, таких как Firefox, уже имеют встроенную поддержку этого объекта, но другие браузеры могут ее не иметь. К примеру, IE 6.0 поддерживает этот объект, однако он реализован в виде элемента ActiveX, что при определенных настройках браузера может привести к тому, что пользователь будет видеть окно предупреждения об опасности ActiveX элемента или приложение не сработает вовсе. В этом руководстве я продемонстрирую вам, как использовать Ajax без необходимости работы с объектом XmlHttpRequest.

Основы Если мы не можем использовать XmlHttpRequest, то необходимо найти иной способ подключать контент из другой странички без перезагрузки и прибегания к другим объектам или нестандартным средствам. Отличным кандидатом на эту роль является тег <script>, который позволяет подключать внешние JavaScript файлы. Что если вместо обычного JS-файла, мы попытаемся подключить PHP-файл, который генерирует и выдает код на JavaScript. Такой файл может выглядеть вот так: <?php $html = '<b>Это контент нашего движка Ajaxe</b>'; ?> div = document.getElementById('contentdiv'); div.innerHTML = '<?php echo $html; ?>';

Как только файл будет подключен к тэгу <script>, он попытается выполнить innerHTML для элемента div с ID равным ‘contentdiv’.

16


Тема с обложки

Ajax и PHP без использования XmlHttpRequest

Тут возникает одна проблема – этот файл не должен быть подключен при загрузке страницы, так как ему необходимо подключиться только после нажатия какой либо кнопки или иного события. Для этого создадим небольшой скрипт на JavaScript, что-то вроде этого: // Получаем url для подключения url = document.x_location.href; xend = url.lastIndexOf("/") + 1; var base_url = url.substring(0, xend); function ajax_do (url) { // Начинается с http? if (url.substring(0, 4) != 'http') { url = base_url + url; }

PHP Inside #16

// Создаем новый JS элемент var jsel = document.createElement('SCRIPT'); jsel.type = 'text/javascript'; jsel.src = url; // Дописываем элемент к документу document.body.appendChild (jsel); }

Этот скрипт первым делом получает текущую директорию URL, и таким образом мы имеем базовый URL. Функция ajax_do() делает остальную работу. Первым делом она определяет, переданный в нее URL относится к другому домену, или является относительным путем внутри своего. Затем создается новый элемент с помощью функции createElement(). После этого, устанавливается атрибут src элемента script и элемент добавляется к телу документа, при этом, подгружая нужный файл на который он ссылается. Теперь нам нужна только обычная HTML-страничка, которая будет осуществлять Ajax-вызовы. К примеру: <html> <head> <title>Demo 1 - Основы</title> <script type="text/javascript" src="engine.js"></script> </head> <body> <div id="contentdiv"> </div> <input type="button" x_onClick="ajax_do ('page1.php');" value="Получить контент" /> </body> </html>

Если вы попробуете этот пример в действии, то заметите, что он работает так же как и «традиционный» Ajax и даже, вероятно, немного лучше в браузере IE. Правда следует учесть, что в IE уровень безопасности не должен быть выше чем Medium (Средний).

Получение контента Чтобы подключить обычный контент с другой странички (который не является JavaScript-кодом) нам потребуется только дополнительный PHP-скрипт, который поможет решить некоторые проблемы.

17


Тема с обложки

Ajax и PHP без использования XmlHttpRequest

Этот PHP-скрипт должен получать содержимое странички и затем отдавать его в виде корректного JavaScript для установки innerHTML элемента. Файл getfile.php может выглядеть следующим образом: <?php // Get URL and div if (!isset($_GET['url'])) { die(); } else { $url = $_GET['url']; } if (!isset($_GET['el'])) { die(); } else { $el = $_GET['el']; }

PHP Inside #16

// Make sure url starts with http if (substr($url, 0, 4) != 'http') { // Set error echo 'alert(\'Security error; incorrect URL!\');'; die(); } // Try and get contents $data = @file_get_contents($url); if ($data === false) { // Set error echo 'alert(\'Unable to retrieve "' . $url . '"\');'; die(); } // Escape data $data = str_replace("'", "\'", $data); $data = str_replace('"', "'+String.fromCharCode(34)+'", $data); $data = str_replace ("\r\n", '\n', $data); $data = str_replace ("\r", '\n', $data); $data = str_replace ("\n", '\n', $data); ?> el = document.getElementById('<?php echo $el; ?>'); el.innerHTML = '<?php echo $data; ?>';

Как вы можете видеть, первым делом получается содержимое файла с использованием функции file_get_contents() и затем выводится обычный JavaScript для установки свойства innerHTML. Функция Ajax-движка, которая будет работать с PHP-скриптом выглядит следующим образом: function ajax_get (url, el) { // Has element been passed as object or id-string? if (typeof(el) == 'string') { el = document.getElementById(el); } // Valid el? if (el == null) { return false; } // Does URL begin with http? if (url.substring(0, 4) != 'http') { url = base_url + url; } // Create getfile URL getfile_url = base_url + 'getfile.php?url=' + escape(url) + '&el=' + escape (el.id); // Do Ajax ajax_do (getfile_url); return true; }

18


Тема с обложки

Ajax и PHP без использования XmlHttpRequest

Первым делом функция проверяет, существует ли указанный элемент, затем создает URL к файлу getfile.php, подключает страничку с помощью знакомой нам ajax_do() функции. Это просто и это работает!

Пример: Форма с использованием Ajax Давайте попробуем использовать наш движок для создания простой формы, которая будет автоматически проверять правильность заполнения полей, основываясь на серверном скрипте. Моя форма выглядит так:

PHP Inside #16

<html> <head> <title>Form Demo - Showing an Ajax Form</title> <script type="text/javascript" src="engine.js"></script> </head> <script type="text/javascript"> function submit_form() { // Get form values var name = document.getElementById('name').value; var email = document.getElementById('email').value; var website = document.getElementById('website').value; // Construct URL url = 'handle_form.php?name=' + escape(name) + '&email=' + escape(email) + '&website=' + escape(website); ajax_get (url, 'result'); } </script> <body> <div id="result"> </div> <b>Name: </b><input type="text" name="name" id="name" /> <b>Email: </b><input type="text" name="email" id="email" /> <b>Website: </b><input type="text" name="website" id="website" /> <input type="button" x_onClick="submit_form();" value="Send Form" /> </body> </html>

А мой обработчик формы выглядит так: <?php // Check variables if (empty($_GET['name'])) { die ('<span style="color:red;">Please fill in your name!</span>'); } if (empty($_GET['email'])) { die ('<span style="color:red;">Please fill in your email address!</span>'); } if (empty($_GET['website'])) { die ('<span style="color:red;">Please fill in your website!</span>'); } echo 'Success! Your form has been submitted!'; ?>

Теперь попробуйте эти скрипты в работе. Для этого оставьте одно из полей пустым.

19


Тема с обложки

Ajax и PHP без использования XmlHttpRequest

Заключение В этом руководстве я показал вам иной метод использования Ajax. Этот метод не имеет тех недостатков, которые есть у XmlHttpRequest, что само по себе хорошо, но у него есть и собственные недостатки. К примеру, он не способен обрабатывать $_POST переменные – только $_GET. Другой недостаток заключается в том, что вы не можете просто взять и и подключить контент из внешнего файла – для этого обязательно потребуется помощь небольшого PHP-скрипта (что и было продемонстрировано).

PHP Inside #16

Еще одним преимуществом данного метода является то, что вы можете использовать кросс-сайтовые скрипты. XmlHttpRequest не позволяет подключать данные с других доменов, а наш метод может использовать PHP-файлы расположенные и на других сайтах. Вы можете легко расширить этот простой пример и развить его до настоящего движка, который сможет работать со всеми браузерами и будет обрабатывать различные ситуации. Я тестировал свои примеры в Firefox, IE6 и Opera 7.1 и они не сработали только в Opera, но я думаю что это можно исправить (В Opera 8.5 все сработало – прим. Перев.). http://www.phpit.net/article/ajax-php-without-xmlhttprequest/

20


Идеи

Ошибки начинающих TDD-практиков

PHP Inside #16

Ошибки начинающих TDD-практиков Сергей Юдин Концепция Test Driven Development (TDD) может стать неэффективной, если ее неправильно использовать. И наоборот, в руках мастера она будет мощным оружием С момента проведения PHP-конференции в Киеве в мае 2005 года прошло достаточно много времени. Тогда мы (я и Павел Щеваев) постарались приложить максимум усилий для популяризации идеи TDD среди PHP-разработчиков. За время после конференции мы намного продвинулись вперед в плане TDD, и взгляд на некоторые вещи у нас изменился. Я признаю, что мой доклад на тему «Целесообразность модульных тестов» получился немного однобоким. Вообще на конференции было очень много сказано о плюсах тестирования, как здорово тесты помогают в разработке и рефакторинге, в профессиональном развитии программистов, но слишком мало – о минусах. О минусах и очень больших минусах тестирования, которые могут проявиться, если трактовать и использовать TDD неправильно. Многие программисты, однажды попробовав работать в стиле TDD, сталкиваются с многочисленными проблемами и бросают тестирование. Они отмечают, что модульное тестирование усложняет процесс разработки, делает его слишком медленным и более трудоемким из-за возрастающего объема тестового кода, который слишком сложно поддерживать. Общаясь с такими разработчиками, мы заметили, что они все сталкиваются с похожими проблемами и делают одни и те же ошибки. Но, как правило, во всем они винят именно тесты. Эти критические замечания насчет TDD дали мне повод провести дополнительные исследования материалов в Сети, касающихся TDD, а также сделать небольшой анализ нашего пути к использованию TDD в повседневной практике программирования и опыт некоторых знакомых нам разработчиков. В данной статье я постараюсь рассказать об ошибках, которые многие начинающие TDD-практики допускают, о минусах, которые могут быть связаны с тестированием и о том, как этих минусов избежать.

Правильно понимаем TDD Начнем с того, что большинство разработчиков неправильно понимает идею TDD. Самое типичное недоразумение, связанное с TDD: «Мы используем в своей работе SimpleTest или PhpUnit, значит, мы работаем в стиле TDD.» Этот совсем не обязательно. Люди, которые пишут (или пытаются написать) модульные тесты для своих классов – не обязательно занимаются TDD. Как-то в одной из TDD-рассылок я наткнулся на некую классификацию способов применения модульных тестов разработчиками: Традиционный – когда разработка ведется полностью через тесты, один тест за раз, при этом активно применяется рефакторинг. Первоначальная реализация рабочего кода обычно является нарочно упрощенной, конечный дизайн кода получается последовательно и только исходя из появления новых тестов

21


Идеи

Ошибки начинающих TDD-практиков

PHP Inside #16

Активный – отличается от традиционного тем, что разработчик сначала обдумывает дизайн рабочего кода, а затем начинает целенаправленно идти к этому дизайну через тесты. Приемочный - вместо того, чтобы писать небольшие тесты, разработчик пишет сразу конечный тест, который реализует конечную функциональность. Далее он продумывает дизайн реализации, и всю несуществующую функциональность забивает мокобъектами, стараясь запустить тесты как можно раньше. После этого постепенно убирает мок-объекты, заменяя их реальными классами. Только первый вариант можно считать полноценным TDD. Второй вариант – это так называемая методика test-first development, то есть TDD без первой D – driven. Третий вариант никакого отношения к самому TDD не имеет, а должен применяться параллельно в виде приемочных тестов. TDD – это процесс итеративного, непрерывного, параллельного написания тестов и рабочего кода, с обязательными фазами рефакторинга. Очень многие разработчики начинают сразу с набора модульных тестов для нового класса, а лишь только затем пишут сам класс. В итоге они тратят полдня на то, чтобы эти тесты сработали. Им приходится отлаживать не только рабочий код, но и тесты, при этом любое изменение структуры заставляет их переписывать большие участки тестового кода - это нудно, неинтересно и занимает много времени. Это вовсе не TDD и даже не test-first. Когда вы занимаетесь TDD, вы должны стремиться минимизировать размер контекста, в котором вы работаете. Это относится как к тесту, так и к рабочему коду. Мы обычно пишем тесты очень мелкими шагами и не пытаемся представить, как будет выглядеть окончательный код. Это позволяет очень рано увидеть зеленую линию и обойтись без debug сессии при начальном запуске тестов. Даже если и так понятно, как будет выглядеть все остальные тесты и сам рабочий код класса, мы предпочитаем написать короткий тест, затем небольшую часть рабочего кода. Затем можно двигаться быстрее и увереннее. Конечно, каждый самостоятельно может определять, что для него «небольшие шаги», но общая рекомендация такая – старайтесь сокращать промежутки между запусками тестов. При активной разработке мы обычно запускаем тесты раз в 2-3 минуты и даже чаще. Это позволяет проверять написанный код сразу же, не теряя контекста, то есть, помня все детали только что измененного кода. Рассмотрим «активный» и «приемочный» способы использования тестов (см. выше). Мы начинали свою практику TDD именно с «активного» способа применения модульных тестов, рисуя UML-диаграммы, а затем старались при помощи тестов реализовать свои задумки в коде. Но постепенно мы отказались от повседневного применения этой практики. Дело в том, что если разрабатывать, ставя во главу test-ability, то есть возможность протестировать код, то полученная реализация всегда будет отличаться задуманной, а время, затраченное на детальную проработку дизайна, становится жаль. Теперь мы стараемся не тратить больше 10-15 минут на дизайн-сессии, достаточно пары небольших набросков на доске, которые дадут ориентировочную картину, и можно приступать к кодированию. Кстати, я вовсе не хочу сказать, что TDD заменяет фазу анализа проекта. Архитектурные решения, оказывающие влияние на весь проект в целом, все равно нужно принимать. В конце концов, TDD никак не может заменить аналитические способности разработчика, его умение предугадывать развитие проекта. Но TDD реально позволяет выбраться из ситуации архитектурного тупика (design deadlock), когда вообще непонятно, как должна выглядеть реализация.

22


Идеи

Ошибки начинающих TDD-практиков

PHP Inside #16

«Приемочный» способ использования тестов искажает смысл понятия «модуль». Обычно в данном случае разработчик пишет тесты на классы высших уровней (фасады). В этом случае понятие модуля (unit) становится слишком большим, поэтому тест становится бесполезным при поиске ошибок. Такой тест зачастую увеличивается в размере и усложняется, его становится тяжелее читать и понимать. Такой критерий как test-ability здесь уже не играет главной роли, поэтому влияние тестов на дизайн будет значительно меньше, чем в традиционном TDD. Обязательным условием успешного внедрения TDD является фаза рефакторинга. После получения рабочего кода и зеленой полосы необходимо обязательно критически посмотреть на код и сделать хотя бы несколько шагов для улучшения читабельности и понятности кода. Идеально, если после рефакторинга код вообще бы не требовал inline комментариев. Важно также как можно чаще при этом запускать тесты, чтобы не сталкиваться с красной полосой. В книге Мартина Фаулера «Рефакторинг: улучшение дизайна существующего кода» техника рефакторинга описана очень подробно.

Правильно внедряем TDD Разработку через тестирование никак нельзя освоить за пару недель. По нашему опыту и опыту некоторых других разработчиков на первоначальное изучение методик тестирования уходит где-то 3-4 месяца. Еще полгода-год нужно на перестройку сознания разработчика. Итого – около года, иногда дольше. Почитайте книги по TDD – это 60% код и еще 35% - комментарии к ним и 5% теории. TDD – это набор лучших практик (best practices), самым лучшим образом зарекомендовавших себя в разработке программного обеспечения. Многие из них нужно попробовать на себе, чтобы понять их важность. TDD должен стать каждодневной реалией вашего рабочего процесса. При этом вам обязательно придется столкнуться с трудностями. Невозможно в кратчайшие сроки быстро научиться писать хорошие тесты, создавать оптимальное количество тестов, двигаться ровными шагами (тест-код-тест-код-рефакторинг), правильно проводить изоляцию тестов, давать правильные имена методам и переменным и т.д. Именно поэтому желательно, чтобы в команде был человек (в XP он называется тренер coach), который имеет опыт тестирования, особенно при внедрении модульного тестирования в готовый проект. Именно наличие такого человека поможет избавиться от многих проблем, связанных с введением TDD, например, с появлением запахов тестового кода. Тренер может в разы сократить сроки внедрения TDD в какую-либо команду. Мы много раз ловили себя на том, что неправильно используем тесты, неправильно проводим большие рефакторинги, теряем ритм разработки и т.д. Только по прошествии 2-х лет очень интенсивной практики TDD мы можем с сказать, что TDD гармонично слилась с нашим рабочим процессом, и мы можем пожинать плоды ее использования. Многие разработчики (и мы в том числе) заметили, что внедрять TDD лучше всего или на новом проекте (можно учебном), или же с отдельных классов. Мы вообще не рекомендуем начинать внедрение TDD на реальных коммерческих проектах, особенно на больших и со сложным наследием. Без унаследованного кода (legacy code) внедрять TDD намного проще, так как в этом случае есть возможность полностью контролировать ситуацию и принимать какие угодно решения. Наш (и не только наш) опыт подсказывает, что в первые месяцы внедрения TDD появляется сильнейшее желание переделывать одну и туже работу по несколько раз, так как происходит смена мировоззрения разработчика - он начинает осознавать настоящие преимущества того или иного дизайна. Поэтому свобода действий является очень важным критерием успешности на начальном этапе.

23


Идеи

Ошибки начинающих TDD-практиков

PHP Inside #16

Идеально, если на начальном этапе внедрения TDD вы не будете ограничены сроками. Первое время при внедрении TDD происходит значительное снижение скорости работы – где-то в 2-3 раза, поэтому многие разработчики срываются и пишут код без тестов. После того, как вы освоите большинство продвинутых методик тестирования, скорость снова возрастет и даже будет выше, чем раньше. Еще один момент - с самого начала внедрения модульных тестов мы работали в паре. Работа в паре вообще очень полезная штука, так как она повышает интенсивность обучения, не позволяет лениться, придает смелости в принятия порой очень важных архитектурных решений и повышает качество программного кода. Начинать внедрение модульного тестирования лучше небольшими шагами и по направлению снизу вверх. То есть сначала тестировать библиотечные классы нижних уровней. При этом разработчик должен сначала привыкнуть к тестам, научиться читать их, использовать их в качестве документации к коду, рассматривать классы с точки зрения возможности их протестировать. В этом случае разработчик будет иметь дело с небольшими модулями (unit), а это действительно важно на начальном этапе. Лишь много позже можно переходить к более сложным случаям, например, к тестам взаимодействия, к расширенному применению мок-объектов, к регрессионным тестам. Если же у вас появились серьезные проблемы с тестами, посмотрите на список наиболее часто появляющихся запахов тестового кода – может быть вы наткнулись на один или сразу нескольких из них. Это позволит понять причины своих неудач и не бросить начатое. Список запахов тестового кода доступен в разделе TDD по адресу http://phpclub.ru/faq/wakka.php?wakka=TDD/TestCodeSmells.

Расширяем свои познания об ООП TDD и ООП идут рука об руку. Поэтому TDD очень плохо подходит для разработчиков, которые не умеют пользоваться продвинутыми методиками ООП, не понимают сути ООП, а также не имеют достаточного опыта программирования вообще. При неправильно выбранной архитектуре системы тесты становятся сильнейшим тормозом в развитии любого проекта, так как могут увеличивать стоимость внесений изменений и поддержки в разы. Теряется мобильность, увеличиваются сроки – страдают все: и разработчики, и менеджеры, и клиенты. Как-то на форуме сайта sitepoint.com я наткнулся на очень интересное высказывание Маркуса Бейкера (создателя пакета для тестирования SimpleTest) насчет объектноориентированного программирования. Суть этого высказывания сводится к тому, что большинство разработчиков начинает использование ООП, не разобравшись до конца (или вообще не разобравшись) в его сути. Начинающий (в плане опыта применения ООП) программист, познакомившийся с концепциями (или даже просто с синтаксисом) ООП, однажды создает несколько классов, каждый из которых потянет на целый пакет и после этого сразу начинает проектировать большую систему, возомнив себя крутым архитектором. Конечно, у него ничего не получается. После этого такие разработчики читают множество книг по паттернам и застревают еще больше, так как не понимают, зачем эти паттерны нужны, и где правильно их применять. Классы становятся обузой и многие их них переходят обратно к процедурному программированию, так как не находят в классах ничего хорошего. Опытные разработчики, по словам Маркуса, используют ООП для того, чтобы не проектировать вовсе! Такие понятия, как повторное использование кода давно устарели. Классы нужно любить за их гибкость, а не за возможность повторного использования. Чтобы понять это нужно очень много времени и упорства или же наличие опытного наставника.

24


Идеи

Ошибки начинающих TDD-практиков

PHP Inside #16

Мы начинали практику TDD осенью 2003 года с типичной ситуации, описанной Маркусом Бейкером. У нас была своя доморощенная CMS, где каждый класс был богоподобным, везде встречались такие модные словечки, как Factory, Singleton, Fa?ade и т.д. Но это была ужасная система. И в этой ситуации мы начали внедрение TDD. Угадайте к чему мы пришли через полгода? Практически к тому же самому! Только теперь к ужасному коду (правда тогда мы так не считали!) добавились еще и неповоротливые тесты. По наивности мы думали, что написание тестов – это и есть TDD. В итоге мы получили закостеневшую и неповоротливую систему. После этого мы поняли, что без коренной перестройки наших знаний об ООП успеха нам ждать не следует, и мы реально учились многому с нуля, открывая многие вещи заново, например, паттерны проектирования. Вот список книг, которые любой TDD-практик просто обязан прочитать (must read) и иметь в любой момент на своем столе: •

Э. Гамма и др. «Design patterns» (GOF).

М. Фаулер «Refactoring: Improving the Design of Existing Code».

М. Фаулер «Patterns of Enterprise Applications Architecture».

Р. Мартин «Agile software development».

Все эти книги есть на русском языке. Я уверен, что по мере совершенствования ваших навыков модульного тестирования вам захочется перечитать эти книги не раз и не два. Также очень полезной оказалась книга V. Manson «Junit In Action», к сожалению ее пока нет на русском языке. Несмотря на то, что она написана для Java, многие идеи, описанные в этой книге, применимы к любому языку. Более полный список книг вы можете найти в разделе TDD по адресу http://phpclub.ru/faq/wakka.php?wakka=TDD/TDDBooks Любой продвинутый TDD-практик (да в принципе любой программист) должен отлично понимать, что такое зависимости между отдельными классами и подсистемами, как эти зависимости снижать, какие при этом могут использоваться методики, что такое клиент-ориентированный API и т.д. Вы должны стремиться к пониманию сути большинства базовых патеров, например, Декоратор или Стратегия, в каких случаях их следует применять. Мне лично очень сильно помогла в плане ООП развития книга Р.Мартина «Быстрая разработка программ» (Agile software development). Кстати, когда мы только начинали практику TDD, нам казалось, что мы очень отлично подкованы в плане ООП, однако оказалось, что это далеко не так. Кстати, это заметили не только одни мы. Люди, которые имеют опыт программирования 5 и более лет, по прошествии лишь 3-4 месяцев практики TDD признают, что тесты заставили их посмотреть на многие вещи другими глазами. Написание тестов очень наглядно показывают целесообразность многих ООП методик, которые многие знают и так, но эти знания лежат у них обычно мертвым грузом. Моя сегодняшняя точка зрения состоит в том, что начинать практику TDD именно на реальных коммерческих проектах без большого опыта программирования – это очень серьезный риск. С тестированием может быть связано много проблем, если использовать его неправильно. В тоже время, по моему глубокому убеждению, TDD – это прекрасный стимул для развития программиста. Тесты обнажают многие проблемы архитектуры, позволяют делать рабочий код лучше, способствуют профессиональному росту разработчика, позволяют делать меньше ошибок, проводить рефакторинги и т.д. По мере вашего развития мы обязательно научитесь выделять четкие интерфейсы, применять паттерны, где это целесообразно, создавать классы с четкими зонами ответственности и снижать зависимости между различными компонентами.

25


Идеи

Ошибки начинающих TDD-практиков

PHP Inside #16

Но извлекать эти преимущества можно, только имея соответствующую подготовку, и потребуется достаточно много времени для получения ощутимого эффекта. И если вы все же решили попробовать свои силы в TDD – приготовьтесь к тому, что вам придется многому учиться.

TDD – это не самоцель Тесты – это не вещь в себе. Но часто разработчики впадают в крайность – это ситуация, когда тестам начинают уделять повышенное внимание. Разработчики пишут слишком много тестов, проверяют все и вся. И это однажды перерастает в разработку ориентированную на тесты (testing oriented development anti-pattern). Джемс Гринвуд так описывает этот анти-паттерн: «Когда неопытные или введенные в заблуждение программисты продолжают писать тесты, хотя в этом нет никакого смысла с практической или финансовой точки зрения. При этом они думают, что это и есть TDD и, добавляя в систему все новые и новые тесты, они увеличивают ее стоимость». Думается, что комментировать это определение нет смысла. Попробую описать другой случай, когда тесты становятся самоцелью и мешают развитию проекта. Иногда это связывают с запахом «Чрезвычайное упования на мокобъекты», но иногда причина кроется в другом. Мы сталкивались с данной проблемой достаточно давно, но последствия дают о себе знать до сих пор. Суть ее такова: у нас был код, который очень трудно поддавался тестированию, так как он не был создан с учетом test-ability. Но, не понимая сути TDD, мы вместо того, чтобы искать причины в дизайне системы, делали все возможное, чтобы просто написать тесты. В результате мы получили полный хаос с частичными или полными мок-объектами, методами вида doParentSomething(), непонятными и огромными фикстурами. То есть мы плодили ненужный код, который тоже нужно было поддерживать. При этом тесты являлись дополнительным фактором загнивания проекта, так как они были очень хрупкими, хотя по идее они должны были напротив повышать его гибкость и качество. Лишь намного позже мы поняли, что тесты наглядно нам показывали, что у нас есть большие проблемы c дизайном рабочего кода. Хороший рабочий код не должен иметь сложных тестов! Это показатель того, что архитектура имеет серьезные недостатки. После правильного применения техник снижения зависимостей между классами, тесты значительно упростились, и их поддержка теперь не составляет труда. Об ошибках, связанные с написанием слишком большого количества тестов до рабочего кода мы указывали ранее. Никакого реального преимущества тесты в этом случае также не дают, а становятся вещью в себе. TDD – это прежде всего design activity, то есть деятельность направленная на формирование дизайна системы. Тесты выступают в качестве примеров реального использования кода и именно в этом их одна из основных ценностей. Досконально проверять рабочий код при помощи тестов – это слишком дорого. Тесты должны помогать в разработке, делать ее более предсказуемой и управляемой, а не трудоемкой и нудной. В конце концов, они должны позволить нам зарабатывать больше денег, делать свою работу быстрее и лучше.

Выводы Разработка через тестирование для меня была чем-то вроде серебряной пули, решением всех проблем. Я готов был с горящими глазами доказывать, что тесты – панацея от всех бед, что нужно всем скорее начинать практику написания тестов.

26


Идеи

Ошибки начинающих TDD-практиков

PHP Inside #16

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

Полезные ссылки: Раздел по TDD на phpclub.ru - http://phpclub.ru/faq/wakka.php?wakka=TDD

27


Идеи

Основы использования PHP для работы с FTP

PHP Inside #16

Основы использования PHP для работы с FTP Авторы: Сообщество developersdigest.org/wiki/ Перевод: Андрей Олищук Руководство затрагивает основы использования PHP для работы с FTP протоколом По прочтению руководства вы сможете: •

Устанавливать соединения с FTP-сервером.

Авторизовываться с вашим логином и паролем.

Проверять статус соединения.

Загружать на сервер файлы.

Проверять статус загрузки.

Закрывать соединения.

Использовать дополнительные возможности.

Подключение к FTP-серверу Установка соединения Установка соединения при помощи PHP – ничего нет проще. Вы сможете подключиться к серверу, используя всего одну строку кода. <?php // Установка соединения $ftp_server = "ftp.developersdigest.org"; $conn_id = ftp_connect($ftp_server); ?>

Авторизация После того, как соединение было установлено, вы можете авторизоваться на сервере со своим логином и паролем. <?php // Авторизация $ftp_user_name = "your_username"; $ftp_password = "your_pass"; $login_result = ftp_login($conn_id, $ftp_user_name, $ftp_password); ?>

28


Идеи

Основы использования PHP для работы с FTP

Проверка статуса соединения

PHP Inside #16

Если вы подключились и авторизовались, то будет неплохой идея проверить статус соединения, чтобы убедиться, что все прошло хорошо. <?php // Проверка статуса соединения if ((!$conn_id) || (!$login_result)) { echo "Ошибка подключения!\n"; echo "Попытка подключения к $ftp_server под пользователем $ftp_user_name"; exit; } else { echo "Подключение к $ftp_server под пользователем $ftp_user_name прошло успешно"; } ?>

Передача файлов Загрузка файлов на сервер Пришло время для самого интересного – загрузки файлов. Существует простейшая функция, которая добавляет «вкусности» в работу с FTP. <?php // загружаем файл $upload = ftp_put($conn_id, $destination_file, $source_file, FTP_BINARY); ?>

Вот так! Функция ftp_put() получает в качестве параметров следующие данные: id соединения, путь и имя будущего файла, и путь и имя закачиваемого исходного файла. Последний параметр определяет метод загрузки и может быть бинарным (FTP_BINARY) или ASCII (FTP_ASCII) – это зависит от типа файла, который вы собираетесь закачать.

Проверка статуса загрузки После того как загрузка завершена, будет неплохо проверить – прошла ли она успешно. К примеру, обрыв соединения в процессе загрузки вызовет ее прерывание и файл не будет успешно загружен. Это так же просто, потому как переменная $upload будет содержать результат выполнения функции ftp_put() (это будет TRUE в случае успеха и FALSE в случае ошибки). <?php // Проверим статус загрузки if (!$upload) { echo "Загрузка не удалась!"; } else { echo "Загрузка $source_file на $ftp_server под именем $destination_file завершена успешно"; } ?>

Закрытие FTP-соединения Чтобы сэкономить системные ресурсы, всегда необходимо закрывать FTP-соединения.

29


Идеи

Основы использования PHP для работы с FTP

PHP Inside #16

<?php // Закрываем FTP поток ftp_close($conn_id); ?>

Получение информации о размере файла Для получения размера файла можно использовать функцию ftp_size(), которая возвращает размер файла в байтах. Синтаксис: int ftp_size ( resource ftp_stream, string remote_file )

Пример: <?php $file = "your_file.txt"; // Получаем размер $file $size = ftp_size($conn_id, $file); ?>

Удаление файла с сервера Загружая файлы на сервер, вы всегда имеете возможность удалить их в последствии. Для этого необходима функция ftp_delete(). Синтаксис: bool ftp_delete ( resource ftp_stream, string path )

Пример: <?php $file = "delete_me.txt"; // Пробуем удалить $file if (ftp_delete($conn_id, $file)) { echo "$file успешно удален\n"; } else { echo "Не могу удалить $file\n"; } ?>

Создание директорий (каталогов) Если вам необходимо создать директорию (каталог), то можно использовать функцию PHP – mkdir(). Синтаксис: string ftp_mkdir ( resource ftp_stream, string directory )

Пример: <?php $dir = "path/to/dir"; // Пробуем создать директорию $dir

30


Идеи

Основы использования PHP для работы с FTP

PHP Inside #16

if (ftp_mkdir($conn_id, $dir)) { echo "Успешно создана $dir\n"; } else { echo "При создании $dir возникли проблемы\n"; } ?>

Смена директории Подключаясь к FTP-серверу, вы попадаете в определенную директорию. Для того чтобы перейти в другую, используйте функцию ftp_chdir(). Синтаксис: bool ftp_chdir ( resource ftp_stream, string directory )

Пример: <?php $dir = "dir"; // Пробуем попасть в директорию $dir if (ftp_chdir($conn_id, $dir)) { echo "Успешно перешли в $dir\n"; } else { echo "При переходе в $dir возникли проблемы\n"; } ?>

Удаление директории Лишние директории можно удалить функцией rmdir(). <?php bool ftp_rmdir ( resource ftp_stream, string directory ) $dir = "path/to/dir"; // Пробуем удалить директорию $dir if (ftp_rmdir($conn_id, $dir)) { echo "Успешно удалили $dir\n"; } else { echo "Проблема при удалении $dir\n"; } ?>

Здесь необходимо учитывать, что данная функция удалит лишь пустую директорию.

Переименование файлов и директорий Если возникла необходимость переименовать файл или директорию на FTP-сервере, то используйте функцию ftp_rename(); <?php bool ftp_rename ( resource ftp_stream, string oldname, string newname ) $old_file = "path/to/old_file.txt"; $new_file = "path/to/new_file.txt"; // Пробуем переименовать $old_file в $new_file if (ftp_rename($conn_id, $old_file, $new_file)) { echo "Успешно переименовали $old_file в $new_file\n";

31


Идеи

Основы использования PHP для работы с FTP

PHP Inside #16

} else { echo "Возникла проблема\n"; } ?>

Получение списка файлов в директории Для получения массива имен файлов в определенной директории, можно использовать функцию ftp_nlist(). <?php array ftp_nlist ( resource ftp_stream, string directory ) // Получаем массив с именами файлов $contents = ftp_nlist($conn_id, "."); ?>

Полный список FTP-функций можно найти по ссылке: http://ru.php.net/manual/en/ref.ftp.php Оригинал материала: http://developersdigest.org/wiki/index.php/Uploading_with_FTP

32


Идеи

Сборка PHP 5 с драйверами mysql и mysqli одновременно

PHP Inside #16

Сборка PHP 5 с драйверами mysql и mysqli одновременно Автор: Джон Бернс Перевод: Андрей Олищук Бывают случаи, когда на одном сервере одновременно должны работать приложения с использованием интерфейсов MySQL и MySQLi. Джон Бернс расскажет как этого добиться на Linux На протяжении нескольких лет библиотека mysql (иногда ее называют ext/mysql для большей понятности) являлась стандартом PHP-разработки и огромнейшее число PHP-приложений, работающих с MySQL использует именно эту библиотеку. С приходом PHP 5 и MySQL 4.1 появилось новое расширение mysqli, название которого расшифровывается как «MySQL Improved» (улучшенная). Итак, чего же такого хорошего есть в mysqli и почему нужно переходить на это расширение? Ответ прост: новый драйвер mysqli, работающий под PHP 5 просто превосходен! (в оригинале используется более грубое выражение – kicks ass, - прим. переводчика). Для тех, кому нужны более веские обоснования, к примеру для представления их боссу или клиенту, позвольте мне привести следующие причины: •

Объектно-ориентированный интерфейс

Поддержка нового бинарного протокола, представленного в MySQL 4.1. Новый протокол гораздо более эффективен чем старый и позволяет использовать некоторые новые особенности MySQL такие как, например, подготовленные выражения.

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

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

Лучшая скорость. На некоторых операциях ext/mysqli превосходит ext/mysql в 40 раз.

Лучшая безопасность. В старых версиях СУБД MySQL были некоторые уязвимости, которые позволяли атакующему извлекать хеши паролей из сети и воссоздавать по ним пароли пользователей. Новая процедура аутентификации более надежна и похожа на процедуры аутентификации таких инструментов как SSH.

Но если mysqli такая великолепная штука, то почему все еще не все начали ею пользоваться? Реальность такова, что много кода написано под расширение mysql и он не будет работать с mysqli. Не всегда есть возможность переделывать этот код или сервер может содержать готовые приложения, которые не работают с mysqli (это, к примеру, MediaWiki, WordPress), а стандартным образом собрать PHP с двумя расширениями сразу не получится.

33


Идеи

Сборка PHP 5 с драйверами mysql и mysqli одновременно

PHP Inside #16

Хорошие новости состоят в том, что mysql и mysqli все же могут сосуществовать на одном сервере и вы можете позволить старым приложениям работать с ext/mysql, а новые разрабатывать на mysqli. Я приведу способ установки PHP 5 с активированными и mysql и mysqli. Для этого, когда вы запускаете команду ./configure, используйте следующий синтаксис: ./configure --with-mysql=/usr –with-mysqli=/usr/bin/mysql_config

Заметьте, что директории для mysql и mysqli различны! Так и должно быть. Размещение этих директорий может быть различным, в зависимости от операционной системы, которую вы используете. Итак, наберите в командной строке: slocate mysql_config

На RedHat/Fedora ответ может быть следующим: /usr/bin/mysql_config

Эта директория mysql_config и будет являться директорией для --with-mysqli, в то время, как директория на два уровня выше mysql_config (в нашем случае это /usr) будет соответсвовать для --with-mysql. Но такая команда ./configure создаст неправильный makefile, поэтому созданный файл makefile необходимо открыть в текстовом редакторе и найти строку, которая начинается с EXTRA_LIBS: EXTRA_LIBS = -lcrypt -lcrypt -lmysqlclient -lpng -lz -lz -lresolv -lm -ldl -lnsl -lxml2 -lz -lm -lxml2 -lz -lm -lmysqlclient -lcrypt -lnsl -lm -lz -lnss_files -lnss_dns -lresolv -lnss_files -lnss_dns -lresolv -lxml2 -lz -lm -lcrypt -lxml2 -lz -lm -lcrypt

Вы можете заметить, что параметр “-lmysqlclient” встречается дважды. Удалите один из них и сохраните файл. Теперь, если запустить команду make, PHP 5 скомпилируется как надо и будет поддерживать оба расширения – mysql и mysqli! Это все!

34


Бонус

Учебник по созданию компонент Joomla!

PHP Inside #16

Учебник по созданию компонент Joomla! Автор: Джозеф ЛеБланк Перевод: Григорий Федоринов Joomla! Является одной из наиболее популярных открытых систем управления контентом. На примере создания компонента «Ежедневные сообщения» автор демонстрирует принцип разработки компонент для этой CMS Это пособие должно помочь вам создать компонент с функциональной зоной администрирования. После прочтения вы будете понимать как работают некоторые классы из ядра Joomlaи сможете использовать наилучшие методы для создания своих собственных компонентов. Полностью законченная версия компонента «Ежедневные сообщения » доступна в приложении к журналу. Компонент также может устанавливаться на Mambo 4.5 и 4.5.1. Дополнительную информацию на русском языке можно найти здесь: http://myjoomla.ru/content/view/12/28/ Различные люди будут понимать функциональность компонентов joomla по разному. Разработчики с опытом разработок на PHP могут начинать с файла dailymessage.php (файл который управляет фронтендом – зоной пользователя) и admin.dailymessage.php (файл который управляет бекендом – зоной администратора). Остальные будут начинать понимать с XML файла в котором описаны все содержащиеся файлы в компоненте, а также SQL – запросы для создания таблиц необходимых для компонента в базе данных. dailymessage.php admin.dailymessage.php admin.dailymessage.html.php dailymessage.class.php toolbar.dailymessage.php toolbar.dailymessage.html.php install.dailymessage.php / uninstall.dailymessage.php XML file

dailymessage.php Этот файл отображает то что увидит пользователь когда компонент «Ежедневные Сообщения» загружен. Хотя этот код прост, это все что необходимо для генерирования HTML для любой версии Joomla. Это только файл, который копируется в папку components/com_dailymessage. Расскажем подробнее что в этом файле находится: defined('_VALID_MOS') or die( 'Direct Access to this location is not allowed.');

Эта строка описывает, что только Joomla может вызвать на исполнение этот файл. Именно с нее должны начинаться все php-файлы – это сделано в целях безопасности.

35


Бонус

Учебник по созданию компонент Joomla! global $database;

PHP Inside #16

Объект $database объявляется в ядре Joomla и директива 'global' делает этот объект доступным в компоненте. // Получаем информацию о конфигурации $database->setQuery("SELECT * FROM mos_joe_dailymessage_conf LIMIT 1"); $rows = $database->loadObjectList(); $row = $rows[0];

Запуск MySQL запросов в Joomla состоит из 2-х этапов. Первый: вы используете метод setQuery() чтобы сохранить текст SQL запроса в объекте $database. Когда вы используете функцию, такую как loadObjectList(), то SQL запрос выполнятеся и возвращается результат запроса. Строка $row = $rows[0] присваивает переменные из первого объекта в списке в массив $row. При этом мы возвращаем информацию о конфигурации из базы данных, которая хранится в одной записи. Также , в MySQL запросах Joomla можно заменять префикс ‘mos_’ на '#_' в этом случае даже если префикс установленной базы данных joomla отличается от ‘mos_’, то запрос все равно остается работоспособным. Это позволяет запускать компонент на сайтах где установлено несколько CMS в одной базе данных, отличающихся префиксом. $bold = $row->bold; $italic = $row->italic; $underline = $row->underline; $showdate = $row->showdate;

После загрузки результата запроса в массив, мы помещаем отдельные значения из массива в переменные для дальнейшего их использования. $starttags = ""; $endtags = ""; if($bold == 1) { $starttags .= "<b>"; $endtags = "</b>" . $endtags; } if($italic == 1) { $starttags .= "<i>"; $endtags = "</i>" . $endtags; } if($underline == 1) { $starttags .= "<u>"; $endtags = "</u>" . $endtags; }

Этот код выстраивает начало и конец строки настроек, которые были выбраны из базы данных. Эти строки будут использованы ниже для форматирования сообщения. //получение данных $database->setQuery("SELECT * FROM mos_joe_dailymessage WHERE published = '1'"); $rows = $database->loadObjectList();

36


Бонус

Учебник по созданию компонент Joomla!

Используя объект $database как показано выше мы устанавливаем запрос и выводим содержание сообщений из таблицы в список объектов $rows.

PHP Inside #16

?><table><?

Теперь мы начинаем вывод HTML кода. Тег ?> разрешает использовать HTML в php файле пока не начнется тег <?. В противном случае надо использовать оператор echo (). foreach($rows as $row) { if($showdate == 1) echo "<tr><td>" . mosFormatDate($row->date) . "</td>"; else echo "<tr>"; echo "<td>" . $starttags . $row->message . $endtags . "</td></tr>"; }

Этот цикл просматривает каждый объект в массиве объектов $rows и загружает содержание в $row. Теперь мы способны вывести информацию и строки компоновки, которые выведут должным образом отформатированный HTML. ?></table>

Вывод заканчивается закрытием тега table.

admin.dailymessage.php Этот файл управляет видом, который видит администратор, когда конфигурирует компонент. Большая часть вида генерируется через функцию HTML_joeDailyMessage [от скромности я не умру :) ] находящуюся в файле admin.dailymessage.html.php . <?php defined('_VALID_MOS') or die('Direct Access to this location is not allowed.'); // проверим что пользователь имеет доступ к этой функции if (!($acl->acl_check( 'administration', 'edit', 'users', $my->usertype, 'components', 'all' ) | $acl->acl_check( 'administration', 'edit', 'users', $my->usertype, 'components', 'com_dailymessage' ))) { mosRedirect( 'index2.php', _NOT_AUTH ); }

Так выполняется проверка чтобы убедится что пользователь является администратором. require_once($mainframe->getPath( 'admin_html' ) ); require_once($mainframe->getPath( 'class' ) );

37


Бонус

Учебник по созданию компонент Joomla!

PHP Inside #16

Эти две строки включают файлы admin.dailymessage.html.php и dailymessage.class.php. Функция getPath() возвращает соответствующие полные пути и имена файлов. $id = mosGetParam( $_REQUEST, 'cid', array(0) ); if (!is_array( $id )) { $id = array(0);

Главный вид имеет форму на которой расположен список состоящий из чекбоксов имеющий name = 'cid.' Функция mosGetParam() возвращает этот массив и сохраняет в переменной $id. Если массив не предстален (например когда загружена первая страница), $id устанавливается в пустой массив чтобы предотвратить ошибки, которые могли бы возникнуть в дальнейшем. switch($act) { case "configure": switch($task) { case "save": saveConfiguration($option); break;

default: listConfiguration($option); break; } break; default: switch ($task) { case "save" : save($option); break; case "edit" : edit( $option, $id ); break; case "new" : $id = ''; edit( $option, $id);

38


Бонус

Учебник по созданию компонент Joomla! break;

PHP Inside #16

case "delete" : del($option, $id); break; case "publish" : publishMessage($option, '1', $id); break;

case "unpublish" : publishMessage($option, '0', $id); break; case "listMessages" : default: listMessages($option); }

break;

break; }

Теперь наступает подходящий момент поговорить о различиях между $task и $act. Когда администратор делает щелчок на кнопке "publish" или "save," действие должно быть передано компоненту. Это достигается через переменную $task. Эта кнопка используется для различных форм и это необходимо для того чтобы компонент узнал какая форма была отправлена в данный момент. Теперь настает черед переменной $act. В главном меню, при выборе Daily Message, появляется два субменю - опции: Edit Messages и Configure. Эти опции называются 'all' и 'configure’ соответственно. Компонент Daily Message использует оператор switch, чтобы выбрать действие, которое будет совершено. Внешний переключатель основан на переменной $act, которая может принимать значения такие как 'all' и 'configure'. function saveConfiguration($option) { global $database; $row = new joeDailyMessageConf($database);

Теперь описание функций для обработки различных заданий. Первая будет обновлять базу данных с новыми переменными, когда кто-нибудь нажмет кнопку 'save' на странице конфигурации. Первое, переменная $option variable применяется в функции и будет установлена в 'com_dailymessage' окружением Joomla.

39


Бонус

Учебник по созданию компонент Joomla!

PHP Inside #16

Это значение будет передано после того как пользователь зайдет в администраторскую часть данного компонента. Следующее объект $database уже определен вне функции. Когда мы декларируем какой либо объект $row например joeDailyMessageConf [defined in dailymessage.class.php], являющийся расширением класса mosDBTable. Объекты основаны на mosDBTable имют много методов, которые позволяют упростить процесс записи информации в базу. // bind it to the table if (!$row -> bind($_POST)) { echo "<script> alert('" .$row -> getError() ."'); window.history.go(-1); </script>n"; exit();

Этот код использует наследованный метод bind() от класса mosDBTable для получения значения переменных из массива post и копирования их в объект $row. Если попытка использовать этот метод не удалась, a Javascript показывает сообщения об ошибке. Ошибка показывается Javascript как всплывающее окно, которое возвращает пользователя на предыдущую страницу. // сохраняет запись в базе if (!$row -> store()) { echo "<script> alert('" .$row -> getError() ."'); window.history.go(-1); </script>n"; exit(); }

Эта часть кода описывает попытку сохранить информацию из объекта $row в базу данных. Обработка ошибок такая же как и в предыдущей части. mosRedirect("index2.php?option=$option&act=configure","ConfigurationSaved"); }

В заключении, если все прошло успешно, пользователь перемещается назад к странице конфигурирования с сообщением "Configuration Saved", которое будет показано вверху экрана. Переменная $option, говорит Joomla страница какого компонента будет использоваться т.е. если значение переменной 'com_dailymessage' то выводится страница администрирования компонента Daily Message. Замечание: если URL не содержит переменную 'act' со значением 'configure', то он перемещается на страницу по умолчанию компонента Daily Message: редактирование сообщений. function listConfiguration($option) { global $database; $database->setQuery("SELECT * FROM mos_joe_dailymessage_conf" ); $rows = $database -> loadObjectList();

40


Бонус

Учебник по созданию компонент Joomla!

PHP Inside #16

Эта функция загружает текущую конфигурацию из базы данных и показывает ее в виде формы, при этом значения могут быть отредактированы администратором. Вызывается соответствующий запрос и результат загружается в объект $rows как список объектов. if ($database -> getErrorNum()) { echo $database -> stderr(); return false; }

Если здесь будет ошибка в запросе, то этот код сообщит эту ошибку и остановит выполнение функции, при этом будет загружена страница без значений конфигурации. HTML_joeDailyMessage::listConfiguration($option,$rows); }

Это вызывает функцию listConfiguration объекта HTML_joeDailyMessage из файла dailymessage.class.php file. function publishMessage( $option, $publish=1 ,$cid ) { global $database, $my; if (!is_array( $cid ) || count( $cid ) < 1) { $action = $publish ? 'publish' : 'unpublish'; echo "<script> alert('Select an item to $action'); window.history.go(-1); </script>n"; exit; }

Когда администратор хочет опубликовать письмо эта функция устанавливает флаг 'published' сообщения в '1.' Первое мы должны определить действительно ли действие является публикацией. Если нет, то выводим ошибку и редиректим пользователя на предыдущую страницу. Строка, где $action устанавливается посредством краткой версии директивы 'if'. Переменная устанавливается в 'publish' если условие выполняется, т.е. $publish равно 1, аналогично если переменная установлена в 'unpublish.' Javascript показывает сообщение и редиректит пользователя. $cids = implode( ',', $cid );

Если мы имеем статьи которые отмечены на публикацию, мы устанавливаем строковую переменную $сids которая включает в себя весь список идентификаторов статей, находящихся в масссиве $id и разделяет переменные посредством запятых. Этот прием позволяет нам просто вставить все это в SQL запрос. $database->setQuery( "UPDATE mos_joe_dailymessage SET published='$publish'" . "nWHERE id IN ($cids)" ); if (!$database->query()) { echo "<script> alert('".$database->getErrorMsg()."'); window.history.go(-1); </script>n";

41


Бонус

Учебник по созданию компонент Joomla! exit();

PHP Inside #16

}

Запрос отсылается в базу данных и любая ошибка будет показана при помощи Javascript. mosRedirect( "index2.php?option=$option" ); }

В заключении администратор редиректится назад к главной странице компонента Daily Message в зоне администрирования. function save($option) { global $database; $row = new joeDailyMessage($database); // bind it to the table if (!$row -> bind($_POST)) { echo "<script> alert('" .$row -> getError() ."'); window.history.go(-1); </script>n"; exit(); } // store it in the db if (!$row -> store()) { echo "<script> alert('" .$row -> getError() ."'); window.history.go(-1); </script>n"; }

exit();

mosRedirect("index2.php?option=$option", "Saved"); }

Эта функция обычно идентична с saveConfiguration(), только она записывает строку в базу с типом joeDailyMessage и редиректит на страницу по умолчанию (редактирование сообщений) "Saved." function del($option, $cid) { global $database; if (!is_array($cid) || count($cid) < 1) { echo "<script> alert('Select an item to delete'); window.history.go(-1); </script>n";

42


Бонус

Учебник по созданию компонент Joomla!

}

Список ежедневных сообщений для удаления обрабатывается следующей функцией. Если нет идентификаторов статей в массиве $cid [или $cid не является массивом], любая ошибка будет показана и администратор будет возвращен на предыдущую страницу. if (count($cid)) { $ids = implode(',', $cid); $database->setQuery("DELETE FROM mos_joe_dailymessage nWHERE id IN ($ids)"); }

Если сообщения, которые нужно удалить были выбраны, вызываем запрос, который удаляет эти сообщения. if (!$database->query()) { echo "<script> alert('" .$database -> getErrorMsg() ."'); window.history.go(-1); </script>n"; } mosRedirect("index2.php?option=$option"); }

Запрос на удаления выполнен и администратор возвращен на страницу по умолчанию. function edit($option, $uid) { global $database; $row = new joeDailyMessage($database); if($uid){ $row -> load($uid[0]); }

Когда администратор выбирает сообщения для редактирования, эта функция будет загружать соответствующее сообщение из базы данных в объект $row. Однако администратор может редактировать только одно сообщение одновременно – первое $uid [0]. HTML_joeDailyMessage::edit($option,$row); }

Функция edit() явлется методом класса HTML_joeDailyMessage, который генерирует соответсвующий вид – HTML страницу. Строка базы данных и имя компонента (com_dailymessage) передаются для того чтобы был сгенерирован соответствующий HTML-код. function listMessages($option) { global $database; $database->setQuery("SELECT * FROM mos_joe_dailymessage ORDER BY id" );

43

PHP Inside #16

exit();


Бонус

Учебник по созданию компонент Joomla!

PHP Inside #16

Эта последняя функция для показа страницы по умолчанию. Если не передается конкретное задание, которое должно быть выполнено выполняется эта функция. При этом показывается список всех сообщений, которые администратор может редактировать, удалять публиковать/делать неопубликованными. Запрос установлен для выборки содержания ежедневного сообщения из таблицы. Он отсортирован по полю id т.к. оно представляет собой автоинкремент, т.е. при добавлении новой записи id увеличивается. Такой способ позволяет легко создать уникальный ключ и позволяет выводить список в порядке создания. $rows = $database -> loadObjectList(); if ($database -> getErrorNum()) {

}

echo $database -> stderr(); return false;

HTML_joeDailyMessage::listMessages($option, $rows); } ?>

Запрос выполняется и если имеет какие либо ошибки, они просто выводятся в странице. Функция listMessages() в классе HTML_joeDailyMessage вызывается для генерации необходимой HTML странички.

admin.dailymessage.html.php defined( '_VALID_MOS' ) or die( 'Direct Access to this location is not allowed.' );

Эта строка кода проверяет действительно ли этот файл вызывается Joomla. class HTML_joeDailyMessage{

Этот класс первоначально вызывается из файла admin.dailymessage.php и обрабатывает основной вывод HTML для зоны администрирования. function edit( $option, &$row ) {

Аргументами этой функции являются название компонента (com_dailymessage) и строка записи из базы данных загружанных в admin.dailymessage.php и показывает форму, где администратор может редактировать сообщение. ?> <form action="index2.php" method="post" name="adminForm" id="adminForm" class="adminForm"> <table border="0" cellpadding="3" cellspacing="0"> <tr> <td>Message: </td> <td><input type="text" size="50" maxsize="100" name="message" value="<?php echo $row->message; ?>" /></td> </tr> <tr> <td>Date: </td> <td><input size="30" name="date" value="<? echo $row->date; ?>"></td> </tr> <tr> <td>Published: </td>

44


Бонус

Учебник по созданию компонент Joomla! <td><? echo mosHTML::yesnoSelectList( "published", "", $row->published ); ? ></td></tr>

PHP Inside #16

Если строка записи базы данных загружена, то форма заполняется данными . Заметьте что поле 'Published' выводится при помощи метода yesnoSelectList класса mosHTML. Класс mosHTML декларирован в файле classes/mambo.php и доступен в любом месте Joomla. Этот метод генерирует выпадающее меню с двумя значениями 'no' и 'yes'. Если значение переменной $row-published равно 0, то будет выставлено значение 'no'. Если значение 1, тогда будет выставлено 'yes'. Это более изящный метод создания административных интерфейсов, нежели чем использовать чекбоксы. </table> <input type="hidden" name="id" value="<? echo $row->id; " ?>" /> <input type="hidden" name="option" value="<?php echo $option; ?>" /> <input type="hidden" name="task" value="" /> </form> <? }

Скрытые поля содержат идентификатор сообщения id, имя компонента, и задачу task. Значение 'task' изначально не установлено, однако яваскрипт заполняет это поле в зависимости от того какая кнопка нажата. function listConfiguration($option, &$rows) {

Эта функция вызывается для показа панели настроек компонента. ?> <form action="index2.php" method="post" name="adminForm"> <table cellpadding="4" cellspacing="0" border="0" width="100%" class="adminlist"> <? $row = $rows[0]; ?> <tr><td>Bold</td><td><? echo mosHTML::yesnoSelectList( "bold", "", $row->bold ); ?></td></tr> <tr><td>Italic</td><td><? echo mosHTML::yesnoSelectList( "italic", "", $row>italic ); ?></td></tr> <tr><td>Underline</td><td><? echo mosHTML::yesnoSelectList( "underline", "", $row->underline ); ?></td></tr> <tr><td>Show Date</td><td><? echo mosHTML::yesnoSelectList( "showdate", "", $row->showdate ); ?></td></tr> </table>

Эта форма заполняется данными из первой записи таблицы mos_joe_dailymessage_conf table [здесь только одна запись]. Метод yesnoSelectList() делает простым управление данными на форме. <input type="hidden" <input type="hidden" <input type="hidden" <input type="hidden" </form> <? }

name="option" value="<?php echo $option; ?>" /> name="task" value="" /> name="configid" value=<? echo $row->configid ?> /> name="act" value="configure" />

45


Бонус

Учебник по созданию компонент Joomla!

PHP Inside #16

Переменные option, task, configid, и act передаются скрытыми полями из формы. Переменная 'configid' выбирает, какая именно запись редактируется, хотя в данном случае у нас только одна запись. Переменная 'act' передает в admin.dailymessage.php данные о том что события произошли на форме редактирования конфигурации, а не сообщений. Это обрабатывается оператором switch который выполняется в начале файла. function listMessages( $option, &$rows ) {

Функция выводит вид по умолчанию - список сообщений. <form action="index2.php" method="post" name="adminForm"> <table cellpadding="4" cellspacing="0" border="0" width="100%" class="adminlist"> <tr> <th width="20"><input type="checkbox" name="toggle" value="" onclick="checkAll(<?php echo count($rows); ?>);" /></th> <th class="title" width="25%">Message</th> <th>Date</th> <th width="25%">Published</th> </tr>

Это заголовки всех колонок, включая мастер чекбокс, который управляет всей колонкой чекбоксов. <?php $k = 0; for($i=0; $i < count( $rows ); $i++) { $row = $rows[$i]; ?>

Начинаем цикл по массиву $rows, чтобы выбрать данные по каждому элементу массива. <tr class="<?php echo "row$k"; ?>">

Меняем название стиля строки в зависимости от тогд является эта строка четной или нечетной. Мы начинаем со строки номер 0 и меняем ее на 1, и наоборот, для того чтобы сделать таблицу удобной для чтения. <td><input type="checkbox" id="cb<?php echo $i;?>" name="cid[]" value="<?php echo $row->id; ?>" onclick="isChecked(this.checked);" /></td>

Показывает чекбокс и осуществляет наполнение данных об идентификаторе данного элемента. Каждый чекбокс id имеет значение cbX, где X это номер позиции элемента в списке. Имена определяются как массивы и значение устанавливается в id для того чтобы найти строку в базе данных. <td><a href="#edit" onclick="return listItemTask('cb<?php echo $i;?>','edit')"><?php echo $row->message; ?></a></td>

Создает ссылки содержащие текст сообщения. Когда происходит щелчок мышью по тексту сообщения ссылка будет автоматически проверять в начале строки чекбокс и после этого Javascript отправляет форму со значением задачи (task) равным 'edit.' <td><? echo mosFormatDate($row->date); ?></td>

46


Бонус

Учебник по созданию компонент Joomla!

Выводит дату сообщения, отформатированную в соответствии с форматом времени функцией mosFormatDate() которая декларирована в файле classes/mambo.php.

PHP Inside #16

<td align="center"> <?php if ($row->published == "1") { echo "<a href="javascript: void(0);" onClick="return listItemTask ('cb$i','unpublish')"><img src="images/publish_g.png" border="0" /></a>"; } else { echo "<a href="javascript: void(0);" onClick="return listItemTask ('cb$i','publish')"><img src="images/publish_x.png" border="0" /></a>"; } ?> </td>

Этот код показывает иконки 'published' или 'unpublished', зависящий от статуса сообщения. Если иконка нажата, форма отправляется и статус меняется на противоположный. <?php $k = 1 - $k; ?>

Изменения переменных, которые описывают цвет строки. Еденица меняется на ноль, ноль на еденицу. </tr> <?php } ?> </table> <input type="hidden" name="option" value="<?php echo $option; ?>" /> <input type="hidden" name="task" value="" /> <input type="hidden" name="boxchecked" value="0" /> </form> <? } }

Скрытые поля option, task, and boxchecked в конце формы. Как 'boxchecked' так и 'task' позже меняются Javascript. Boxchecked используется для того чтобы проверить что элементы списка перед тем как запущено задание.

dailymessage.class.php Опять проверяется что скрипты вызван из Joomla (Mambo). <? defined( '_VALID_MOS' ) or die( 'Direct Access to this location is not allowed.' ); class var var var var

joeDailyMessage extends mosDBTable { $id = null; $message = null; $date = null; $published = null;

Этот класс является расширением mosDBTable class, который предоставляет многие методы, облегчающие работу с записями в таблице данных. Первое, переменная идентична полю в базе данных. По такому пути созданы объекты joeDailyMessage, загруженные в объект желательные аргументы отсылаются в базу данных. function joeDailyMessage(&$db){

47


Этот конструктор класса наследуется у объекта mosDBTable(), при этом имя таблицы можно задавать не жестко, а использовать префикс, который будет заменен на нужный [#_ shorthand для 'mos'] также желательно описать имя поля содержащее первичный ключ. class var var var var var

joeDailyMessageConf extends mosDBTable { $bold = null; $italic = null; $underline = null; $showdate = null; $configid = null;

function joeDailyMessageConf(&$db){ $this->mosDBTable('mos_joe_dailymessage_conf', 'configid', $db); } }

Класс joeDailyMessageConf создается по такому же принципу как и класс joeDailyMessage, основан он на таблце mos_joe_dailymessage_conf и поле 'configid' будет использовано в качестве первичного ключа.

toolbar.dailymessage.php Для всех компонентов, Joomla загружает панель инструментов для Бекенда. Создание этого кода довольно однообразное, поскольку весь функционал довольно четко описан. Код в этом файле зависит от значений переменных 'task' и 'act'. defined( '_VALID_MOS' ) or die( 'Direct Access to this location is not allowed.' ); require_once( $mainframe->getPath( 'toolbar_html' ) );

Первым делом проверьте что файл запускается из Joomla (Mambo) toolbar.dailymessage.html.php, и включите файл, который обрабатывает вывод HTML для этого меню. switch ( $task ) { case 'edit': menuDailyMessage::EDIT_MENU(); break; case 'new': menuDailyMessage::EDIT_MENU(); break; default: switch($act) { case "configure": menuDailyMessage::CONFIGURE_MENU(); break; default: menuDailyMessage::DEFAULT_MENU(); break; }

Бонус

$this->mosDBTable('mos_joe_dailymessage', 'id', $db); } }

PHP Inside #16

Учебник по созданию компонент Joomla!


Бонус

Учебник по созданию компонент Joomla!

Замечание оба значения task - 'new' и 'edit' разделяют это меню. Это не вызывает конфликта потому что меню начинает запускать задачи функционально друг от друга не зависящие. Текущее значения `task` и 'act' описывают какой вид будет показан. Все меню вызваются через методы класса menuDailyMessage, который находится в файле toolbar.dailymessage.html.php.

PHP Inside #16

break; }


Этот файл описывает класс menuDailyMessage, который имеет три метода для трех различных видов. defined( '_VALID_MOS' ) or die( 'Direct Access to this location is not allowed.' );

Конечно проверяем что вызывается все из Joomla. class menuDailyMessage{ function DEFAULT_MENU() { mosMenuBar::startTable(); mosMenuBar::publish('publish'); mosMenuBar::unpublish('unpublish'); mosMenuBar::divider(); mosMenuBar::addNew('new'); mosMenuBar::editList('edit', 'Edit'); mosMenuBar::deleteList( ' ', 'delete', 'Remove' ); mosMenuBar::endTable();

У большинства приведенных функций первый параметр это значение переменной 'task'. Для функции deleteList() первый аргумент необязательный он нужен для того чтобы показывать сообщения типа "вы действительно уверены в том что хотите удалить это". Второй аргумент это уже значение переменной 'task', и последний аргумент это надпись для показа при наведении на пункт меню мышью – установка свойства изображения 'alt'. Эта функция доступна для других кнопок. Функции startTable() и endTable() выводят начало и конец кода таблицы, для того чтобы Javascript мог нарисовать панель. Функция divider() рисует вертикальный разделитель для визуального разделения кнопок. function EDIT_MENU() { mosMenuBar::startTable(); mosMenuBar::back(); mosMenuBar::spacer(); mosMenuBar::save('save'); mosMenuBar::endTable();

Функция back() просто показывает иконку 'cancel' icon с текстом 'back.' Когда нажимаешь на эту кнопку то возвращаешься к предыдущей странице. Функция spacer() рисует некоторое пустое пространство между иконками. } function CONFIGURE_MENU() { mosMenuBar::startTable(); mosMenuBar::save('save'); mosMenuBar::endTable(); } } ?>

Бонус

toolbar.dailymessage.html.php

PHP Inside #16

Учебник по созданию компонент Joomla!


Файлы install.dailymessage.php и uninstall.dailymessage.php предназначены для вывода дополнительных сообщений когда компонент инсталлируется и деинсталлируется. Компонент Daily Message не имеет каких либо дополнительных сообщений. install.dailymessage.php: <?php function com_install() { } ?> uninstall.dailymessage.php: <? function com_uninstall() { } ?>

dailymessage.xml <?xml version="1.0" ?>

Этот тег описывает версию XML используемую в этом документе. Большинство XML файлов написаны в версии 1.0. Вы можете прочитать больше об XML на www.w3c.org. <mosinstall type="component">

Тег mosinstall говорит Joomla что за расширение будет установлено. Этот код инсталлируется как компонент, однако могут еще устанавливаться модули, шаблоны, мамботы. <name>DailyMessage</name> <creationDate>06/03/2004</creationDate> <author>Joseph LeBlanc</author> <copyright>This component in released under the GNU/GPL License</copyright> <authorEmail> contact@jlleblanc.com</authorEmail> <authorUrl>www.jlleblanc.com</authorUrl> <version>1.0</version>

Эти теги рассказывают об имени компонента, даты его создания, авторе, копирайтах, e-mail автора и его вебсайте, а также версии компонента. Эта информация в дальнейшем будет отображаться в Бекенде. Имя компонент используется для создания директории с именем com_[имя компонента]. Оно не должно содержать пробелов и служебных символов.

Бонус

Install и uninstall

PHP Inside #16

Учебник по созданию компонент Joomla!


<files>

Бонус

Учебник по созданию компонент Joomla!

</files>

Тег <files> открывает список файлов, которые будут скопированы во Фронтенд, имя файлов пишется в теге <filename> . В этом компоненте используется только один файл. <install> <queries> <query> DROP TABLE IF EXISTS `# __joe_dailymessage`; </query> <query> CREATE TABLE `# __joe_dailymessage` ( `id` INT NOT NULL AUTO_INCREMENT, `message` TEXT NOT NULL, `date` DATETIME NOT NULL, `published` TINYINT(1) NOT NULL, PRIMARY KEY (`id`) ) </query> <query> DROP TABLE IF EXISTS `# __joe_dailymessage_conf`; </query> <query> CREATE TABLE `# __joe_dailymessage_conf` ( `bold` TINYINT(1) NOT NULL, `italic` TINYINT(1) NOT NULL, `underline` TINYINT(1) NOT NULL, `showdate` TINYINT(1) NOT NULL, `configid` TINYINT(4) NOT NULL ) </query> <query> INSERT INTO `# __joe_dailymessage_conf`(bold, italic, underline, showdate,

PHP Inside #16

<filename>dailymessage.php</filename>


configid) values(0, 0, 0 , 1, 1);

Бонус

Учебник по созданию компонент Joomla!

</queries> </install>

Инсталляционные запросы, которые устанавливают таблицы баз данных, для компонента открываются посредством тега <query>, которые ограничиваются тегами <install>. Префикс '#_' в запросах будет замещен в на префикс текущей базы данных Joomla [обычно 'jos']. Две таблицы созданы: одна для сообщений, а вторая для хранения информации о конфигурации. Также, первоначальные значения вставляются в конфигурационную таблицу. <uninstall> <queries> <query> DROP TABLE IF EXISTS `# __joe_dailymessage`; </query> <query> DROP TABLE IF EXISTS `# __joe_dailymessage_conf`; </query> </queries> </uninstall>

Подобные инсталляционным теги для анинсталляции - <uninstall>, ограничивают запросы, которые необходимы, для удаления таблиц созданных при установке компонента. Эти запросы будут исполненты только когда администратор удалит компонент. <installfile> <filename>install.dailymessage.php</filename> </installfile> <uninstallfile> <filename>uninstall.dailymessage.php</filename> </uninstallfile>

Теги <installfile> и <uninstallfile> содержат имена файлов, которые небоходимы для произведения дополнительных действий при инсталляции и анинсталляции компонента. Они копируются в папку administrator/com_dailymessage. <administration> <menu>Daily Message</menu> <submenu> <menu act="all">Edit Messages</menu>

PHP Inside #16

</query>


Бонус

Учебник по созданию компонент Joomla!

</submenu>

Тег <administration> содержит все что относится к зоне администрирования – Бекенду. Тег <menu> содержит заголовок, который будет помещен в качестве элемента меню в главном меню в Бекенде под меню 'Components.' Тег <submenu> содержит имена элементов меню, которые будут появлятся при выборе в главном меню в зоне администрирования 'Daily Message' под меню 'Components'. Свойство 'act' будет использовано для передачи информации Joomla о выбранном сабменю. <files> <filename>admin.dailymessage.php</filename> <filename>admin.dailymessage.html.php</filename> <filename>dailymessage.class.php</filename> <filename>toolbar.dailymessage.php</filename> <filename>toolbar.dailymessage.html.php</filename> </files>

Теги <files> внутри тега <administration> описывают какие файлы будут скопированы в Бекенд - папку administration/com_dailymessage. </administration> </mosinstall>

PHP Inside #16

<menu act="configure">Configure</menu>


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.