Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

PHP5_nachinayushim

.pdf
Скачиваний:
29
Добавлен:
20.03.2015
Размер:
26.79 Mб
Скачать

17

Учебный пример: диспетчер протоколирования на PHP

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

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

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

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

634 Глава 17

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

Итак, приступим к изучению учебного примера.

Почему именно диспетчер протоколирования?

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

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

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

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

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

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

Учебный пример: диспетчер протоколирования на PHP 635

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

Предположим, решение принято: требуется приложение, принимающее инфор+ мацию от различных Web+сайтов, протоколирующее, а затем трансформирующее ее

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

Часть учебного примера представляет собой краткое введение в систему Smarty, так как некоторые из читателей, возможно, не сталкивались с ней раньше. Сущест+ венно облегчает тестирование приложения использование пакета PHPUnit, поэтому

вглаве есть также небольшой раздел, посвященный этому продукту. Читатели, имею+ щие хорошие практические навыки использования Smarty и PHPUnit, могут смело пропустить эти разделы.

Smarty

Почему в этой книге рассматривается Smarty? Тому, кто работал над Web+прило+ жениями как Web+дизайнер, либо как программист, возможно, приходилось модифи+ цировать свой участок кода всякий раз после того, как другой участник проекта изме+ нял свой. Например, разработчик получает HTML+страницы для определенного сайта и в течение недели добавляет PHP+код на уровне представления, чтобы пользователи могли выполнять поиск в таблицах базы данных или отправлять информацию (в зави+ симости от поставленных перед сайтом задач).

Неделю спустя клиент решает изменить внешний вид сайта. Дизайнер страниц раздражен, но переделывает дизайн согласно новым требованиям. Затем он уходит в недельный отпуск, оставляя программисту исправлять ошибки красивого, но со+ вершенно не работающего Web+сайта. Так как весь HTML+код был модифицирован, ни один блок встроенного в Web+страницы PHP+кода не работает так, как должен, по+ этому остается только просматривать код строка за строкой и восстанавливать рабо+ тоспособность приложения.

Гораздо лучше было бы отделить логику представления от бизнес+логики. И здесь система Smarty оказывается весьма полезной.

Установка Smarty

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

Ниже описаны этапы установки Smarty.

636Глава 17

1.Загрузите систему шаблонов Smarty с Web+сайта http://smarty.php.net.

2.Распакуйте архив в любой каталог.

3.Убедитесь, что PHP может использовать классы, предоставляемые Smarty. Эти классы расположены в каталоге libs инсталляции Smarty. Добавьте абсолют+ ный путь к этому каталогу в переменную include_path в файле php.ini:

include_path = ".;c:\php5\includes;c:\php5\pear;C:\Program Files\ Smarty-2.6.2\libs"

4.Естественно, Smarty+классы можно рассматривать как любые другие PHP+ классы и подключать их по отдельности в .php+файлы, например:

require('C:\Program Files\Smarty-2.6.2\libs\Smarty.class.php'); $smarty = new Smarty;

Используя Smarty, необходимо учитывать некоторые обстоятельства. Для работы Smarty требуется наличие четырех каталогов, которые (по умолчанию) называются templates, templates_c, configs и cache. Эти имена можно явно определить в фай+ ле Smarty.class.php ++++++ каждое из них содержится в соответствующей переменной: $template_dir, $compile_dir, $config_dir, $cache_dir. Убедитесь, что Web+ сервер имеет права на запись в каталог $compile_dir (templates_c по умолчанию).

Документация рекомендует создавать отдельную группу таких каталогов для каждого приложения, использующего Smarty.

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

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

Практика Использование Smarty

В этом примере создается шаблон, позволяющий представить полученную из MySQL+таблицы информацию в виде аккуратно отформатированного списка. Для это+ го создается один .php+файл, ответственный за создание подключения к базе данных, выполнение запросов и возвращение результатов в Smarty+шаблон. Затем шаблон форматирует информацию, полученную данным PHP+сценарием, с помощью одной из встроенных в Smarty функций.

1.Сохраните следующий код в файле с именем index.php. Для работы сценария необходим доступ к таблице user, которая упоминалась в главах 11++++13, либо можно создать отдельную таблицу специально для этого сценария:

<?php

require 'Smarty.class.php'; $smarty = new Smarty;

$hostname = "localhost"; $db_user = "phpuser"; $db_pass = "phppass"; $db_name = "sample_db";

// подключение к базе данных

$conn = mysql_connect($hostname, $db_user, $db_pass);

if (!$conn){

die ("Не удалось подключиться к базе данных!");

Учебный пример: диспетчер протоколирования на PHP 637

}

mysql_select_db($db_name);

$sql = "SELECT DISTINCT userposition FROM user";

$res = mysql_query($sql); $results = array(); $i=0;

while ($pos=mysql_fetch_row($res)) { $results[$i++] = $pos[0];

}

$smarty->assign('results', $results); $smarty->display('index.tpl');

?>

2.Теперь создайте новый файл, назовите его index.tpl и сохраните в нем сле+ дующий код:

<HTML>

<BODY>

<B>Перечень позиций игроков: </B><BR><BR> {section name=position loop=$results} {$results[position]}<BR>

{/section}

</BODY>

</HTML>

3.Вызовите в Web+браузере страницу index.php; результат должен быть похож на рис. 17.1.

Рис. 17.1.

Как это работает

PHP+сценарий начинается с подключения класса Smarty и создания нового Smarty+объекта:

<?php

require 'Smarty.class.php'; $smarty = new Smarty;

638Глава 17

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

$hostname = "localhost"; $db_user = "phpuser"; $db_pass = "phppass"; $db_name = "sample_db";

// подключение к базе данных

$conn = mysql_connect($hostname, $db_user, $db_pass);

if (!$conn){

die ("Не удалось подключиться к базе данных!");

}

mysql_select_db($db_name);

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

$sql = "SELECT DISTINCT userposition FROM user"; $res = mysql_query($sql);

$results = array(); $i=0;

while ($pos=mysql_fetch_row($res)) { $results[$i++] = $pos[0];

}

Весьма интересна завершающая часть сценария. Результаты присваиваются пере+ менной шаблона (в данном случае она называется results), а затем отображается шаблон (index.tpl):

$smarty->assign('results', $results); $smarty->display('index.tpl');

?>

Это конец PHP+сценария, поэтому теперь можно перейти собственно к шаблону, файлу index.tpl. Код шаблона начинается со стандартных HTML+тегов и краткого поясняющего текста:

<HTML>

<BODY>

<B>Перечень позиций игроков: </B><BR><BR>

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

{section name=position loop=$results} {$results[position]}<BR>

{/section}

Код шаблона завершается закрывающими HTML+тегами:

</BODY>

</HTML>

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

Учебный пример: диспетчер протоколирования на PHP 639

<?php /* Smarty version 2.6.2, created on 2004-04-12 21:22:51 compiled from index.tpl */ ?>

<HTML>

<BODY>

<B>Перечень позиций игроков: </B><BR><BR> <?php if (isset($this->_sections['position'])) unset($this->_sections['position']);

$this->_sections['position']['name'] = 'position'; $this->_sections['position']['loop'] = is_array($_loop=$this- >_tpl_vars['results']) ? count($_loop) : max(0, (int)$_loop); unset($_loop); $this->_sections['position']['show'] = true; $this->_sections['position']['max'] = $this->_sections['position']['loop']; $this->_sections['position']['step'] = 1; $this->_sections['position']['start'] = $this->_sections['position']['step'] > 0 ? 0 : $this->_sections['position']['loop']-1;

if ($this->_sections['position']['show']) { $this->_sections['position']['total'] = $this->_sections['position']['loop'];

if ($this->_sections['position']['total'] == 0) $this->_sections['position']['show'] = false;

} else

$this->_sections['position']['total'] = 0; if ($this->_sections['position']['show']):

for ($this->_sections['position']['index'] = $this->_sections['position']['start'], $this->_sections['position']['iteration'] = 1; $this->_sections['position']['iteration'] <= $this->_sections['position']['total']; $this->_sections['position']['index'] += $this->_sections['position']['step'], $this->_sections['position']['iteration']++): $this->_sections['position']['rownum'] = $this->_sections['position']['iteration']; $this->_sections['position']['index_prev'] = $this->_sections['position']['index'] - $this->_sections['position']['step']; $this->_sections['position']['index_next'] = $this->_sections['position']['index'] + $this->_sections['position']['step']; $this->_sections['position']['first'] = ($this->_sections['position']['iteration'] == 1); $this->_sections['position']['last'] = ($this->_sections['position']['iteration'] == $this- >_sections['position']['total']);

?> <?php echo $this->_tpl_vars['results'][$this->_sections['position'] ['index']]; ?>

<BR>

<?php endfor; endif; ?> </BODY>

</HTML>

Этим примером завершается вводный материал по системе шаблонов Smarty. Бо+ лее подробная информация о Smarty представлена на странице документации проекта http://smarty.php.net/manual/ru/. Теперь, имея достаточное представление о Smarty, вы сможете разобраться с особенностями использования системы в описы+ ваемом учебном примере.

640 Глава 17

PHPUnit

Получить самую свежую версию пакета PHPUnit можно по адресу http://sourceforge/net/projects/phpunit/. После загрузки пакета и его раз+ мещения в каком+либо каталоге необходимо убедиться, что файл phpunit.php досту+ пен PHP, поэтому путь к выбранному каталогу нужно указать в параметре include_path в php.ini. Если изменять php.ini нежелательно, то можно установить пакет в тот же каталог, в котором расположен тестируемый код.

Рассмотрим практическое применение пакета PHPUnit.

Работа с PHPUnit

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

Во+первых, PHPUnit предоставляет функции, которые можно использовать для проверки определенных условий. Например, требуется определить, какое значение возвращает заданный метод, true или false. Чтобы это сделать, необходимо сфор+ мулировать утверждение об этом методе и проверить, является ли оно верным. Для проверки правильности утверждений PHPUnit предоставляет метод assert():

function assert($boolean, $message=0) { if (! $boolean)

$this->fail($message);

}

Например, чтобы для проверки открытия соединения с базой данных, может ис+ пользоваться следующая функция:

function isOpen($db){ If ($db == null){

return false;

}

return true;

}

Зная, что функция isOpen() возвращает булево значение, можно использовать функцию assert() так:

$this->assert(isOpen($db)), "Соединение не создано!");

Функция assert() не единственная используемая функция в PHPUnit, а кроме того, существуют разные варианты ее применения (хотя пока это все, что требуется знать об использовании методов PHPUnit).

Как же добраться до этих методов, ведь очевидно, что должен существовать про+ инициализированный PHPUnit+объект? Здесь проявляется вся ценность PHPUnit. Использование этого пакета позволяет унифицированным и интуитивно понятным способом запускать за один раз все тесты. Ниже описана последовательность тести+ рования кода с помощью PHPUnit.

1.Для формирования контекста, в котором можно будет запускать тесты, необхо+ димо создать класс, расширяющий класс TestCase пакета PHPUnit:

class MultiLogTestSuite extends TestCase

Учебный пример: диспетчер протоколирования на PHP 641

2.Специфический контекст, в котором запускаются тесты, создается путем опре+ деления метода setUp() ++++++ буквально создается среда, в которой будет тести+ роваться код. В рассматриваемом учебном примере в методе setUp(), который впоследствии будет использоваться для запуска тестов, создается два UserLog+ объекта и определяются значения для них. (В действии это будет показано в конце главы в разделе ‘‘Тестирование приложения’’.)

3.Создаются необходимые тесты с именами, которые следуют описанному ниже соглашению:

testMeaningfulName()

testAnotherMeaningfulName()

4. Создается новый комплект тестов:

$suiteOfTests = new TestSuite;

5. В комплект добавляются тесты:

$suiteOfTests->addTest(New MultiLogTestSuite("testMeaningfulName");

$suiteOfTests->addTest(New MultiLogTestSuite("testAnotherMeaningfulName");

6.Создается новый TestRunner+объект. В классе TestRunner просто определена функция run() для запуска всех созданных тестов и вывода отчета о тестировании.

$testRunner = new TestRunner();

7. Наконец, вызывается метод run() (определенный на предыдущем этапе):

$testRunner->run($suiteOfTests);

Это все. Теперь PHPUnit добросовестно выполнит все тесты в комплекте и выве+ дет отчет обо всех результатах (все эти этапы подробно описаны далее в настоящей главе). В этом разделе приведено достаточно сведений для начала работы. После прочтения главы читателю рекомендуется самостоятельно поэкспериментировать с классом PHPUnit, потому что само по себе умение использовать блочные тесты име+ ет определенную ценность.

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

Проектирование диспетчера протоколирования

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

Чего вы пытаетесь добиться?

Как вы собираетесь это сделать?

Для любого разрабатываемого приложения стоит обдумать несколько различных аспектов. Например, как будет реализована проверка и обработка ошибок, и нужно ли реализовать какую+либо систему безопасности?

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

642 Глава 17

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

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

Впримере используются современные методики программирования, а именно ++++++

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

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

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

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

сбазами данных (аналогичный классу DataManager, который рассматривался в главе 13).

Вкачестве СУБД будет использоваться SQLite, а необходимые базы данных будут созданы с помощью специальных сценариев. Знание структуры базы данных предпо+ лагаемого приложения подталкивает разработчика обдумывать тип необходимого ко+ да, поэтому таблицы баз данных представляют собой хорошую отправную точку для начала разработки приложения.

База данных sitelogs.db

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]