Изучаем PHP, Часть 1: Страница регистрации, формы и взаимодействие с базой данных

developerWorks

Уровень сложности: средний

Никлас Чейз, Независимый автор, Backstop Media
Тайлер Андерсон, Независимый автор, Stexar Corp.

11.01.2007

Перейти к учебному пособию

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

В этом учебном пособии

Если вам раньше не приходилось сталкиваться с PHP, но вы хотите научиться писать Web-ориентированные приложения, то начните изучение с данного пособия, которое является первой частью в серии из трех учебных пособий. PHP является языком скриптового типа, он очень прост в изучении, но с его помощью можно строить достаточно сложные приложения с широкой функциональностью. Это учебное пособие научит вас писать несложные приложения, строить PHP-страницы с использованием HTML-форм и получать доступ к базам данных.


Требуемый опыт

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


Системные требования

Вам необходимо иметь Web-сервер, PHP и базу данных. Если у вас есть доступ к сети, в которой установлен сервер PHP V5 с доступом к базе данных MySQL, то вы можете им воспользоваться. В противном случае загрузите и установите следующие пакеты:

Web сервер
Вне зависимости от того, работаете ли вы под Windows®, Linux® или Mac OS X, вы имеете возможность использовать Apache Web-сервер. Вы можете работать с версиями Apache V1.3 или 2.0, но примеры в этом пособии ориентированы на версию V2.0. Если вы работаете под Windows, вы можете также использовать Internet Information Services, встроенный в Windows Web-сервер.
PHP
Вам необходим дистрибутив PHP. Во время написания этого пособия в ходу были версии PHP V4 и V5, в своих примерах мы ориентировались на V5. Различия в версиях PHP несущественны для этой части пособия, но в двух последующих частях они станут заметны.
MySQL
Одной из тем этого учебника является получение доступа к базе данных. Соответственно, вам нужна та или иная база данных. Мы разбираем доступ к базе MySQL, поскольку именно эта база данных как правило используется вместе с PHP.


Форматы

html, pdf

Изучаем PHP: Часть 1. Страница регистрации, формы и взаимодействие с базой данных

developerWorks
Страница 1 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Уровень сложности: средний

Николас Чейз, автор, Studio B
Тайлер Андерсон, Независимый автор, Stexar Corp.

12.01.2007

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

Прежде чем начать

Об этом учебном пособии

Цель этого учебного пособия -- научить вас писать на языке PHP приложения, поддерживающие workflow. Мы начнем с того, что научимся регистрироваться с определенным именем пользователя и паролем, загружать файлы на сервер из браузера, просматривать загруженные файлы. В дальнейшем для пользователей с административными полномочиями мы разработаем специальную процедуру одобрения документов для публичного доступа. В последующих двух частях этой серии (Часть 2 и Часть 3) мы рассмотрим HTTP-аутентификацию, использование потоков данных, обработку исключений и другие важные вопросы.

В этом пособии рассматриваются следующие темы:

  • Создание базовой страницы
  • Переменные, циклы и условные операторы if-then
  • Массивы и функции
  • Доступ к базам данных
  • Включение файлов с помощью оператора include


В начало


Для кого написано это пособие?

Если вам раньше не приходилось сталкиваться с PHP, но вы хотите научиться писать Web-ориентированные приложения, то начните изучение с данного пособия, которое является первой частью в серии из трех учебных пособий. PHP является языком скриптового типа, он очень прост в изучении, но с его помощью можно строить достаточно сложные приложения с широкой функциональностью. Это учебное пособие научит вас писать несложные приложения, строить PHP-страницы с использованием HTML-форм и получать доступ к базам данных.



В начало


Требования к подготовке

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



В начало


Инструментальные средства

Вам необходимо иметь Web-сервер, PHP и базу данных. Если у вас есть доступ к сети, в которой установлен сервер PHP V5 с доступом к базе данных MySQL, то вы можете им воспользоваться. В противном случае загрузите и установите следующие пакеты:

Web-сервер
Вне зависимости от того, работаете ли вы под Windows, Linux или Mac OS X, вы имеете возможность использовать Apache Web-сервер. Вы можете работать с версиями Apache V1.3 или 2.0, но примеры в этом пособии ориентированы на версию V2.0. Если вы работаете под Windows, вы можете также использовать Internet Information Services, встроенный в Windows Web-сервер.
PHP V5
Вам необходим дистрибутив PHP. Во время написания этого пособия в ходу были версии PHP V4 и V5, в своих примерах мы ориентировались на V5. Различия в версиях PHP несущественны для этой части пособия, но в двух последующих частях они станут заметны.
MySQL
Одной из тем этого учебника является получение доступа к базе данных. Соответственно, вам нужна та или иная база данных. Мы разбираем доступ к базе MySQL, поскольку именно эта база данных как правило используется вместе с PHP.


В начало



Страница 1 из 10  На предыдущую страницу

Изучаем PHP: Часть 1. Страница регистрации, формы и взаимодействие с базой данных

developerWorks
На предыдущую страницу Страница 2 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Базовый синтаксис PHP

Простая PHP-страница

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

Начните с создания простой PHP-страницы:

<html>
<title>Workflow Registration</title>
<body>
<p>You entered:</p>
<p><?php echo "Some Data"; ?></p>
</body>
</html>

В этом примере мы видим простую HTML-страницу c одной PHP-секцией, которая выделена жирным шрифтом. Когда Web-сервер встречает последовательность символов <?php, то это служит для него сигналом, что далее следуют команды, а не просто текст, который надо послать браузеру. Сервер продолжает выполнять команды PHP, каким образом -- мы обсудим чуть ниже, до тех пор, пока не дойдет до признака конца секции, а именно символов ?>.

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

<html>
<title>Workflow Registration</title>
<body>
<p>You entered:</p>
<p>Some Data</p>
</body>
</html>

Чтобы посмотреть, как это работает, сохраните текст PHP-страницы в файл с именем registration_action.php и поместите файл в корневую папку с документами для вашего сервера. Для Apache это будет папка, похожая на /var/www/html, для Internet Information Services это будет папка, похожая на C:\Inetpub\wwwroot.

Откройте браузер с адресом http://localhost/registration_action.php. Результат работы вашего браузера будет подобен тому, что вы можете видеть ниже на Рисунке 1.


Рисунок 1. Результат работы команды echo
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

Таким образом, вы написали свою первую страницу с использованием PHP.



В начало


Переменные

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

<html>
<title>Workflow Registration</title>
<body>
<p>You entered:</p>

<?php
$username = "tyler";
$password = "mypassword";

echo "<p>Username = " . $username . "</p>";
echo "<p>Password = " . $password . "</p>";
?>

</body>
</html>

Сохраните файл и обновите страницу вашего браузера. То, что вы увидите, будет похоже на Рисунок 2.


Рисунок 2. Страница браузера после обновления
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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

Еще одно замечание: имена переменных в PHP чувствительны к регистру, то есть, $UserName и $username -- это две совершенно различные переменные.

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

Прежде чем двинуться дальше, рассмотрим специальный тип переменных.



В начало


Константы

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

                    <?php
define("PAGE_TITLE", "Workflow Registration");
?>


<html>
<title><?php echo PAGE_TITLE ?></title>
<body>
<p>You entered:</p>
...

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

Заметьте, что в этом операторе задается пара: имя константы и ее значение. Если вы попытаетесь изменить значение константы после того, как оно было определено, то получите сообщение об ошибке.

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



В начало


Упрощенный вывод

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

Для вывода данных в PHP существует оператор вывода, вы можете передать данные при помощи конструкции <?= ?> :

<?php
define("PAGE_TITLE", "Workflow Registration");
?>
<html>
<title><?= PAGE_TITLE ?></title>
<body>
<p>You entered:</p>
...

Обратите внимание, что за оператором вывода не следует точка с запятой.

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



В начало



На предыдущую страницу Страница 2 из 10  На предыдущую страницу


Изучаем PHP: Часть 1. Страница регистрации, формы и взаимодействие с базой данных

developerWorks
На предыдущую страницу Страница 3 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Формы и PHP

Создание и использование форм в PHP

Язык PHP изначально разрабатывался как язык для Web-программирования. Конечно, вы можете запустить PHP из командной строки, но фактически случаи использования PHP вне Web-приложений очень редки. Как правило PHP используется вместе с HTML-формами.

Вы создаете форму с использованием HTML, затем пользователь вводит данные в эту форму и инициирует ее передачу, браузер пересылает данные серверу в форме массива.

В этом разделе мы определим, что такое массив данных, рассмотрим способы работы с данными в формах. Вы также познакомитесь с конструкциями, управляющими ходом выполнения PHP-скрипта, такими как циклы и условные операторы if-then.



В начало


Создание форм в HTML

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

<html>
<head><title>Workflow System</title></head>
<body>
<h1>Register for an Account:</h1>
<form action="registration_action.php" method="GET">

Username: <input type="text" name="name" /><br />
Email: <input type="text" name="email" /><br />
Password: <input type="password" name="pword" /><br />
<input type="submit" value="GO" />
</form>

</body>
</html>

Таким образом, мы имеем простую форму, которая содержится внутри HTML-тега form, в ней есть текстовое поле для ввода пароля и клавиша для отсылки данных. Сохраните этот текст в файл с именем registration.php и поместите файл в корневую папку с документами для вашего сервера, затем откройте браузер с адресом http://localhost/registration.php. Результат работы вашего браузера будет похож на то, что можно видеть ниже, на Рисунке 3.


Рисунок 3. Форма для регистрации
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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



В начало


Передача данных из формы

Рассмотрим подробнее формат элемента form нашей формы.

<form action="registration_action.php" method="GET">

В этом элементе определено два параметра. Первый, action, сообщает браузеру куда посылать информацию. В нашем случае ссылка ведет к тому файлу, который мы создали ранее, registration_action.php. Второй параметр, method, сообщает браузеру как передавать данные.

Посмотрим, как это работает. Введите какие-либо данные и нажмите кнопку GO. Вы увидите нечто похожее на Рисунок 4.


Рисунок 4. Вывод данных, введенных в форму
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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

http://localhost/registration_action.php?name=roadnick&email=
ibmquestions%40nicholaschase.com&pword=supersecretpassword

Заметьте, что каждому элементу формы в URL соответствует пара имя-значение и эти пары разделены амперсандами. Такой вид URL обусловлен тем, что мы использовали в качестве метода передачи GET. Далее мы рассмотрим использование метода POST, но прежде научимся принимать данные из формы в PHP-страницу.



В начало


Прием данных из формы

Рассмотрим, каким образом данные, введенные в HTML-форму, можно сделать доступными в принимающей PHP-странице. Внесите следующие изменения в файл registration_action.php:

...
<body>
<p>You entered:</p>

<?php
$username = $_GET['name'];
$password = $_GET['pword'];

echo "<p>Username = " . $username . "</p>";
echo "<p>Password = " . $password . "</p>";
?>

</body>
</html>

Значения переменных будут теперь выбираться по имени из массива $_GET. Чуть ниже мы поговорим о массивах подробнее, а сейчас попробуйте обновить окошко браузера, вы увидите, что введенные нами данные появились на своих местах, как показано на Рисунке 5.


Рисунок 5. Введенные данные отображаются в браузере
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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



В начало


Массивы

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

$formnames = array("name", "email", "pword");
echo "0=".$formnames[0]."<br />";
echo "1=".$formnames[1]."<br />";
echo "2=".$formnames[2]."<br />";

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

Результат работы этого скрипта будет такой:

0=name<br />
1=email<br />
2=pword<br />

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

В тот момент, когда вы отсылаете свою форму, вы по существу дела создаете ассоциативный массив:

$_GET = array("name" => "roadnick", 
"email" => "ibmquestions@nicholaschase.com",
"pword" => "supersecretpassword");

Именно этот факт позволяет вам впоследствии выделить отдельные значения, например, $_GET["name"], имена переменных в этом случае играют роль ключей.



В начало


Получение информации о массиве

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

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

<body>
<p>You entered:</p>

<?php
$form_names = array_keys($_GET);
$form_values = array_values($_GET);

echo "<p>" . $form_names[0] . " = " . $form_values[0] . "</p>";
echo "<p>" . $form_names[1] . " = " . $form_values[1] . "</p>";
echo "<p>" . $form_names[2] . " = " . $form_values[2] . "</p>";
?>

</body>
</html>

Функции array_keys() и array_values() возвращают обыкновенные массивы, к элементам которых можно обращаться при помощи числовых индексов, как показано ниже на Рисунке 6.


Рисунок 6. Извлечение данных из массива с использованием числовых индексов
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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



В начало


Использование цикла for-next

Задача последовательный обработки некоторого числа однотипных значений возникает очень часто. Для ее выполнения применяется такая конструкция как цикл. Вот один из примеров цикла:

for ($i = 0; $i < 10; $i++) {
echo $i . " ";
}

Результат его работы будет следующим:

0 1 2 3 4 5 6 7 8 9

Рассмотрим синтаксис выражения for. Первоначально PHP приписывает значение 0 переменной $i. Цикл продолжается до тех пор, пока значение переменной $i остается меньше 10, на каждом шаге цикла значение переменной $i увеличивается на единицу.

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

<body>
<p>You entered:</p>

<?php
$form_names = array_keys($_GET);
$form_values = array_values($_GET);

for ($i = 0; $i < sizeof($_GET); $i++) {
echo "<p>".$form_names[$i]." = " . $form_values[$i] . "</p>";
}
?>

</body>
</html>

Функция sizeof() возвращает число элементов массива, в нашем случае массива $_GET. Вы можете использовать возвращаемое этой функцией значение в условном операторе, который останавливает выполнение цикла, результат работы цикла показан ниже на Рисунке 7.


Рисунок 7. Использование функции sizeof для остановки цикла
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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



В начало


Использование цикла foreach

Ассоциированные массивы настолько часто используются в PHP, что для работы с ними была создана специальная конструкция, которая позволяет получать доступ к данным, минуя ключи. Вместо этого вы можете использовать цикл foreach, он позволяет манипулировать с данными массива напрямую. Давайте рассмотрим пример кода:

...
<?php
foreach ($_GET as $value) {
echo "<p>" . $value . "</p>";
}

?>

На первом шаге цикла будет извлечено первое значение из массива $_GET, это значение будет приписано переменной $value, затем это значение выводится командой echo. Затем цикл повторяется, переменной $value приписывается второе значение и так далее, цикл работает до тех пор, пока в массиве $_GET еще остаются необработанные элементы. Результат работы этого цикла будет следующим:

<p>roadnick</p>
<p>ibmquestions@nicholaschase.com</p>
<p>supersecretpassword</p>

Эта конструкция позволяет извлекать не только значения элементов ассоциированного массива, но и ключи:

...
<?php

foreach ($_GET as $key=>$value) {
echo "<p>".$key." = " . $value . "</p>";
}
?<
...

Такой вариант кода приведет нас к результату, который мы уже видели выше:


Рисунок 8. Исходный вариант вывода
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.



В начало


Повторяющиеся значения в формах

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

...
Username: <input type="text" name="name" /><br />
Email: <input type="text" name="email" /><br />
Password: <input type="password" name="pword[]" /><br />
Password (again): <input type="password" name="pword[]" /><br />
<input type="submit" value="GO" />
...

Заметьте, что имя поля pword несколько изменилось. Поскольку это поле должно содержать несколько значений, то оно само стало массивом. Таким образом, массив передаваемых данных для этой формы будет содержать в качестве одного из своих элементов другой массив. Когда вы нажмете кнопку отправки формы, то в поле адреса возникнет следующий URL:

http://localhost/registration_action.php?name=roadnick&email=ibmquestions%40nicholas
chase.com&pword[]=supersecretpassword&pword[]=supersecretpassword

При этом в процессе обработки будет создан массив:

$passwords = array("supersecretpassword", "supersecretpassword");
$_POST = array("name"=>"roadnick",
"email"=>"ibmquestions@nicholaschase.com",
"pword"=>$passwords);

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

...
foreach ($_GET as $key=>$value) {
echo "<p>".$key." = " . $value . "</p>";
}

$passwords = $_GET["pword"];
echo "First password = ".$passwords[0];
echo "<br />";
echo "Second password = ".$passwords[1];
...

Если вы отошлете форму (не забудьте обновить страницу), то можете заметить некоторые изменения, как показано ниже на Рисунке 9.


Рисунок 9. Новый вариант вывода
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

Обратите внимание, в поле, где раньше отображался пароль, появилось слово Array, элементы самого массива отображены ниже.



В начало


Отличия методов GET и POST

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

Согласно принятым стандартам технологии Web-программирования, метод GET не следует использовать для передачи данных в скрипты, которые могут иметь "побочные эффекты", а фактически любой скрипт, в котором происходит обработка данных, обладает этим свойством. До сих пор мы только отображали введенные данные, обработки не происходило, но если мы хотим добавить запись в базу данных, то это уже обработка, которая может иметь побочный эффект.

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

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



В начало


Использование метода POST

Внешне использование метода POST мало отличается от использования метода GET. Сначала внесем изменения в файл registration.php:

...
<h1>Register for an Account:</h1>
<form action="registration_action.php" method="POST">

Username: <input type="text" name="name" /><br />
...

При отсылке формы вы увидите, что содержание URL-адреса изменилось:

http://localhost/registration_action.php

Изменения нужны и в файле registration_action.php, для приема данных вместо массива $_GET будет использоваться массив $_POST:

...
<body>
<p>You entered:</p>

<?php

foreach ($_POST as $key=>$value) {
echo "<p>".$key." = " . $value . "</p>";
}

$passwords = $_POST["pword"];
echo "First password = ".$passwords[0];
echo "<br />";
echo "Second password = ".$passwords[1];
?>

</body>
</html>

Дальнейшая работа с массивом $_POST происходит точно так же, как и с массивом $_GET.



В начало


Контроль данных: условный оператор if-then

Выше мы попросили пользователя ввести пароль дважды. Теперь посмотрим, как можно проверить, не было ли сделано ошибки при вводе пароля. Для этого используем условный оператор if-then:

...
$passwords = $_POST["pword"];
echo "First password = ".$passwords[0];
echo "<br />";
echo "Second password = ".$passwords[1];

if ($passwords[0] == $passwords[1]) {
echo "<p>Passwords match. Thank you.</p>";
} else {
echo "<p>Passwords don't match. Please try again.</p>";
}
...

В заголовке условного оператора в скобках помещено некоторое выражение (в нашем примере это $passwords[0] == $passwords[1]), которое может быть истинно (TRUE) или ложно (FALSE). Если оно истинно, то PHP выполняет тот оператор, который следует за условным выражением, это может быть блок операторов в фигурных скобках. Если оно ложно, что выполняется блок, следующий за ключевым словом else, а если его нет, то скрипт переходит к выполнению следующего оператора.

Обратите внимание на то, что мы написали $passwords[0] == $passwords[1], а не $passwords[0] = $passwords[1], то есть использовали двойной знак равенства. Это не случайно. Простой знак равенства работает как оператор присваивания, то есть, значение $passwords[0] в этом случае просто станет равным $passwords[1]. Нам же надо совсем не это, мы хотим сравнить значения переменных и поэтому используем оператор сравнения -- двойное равенство.

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


Рисунок 10. Предупреждение об ошибке при вводе пароля
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

Для формирования более сложных логических выражений можно применять логические операторы, например, оператор И (&&) и оператор ИЛИ (||). Рассмотрим следующее выражение:

if (($today == "Monday") && ($status == "Not a holiday")) {
echo "GO TO WORK!!!";
}

Значение логического выражения в скобках будет истина (TRUE) в том случае, если сегодня понедельник (Monday) И этот день не является праздничным.



В начало



На предыдущую страницу Страница 3 из 10  На предыдущую страницу

Изучаем PHP: Часть 1. Страница регистрации, формы и взаимодействие с базой данных

developerWorks
На предыдущую страницу Страница 4 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Функции

Создание функций

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

Именно для этих целей и служат функции. Например, вы можете выделить проверку пароля в отдельную функцию:


...
<body>
<p>You entered:</p>

<?php

function checkPasswords($firstPass, $secondPass){

if ($firstPass == $secondPass) {
echo "<p>Passwords match. Thank you.</p>";
} else {
echo "<p>Passwords don't match. \
Please try again.</p>";
}

}

foreach ($_POST as $key=>$value) {
echo "<p>".$key." = " . $value . "</p>";
}

$passwords = $_POST["pword"];
echo "First password = ".$passwords[0];
echo "<br />";
echo "Second password = ".$passwords[1];

?>
</body>
</html>

Когда сервер будет обрабатывать эту страницу и дойдет до ключевого слова function, он пропустит следующий блок, поскольку функции выполняются только по специальному запросу, и перейдет непосредственно в выполнению цикла foreach, как можно видеть ниже на Рисунке 11.


Рисунок 11. Выполнение цикла foreach
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

Для того чтобы использовать функцию, ее надо вызвать.



В начало


Вызов функции

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

...
<body>
<p>You entered:</p>

<?php

function checkPasswords($firstPass, $secondPass){

if ($firstPass == $secondPass) {
echo "<p>Passwords match. Thank you.</p>";
} else {
echo "<p>Passwords don't match. Please try again.</p>";
}

}

foreach ($_POST as $key=>$value) {
echo "<p>".$key." = " . $value . "</p>";
}

$passwords = $_POST["pword"];
echo "First password = ".$passwords[0];
echo "<br />";
echo "Second password = ".$passwords[1];

checkPasswords($passwords[0], $passwords[1]);

?>
</body>
</html>

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


Рисунок 12. Выполнение функции checkPasswords() после цикла foreach
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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



В начало


Возвращаемые значения

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

...
<body>
<p>You entered:</p>

<?php
function validate($allSubmitted){

$message = "";

$passwords = $allSubmitted["pword"];
$firstPass = $passwords[0];
$secondPass = $passwords[1];
$username = $allSubmitted["name"];

if ($firstPass != $secondPass) {
$message = $message."Passwords don't match<br />";
}
if (strlen($username) < 5 || strlen($username) > 50){
$message = $message."Username must be \
between 5 and 50 characters<br />";
}

if ($message == ""){
$message = "OK";
}

return $message;

}
...

В качестве аргумента функция будет принимать массив $_POST и выбирать из него информацию для проверки. В начале работы функции мы создаем пустую строку сообщений $message. Если пароль введен с ошибкой или нас не устраивает длина имени пользователя (для проверки имени использовалась функция strlen(), которая возвращает количество символов в строке), то в переменную $message добавляется некоторый текст. Если же все прошло успешно, то строка остается пустой и мы записываем в нее сообщение "OK", далее, при вызове функции мы будем проверять наличие именно этого сообщения.



В начало


Проверка данных

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

...
echo "<br />";
echo "Second password = ".$passwords[1];

if (validate($_POST) == "OK") {
echo "<p>Thank you for registering!</p>";
} else {
echo "<p>There was a problem with your registration:</p>";
echo validate($_POST);
echo "<p>Please try again.</p>";
}

?>
...

В условном операторе if-then одновременно вызывается функция validate() и проверяется возвращаемое ею значение. Если проверка прошла успешно, то пользователю говорят "спасибо", а если нет -- то выводится сообщение об ошибке, как можно видеть ниже на Рисунке 13.


Рисунок 13. Вывод сообщения об ошибке
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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

Теперь наши данные готовы для записи их в базу данных.



В начало



На предыдущую страницу Страница 4 из 10  На предыдущую страницу


Изучаем PHP: Часть 1. Страница регистрации, формы и взаимодействие с базой данных

developerWorks
На предыдущую страницу Страница 5 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Взаимодействие с базой данных MySQL

Создание базы

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

Находясь в консоли MySQL, наберите следующие команды:

create database workflow;

use workflow;

create table users (id int auto_increment primary key, username
varchar(50), email varchar(255), password varchar(50));

show tables;

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

+--------------------+
| Tables_in_workflow |
+--------------------+
| users |
+--------------------+
1 row in set (0.00 sec)

Наконец, создадим пользователя wfuser с паролем wfpass:

GRANT ALL PRIVILEGES ON *.* TO 'wfuser'@'localhost'
IDENTIFIED BY 'wfpass' WITH GRANT OPTION;

Теперь мы можем перейти к использованию базы с PHP.



В начало


Соединение с базой данных MySQL

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

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

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

...
return $message;

}

function db_connect($user='wfuser',
$password='wfpass', $db='workflow'){

mysql_connect('localhost', $user, $password)
or die('I cannot connect to db: ' . mysql_error());

}

foreach ($_POST as $key=>$value) {
echo "<p>".$key." = " . $value . "</p>";
}

...
if (validate($_POST) == "OK") {
echo "<p>Thank you for registering!</p>";

db_connect();

} else {
echo "<p>There was a problem with your registration:</p>";
...

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

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

Если установить соединение с базой не удалось, то процесс останавливается (для этого служит опция dies) и PHP выводит текстовое сообщение об ошибке.

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

Последний выделенный оператор и есть, собственно, вызов нашей функции.



В начало


Выбор базы данных

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

...
function db_connect($user='wfuser',
$password='wfpass', $db='workflow'){

mysql_connect('localhost', $user, $password)
or die('I cannot connect to db: ' . mysql_error());
mysql_select_db($db);

}
...

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

Теперь мы готовы к тому, чтобы добавить данные в нашу базу.



В начало


Добавление записей в базу

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

Запрос будет иметь следующую форму:

insert into users (username, email, password) values 
('roadnick', 'ibmquestions@nicholaschase.com', 'supersecretpassword')

Вы могли заметить, что в этом запросе использованы не все поля таблицы users, поле с именем id пропущено. Дело в том, что это поле имело атрибут AUTO_INCREMENT, в том случае, если его значение пропущено в запросе, MySQL подставляет первое свободное целое число. Теперь попытаемся поместить в базу данных реальные данные из нашей формы регистрации, то есть, выполнить запрос:

...
if (validate($_POST) == "OK") {
echo "<p>Thank you for registering!</p>";

db_connect();

$sql = "insert into users (username, email, password) values
('".$_POST["name"]."', '".$_POST["email"]."', \
'".$passwords[0]."')";
$result = mysql_query($sql);

if ($result){
echo "It's entered!";
} else {
echo "There's been a problem: ".mysql_error();
}

} else {
echo "<p>There was a problem with your registration:</p>";
...

Обратите внимание на то, что функция, которая выполняет запрос, mysql_query(), возвращает значение логического типа, которое записывается в переменную $result. Если запрос отработал успешно, то значение будет "истина" (TRUE), а если возникли те или иные проблемы, то значение будет "ложь" (FALSE). Это значение можно затем использовать в условных операторах if-then, для того чтобы предпринять те или иные действия.

Если возникли какие-либо проблемы, MySQL передаст сообщение об ошибке в функцию mysql_error() и это сообщение можно будет вывести на странице.

Посмотрим теперь, как извлекается информация из базы данных.



В начало


Выбор записей из базы

Итак, мы умеем записывать наши данные в базу. Но как мы можем узнать, уникально ли предложенное имя пользователя? Посмотрим, как можно проверить имя, прежде чем добавлять данные:

...
if (validate($_POST) == "OK") {
echo "<p>Thank you for registering!</p>";

db_connect();

$sql = "select * from users where username='".$_POST["name"]."'";
$result = mysql_query($sql);
if (!$result) {

$sql = "insert into users (username, email, password) values
('".$_POST["name"]."', '".$_POST["email"]."', '".$passwords[0]."')";
$result = mysql_query($sql);

if ($result){
echo "It's entered!";
} else {
echo "There's been a problem: ".mysql_error();
}
} else {

echo "There is already a user with that name: <br />";
$sqlAll = "select * from users";
$resultsAll = mysql_query($sqlAll);

}

} else {
echo "<p>There was a problem with your registration:</p>";
...

Первый SQL-запрос выбирает из базы все записи, у которых значение поля username совпадает с именем пользователя, которое мы планируем вставить. Если такие записи были найдены, то функция mysql_query() возвращает TRUE, а если нет – то FALSE.

Поскольку мы хотим вставлять только уникальные имена, то для нас желательным значением переменной $result будет FALSE. Но условный оператор выполняет первый сегмент кода в том случае, если значение условного выражения равно TRUE. Конечно, мы могли бы поместить вставку записи в базу данных после ключевого слова else но для того, чтобы сделать нашу программу изящнее, мы применили к переменной $result оператор инверсии (представленный восклицательным знаком), которой меняет логическое значение на противоположное.

Что произойдет, если совпадающее значение будет найдено и переменная $result будет иметь значение TRUE? Тогда значение выражения !$result будет FALSE, и программа перейдет к выполнению сегмента else. Мы сообщим пользователю о том, что введенное имя уже существует и выполним SQL-запрос, который выбирает все данные из нашей таблицы.



В начало


Получение данных из базы

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

Выше мы создали и выполнили SQL-запрос, который выбирает все записи из таблицы users и записали результат в переменную $resultsAll. Очевидно, что эта переменная имеет сложную структуру. Посмотрим, как извлечь из нее данные:

...
} else {

echo "There is already a user with that name: <br />";
$sqlAll = "select * from users";
$resultsAll = mysql_query($sqlAll);
$row = mysql_fetch_array($resultsAll);

echo $row["username"]." -- ".$row["email"]."<br />";

}
...

Итак, для извлечения данных из переменной $resultsAll была использована функция mysql_fetch_array(), эта функция возвращает ассоциативный массив, соответствующий набору данных одной записи в таблице. Ключами массива являются имена полей таблицы. Запишем этот массив в переменную $row и затем извлечем из нее значения по ключам.

Да, но такой способ позволяет работать только с одной строкой таблицы. Каким образом получить доступ ко всем данным?



В начало


Просмотр результатов: цикл while

Если в результате выполнения запроса была выбрана хотя бы одна строка, то логическое значение переменной $row будет TRUE. Это обстоятельство можно использовать в условных операторах if-then или в циклах while. На первом шаге цикла значение условного выражения в скобках будет TRUE, поэтому начнет выполняться блок операторов. Данные массива отображаются на странице, затем делается попытка получить следующую строку.

...
} else {

echo "There is already a user with that name: <br />";
$sqlAll = "select * from users";
$resultsAll = mysql_query($sqlAll);
$row = mysql_fetch_array($resultsAll);
while ($row) {

echo $row["username"]." -- ".$row["email"]."<br />";

$row = mysql_fetch_array($result);
}
}
...

Цикл будет продолжаться до тех пор, пока остаются строки для обработки. После того как обработана последняя строка, функция mysql_fetch_array() вернет FALSE, цикл прервется и начнут выполняться операторы, стоящие после цикла.

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



В начало


Закрыть соединение с базой данных

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

...
if (validate($_POST) == "OK") {
echo "<p>Thank you for registering!</p>";

db_connect();

$sql = "select * from users where username='".$_POST["name"]."'";
$result = mysql_query($sql);
if (!$result) {
...
}

mysql_close();

} else {
...

Теперь настало время навести порядок в структуре наших файлов.



В начало



На предыдущую страницу Страница 5 из 10  На предыдущую страницу

Изучаем PHP: Часть 1. Страница регистрации, формы и взаимодействие с базой данных

developerWorks
На предыдущую страницу Страница 6 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Наведем порядок: включение кода из файлов

Зачем нужно включение кода из файлов?

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

В PHP существует два оператора включения файлов. Один из них используется для включения обычной информации, например, заголовков или сообщений, а другой -- для критической, например, для включения функций, вызываемых из Web-страницы.



В начало


Файлы для включения

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

Для начала создадим файл с именем top.txt и включим в него следующий текст:

<html>
<head>
<title>Workflow System</title>
</head>
<body>
<table>
<tr><td colspan="2"><h2>The Workflow System</h2></td></tr>

<tr>
<td width="30%">
<h3>Navigation</h3>

<p><a href="register.php">Register</a></p>

</td>
<td>

Во второй файл, который мы назовем bottom.txt, добавьте следующие строки:

   </td>
</tr>
</table>
</body>
</html>

Сохраните файлы в том же каталоге, где уже находится файл registration.php.



В начало


Оператор включения include

Теперь включим созданные нам файлы в страницу регистрации. Внесите в файл registration.php следующие изменения:

                    <?php

include("top.txt");

?>


<h1>Register for an Account:</h1>
<form action="registration_action.php" method="POST">

Username: <input type="text" name="name" /><br />
Email: <input type="text" name="email" /><br />
Password: <input type="password" name="pword[]" /><br />
Password (again): <input type="password" name="pword[]" /><br />
<input type="submit" value="GO" />

</form>

<?php

include("bottom.txt");

?>


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



В начало


Посмотрим на результаты

Обновите страницу регистрации в браузере и вы увидите, что ее внешний вид сильно переменился, новый вариант показан на Рисунке 14.


Рисунок 14. Новый вид регистрационной страницы
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

Если вы нажмете клавишу просмотра исходного кода страницы в своем браузере, то увидите, что в нем объединены все три файла:

<html>
<head>
<title>Workflow System</title>
</head>
<body>
<table>
<tr><td colspan="2"><h2>The Workflow System</h2></td></tr>

<tr>
<td width="30%">
<h3>Navigation</h3>

<p><a href="register.php">Register</a></p>

</td>
<td>


<h1>Register for an Account:</h1>
<form action="registration_action.php" method="POST">

Username: <input type="text" name="name" /><br />
Email: <input type="text" name="email" /><br />
Password: <input type="password" name="pword[]" /><br />
Password (again): <input type="password" name="pword[]" /><br />
<input type="submit" value="GO" />

</form>

</td>
</tr>
</table>
</body>
</html>

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

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



В начало


Оператор включения require

Если PHP не может найти файла заголовка или нижнего колонтитула, то это плохо, но не катастрофично, приложение все равно будет работать. Если отсутствует файл, указанный в операторе include, PHP выводит предупреждение и продолжает обработку страницы.

Однако в некоторых случаях отсутствие включаемого файла является катастрофой. Например, мы могли решить, что полезно поместить описание функций validate() и db_connect() в отдельный файл и затем включить этот файл в registration_action.php при помощи оператора include. Если PHP не сможет найти файла с функциями, но не прекратит работу, то возникнет масса проблем. Для того чтобы предотвратить эту ситуацию, следует использовать оператор require вместо include:

<?php

include("top.txt");
require("scripts.txt");
?>

<p>You entered:</p>

<?php

foreach ($_POST as $key=>$value) {
echo "<p>".$key." = " . $value . "</p>";
}

$passwords = $_POST["pword"];
echo "First password = ".$passwords[0];
echo "<br />";
echo "Second password = ".$passwords[1];

if (validate($_POST) == "OK") {
echo "<p>Thank you for registering!</p>";
...

В целом оператор require работает также, как и include, но если PHP не может найти включаемого файла, то он сообщает о фатальной ошибке и выполнение скрипта прекращается.



В начало


Предупреждение возникновения дубликатов.

Нет никаких препятствий к тому, чтобы включаемый файл сам содержал в себе оператор include. Но в том случае, если структура подключаемых файлов становится слишком сложной, есть риск включить один и тот же файл несколько раз. Кроме испорченного интерфейса от этого могут произойти и другие неприятности, например, ошибки, связанные с переопределением функций или констант. Для предупреждения повторного включения файлов в PHP существуют специальные версии операторов включения include и require, ниже вы можете видеть пример их использования:

<?php

include_once("top.txt");
require_once("scripts.txt");
?>

<p>You entered:</p>

<?php

foreach ($_POST as $key=>$value) {
echo "<p>".$key." = " . $value . "</p>";
}
...

Когда PHP встречает операторы include_once или require_once, то происходит проверка, не было ли включения указанных файлов ранее, таким образом предупреждается включение файлов-дубликатов.



В начало



На предыдущую страницу Страница 6 из 10  На предыдущую страницу

Изучаем PHP: Часть 1. Страница регистрации, формы и взаимодействие с базой данных

developerWorks
На предыдущую страницу Страница 7 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Подведем итоги

В этом пособии мы начали знакомство с языком программирования PHP с создания простого Web-приложения. Вы изучили основы синтаксиса PHP-скриптов, узнали как можно передавать данные при помощи HTML-форм, познакомились с такими базовыми понятиями как переменная, условный оператор и цикл. Мы определили понятие массива данных, рассмотрели ассоциативные массивы и массивы с числовыми индексами, способы доступа к данным массивов. Вы научились просматривать и отсылать данные в базу данных MySQL при помощи формирования и выполнения SQL-запросов, узнали каким образом можно обработать строку данных из таблицы базы. Наконец, мы рассмотрели операторы включения файлов, которые позволяют оптимизировать структуру Web-приложения.

Цель этой серии учебных пособий -- научить вас создавать с помощью PHP приложения, поддерживающие workflow. В первой части серии мы начали процесс с создания страницы, на которой пользователь вводит новую учетную запись, мы проверяем ее и помещаем в базу данных. В следующей части этой серии мы научимся использовать процедуру HTTP-аутентификации и загрузку документов с сервера, рассмотрим использование формата XML и другие вопросы, которые помогут вам в освоении программирования на PHP.



В начало



На предыдущую страницу Страница 7 из 10  На предыдущую страницу

Изучаем PHP: Часть 1. Страница регистрации, формы и взаимодействие с базой данных

developerWorks
На предыдущую страницу Страница 8 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Ресурсы

Научиться

Получить продукты и технологии
  • Загрузите PHP.

  • Загрузите MySQL.

  • Разработайте ваш следующий проект с открытым исходным кодом с использованием пробного программного обеспечения IMB, которое можно загрузить со страницы IBM trial software, или получить на DVD.


Обсудить

Изучаем PHP, Часть 2: Загрузка файлов на сервер и использование XML для хранения информации о файлах

developerWorks

Уровень сложности: средний

Тайлер Андерсон, Независимый автор, Stexar Corp.
Никлас Чейз, Независимый автор, Backstop Media

16.01.2007

Перейти к учебному пособию

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

Требуемый опыт

Базовые знания по PHP, на которые мы опираемся в этом учебном пособии, включают умение работать с такими структурами как условные операторы и циклы, HTML-формы и базы данных. Все эти темы обсуждались в Части 1 этой серии. Знакомство с форматом XML желательно, но не обязательно.


Системные требования

Вам необходимо иметь Web-сервер, PHP и базу данных. Если у вас есть доступ к сети, в которой установлен сервер PHP V5 с доступом к базе данных MySQL, то вы можете им воспользоваться. В противном случае загрузите и установите следующие пакеты:

Web сервер
Вне зависимости от того, работаете ли вы под Windows®, Linux® или Mac OS X, вы имеете возможность использовать Apache Web-сервер. Вы можете работать с версиями Apache V1.3 или 2.0, но примеры в этом пособии ориентированы на версию V2.0. Если вы работаете под Windows, вы можете также использовать Internet Information Services, встроенный в Windows Web-сервер.
PHP
Вам необходим дистрибутив PHP. Во время написания этого пособия в ходу были версии PHP V4 и V5, в своих примерах мы ориентировались на V5. Различия в версиях PHP несущественны для этой части пособия, но в последней части они станут заметны.
MySQL
Одной из тем этого учебника является получение доступа к базе данных. Соответственно, вам нужна та или иная база данных. Мы разбираем доступ к базе MySQL, поскольку именно эта база данных как правило используется вместе с PHP.


Форматы

html, pdf




В начало


IBM обладает всеми авторскими правами касательно информации, расположенной на developerWorks. Использование информации приведенной на этом ресурсе без явного письменного разрешения от IBM или первоначального автора запрещены. Если Вы желаете использовать информацию с developerWorks, пожалуйста воспользуйтесь регистрационной формой для того, чтобы связаться с нами запрос на использование материалов developerWorks Россия.
Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Отправить эту страницу по e-mail

Отправить эту страницу по e-mail

Опции документа, требующие включения JavaScript, не отображаются


Другие статьи из этой серии:
Изучаем PHP, Часть 2


Изучаем PHP: Часть 2. Загрузка файлов на сервер и использование XML для хранения информации о файлах

developerWorks
Страница 1 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Уровень сложности: средний

Тайлер Андерсон, инженер, Stexar Corp.
Николас Чейз (Nicholas Chase), президент, Chase and Chase, Inc.

15.01.2007

Из этого учебного пособия, второго из трех в серии "Изучаем PHP", вы узнаете, как использовать PHP для создания простейшего приложения, поддерживающего workflow. Это пособие поможет вам, если вы знакомы с основами программирования на языке PHP и хотите узнать о процедуре загрузки файла из браузера, сессиях и использовании PHP для работы с XML.

Прежде чем начать

Об этом учебном пособии

Цель этого учебного пособия -- научить вас писать на языке PHP приложения, поддерживающие workflow. В первой части серии "Изучаем PHP, Часть 1" рассматриваются основы синтаксиса PHP, работа с HTML-формами и взаимодействие с базами данных. Учебное приложение, разработанное в первой части, обеспечивает возможность ввести новую учетную запись, проверить и сохранить ее в базе данных.

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

Изучаем PHP, Часть 3 касается вопросов HTTP-аутентификации и защиты файлов путем потоковой передачи их из каталогов, закрытых для непосредственного доступа. В последней части мы изучим также создание объектов и исключения.

В этом пособии рассматриваются следующие темы:

  • Создание сессий и передача информации между сессиями.
  • Загрузка файлов на сервер из браузера.
  • Создание документов в формате XML с использованием DOM (Document Object Model -- Объектная модель документа).
  • Работа с данными в формате XML с помощью DOM.
  • Создание функций обработки XML-данных, стандарт SAX (Simple API for XML -- Простой API для XML).
  • Чтение данных в формате XML с помощью SAX.


В начало


Для кого написано это пособие?

Это учебное пособие является второй частью в серии из трех пособий, описывающих создание с помощью PHP приложения, поддерживающего workflow. Мы предполагаем, что у читателя есть базовые знания по PHP и желание научиться загружать файлы на сервер из браузера и использовать PHP для работы с XML.

Базовые знания по PHP, на которые мы опираемся в этом учебном пособии, включают умение работать с такими структурами как условные операторы и циклы, HTML-формы и базы данных. Все эти темы обсуждались в Части 1 этой серии. Знакомство с форматом XML желательно, но не обязательно. (Дополнительную информацию по темам этого пособия можно найти в разделе Ресурсы.)



В начало


Необходимые условия

Вам необходимо иметь Web-сервер, PHP и базу данных. Если у вас есть доступ к сети, в которой установлен сервер PHP V5 с доступом к базе данных MySQL, то вы можете им воспользоваться. В противном случае загрузите и установите следующие пакеты:

Web-сервер -- Вне зависимости от того, работаете ли вы под Windows®, Linux® или Mac OS X, вы имеете возможность использовать Apache Web-сервер. Вы можете работать с версиями Apache V1.3 или 2.0, но примеры в этом пособии ориентированы на версию V2.0. Загрузить Web-сервер можно со страницы Apache. Если вы работаете под Windows, вы можете также использовать Internet Information Services, встроенный в Windows Web-сервер.

PHP -- Вам необходим дистрибутив PHP. Во время написания этого пособия в ходу были версии PHP V4 и V5, в своих примерах мы ориентировались на V5. Различия в версиях PHP несущественны для этой части пособия, но в последней части они станут заметны. Загрузить дистрибутив можно со страницы PHP.

База данных -- Одной из тем этого учебника является получение доступа к базе данных. Соответственно, вам нужна та или иная база данных. Мы разбираем доступ к базе MySQL, поскольку именно эта база данных как правило используется вместе с PHP. Загрузите базу данных со страницы MySQL.



В начало



Страница 1 из 10  На предыдущую страницу

Изучаем PHP: Часть 2. Загрузка файлов на сервер и использование XML для хранения информации о файлах

developerWorks
На предыдущую страницу Страница 2 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Работа с сессиями

Процесс регистрации в системе, Часть 1

В Части 1 этой серии мы разработали простую систему регистрации пользователей и на этом примере научились отсылать информацию с помощью HTML-форм и взаимодействовать с базой данных. Результаты этой работы можно найти в разделе Ресурсы.


Мы пропустили только один момент, мы не создали страницу, при помощи которой пользователь может зарегистрироваться. Давайте сделаем это сейчас, заодно кратко повторим вопросы, разобранные в Части 1.


Создайте новый файл с именем login.php и сохрание его в том же каталоге, что и registration.php. Добавьте в него следующий код:


<?php

include("top.txt");
require("scripts.txt");
?>

<h1>Please log in</h1>
<form action="login_action.php" method="post">
Username: <input type="text" name="username" /><br />
Password: <input type="password" name="password" /><br />
<input type="submit" value="Log In" />
</form>

<?php
include("bottom.txt");
?>

Ввод данных пользователем и отправку HTML-формы мы обсуждали выше, вы помните, конечно, что PHP создает массив $_POST с двумя элементами: username и password, затем мы контролируем введенные данные с использованием базы данных. После этого страница, в которую включены элементы интерфейса, загружается в браузер.


Рисунок 1.Страница регистрации с элементами интерфейса
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.



В начало


Процесс регистрации в системе, Часть 2

Создайте еще один новый файл с именем login_action.php. Добавьте в него следующий код:

<?php

include("top.txt");
require("scripts.txt");

db_connect();

$username = $_POST["username"];
$password = $_POST["password"];
$sql = "select * from users where username='".$username.
"' and password='".$password."'";

$result = mysql_query($sql);
$row = mysql_fetch_row($result);
if ($row) {
echo "You are logged in. Thank you!";
} else {
echo "There is no user account with that username and password.";
}

mysql_close();

include("bottom.txt");
?>

После того как в начало файла с помощью оператора includе включены элементы интерфейса, открывается соединение с базой данных при помощи функции db_connect(), описание которой находится в файле scripts.txt. Пользуясь ссылкой на установленное соединение, мы можем выполнить SQL-запрос, проверить уникальность имени пользователя и записать данные в базу данных.



В начало


Создание сессии

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

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

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

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

Итак, добавим создание сессии в самое начало файла login_action.php:

<?php
session_start();

require("scripts.txt");

db_connect();

$username = $_POST["username"];
$password = $_POST["password"];
$sql = "select * from users where username='".$username.
"' and password='".$password."'";

$loginOk = false;

$result = mysql_query($sql);
$row = mysql_fetch_row($result);
if ($row) {
$loginOk = true;
}

mysql_close();

include("top.txt");

if ($loginOk) {
echo "You are logged in. Thank you!";
} else {
echo "There is no user account with that username and password.";
}

include("bottom.txt");

?>



В начало


Переменные сессии

Теперь мы должны указать, какие именно данные будут передаваться от страницы к странице в рамках сессии, в нашем случае это будут имя пользователя и его E-mail адрес:

<?php
session_start();

require("scripts.txt");

db_connect();

$username = $_POST["username"];
$password = $_POST["password"];
$sql = "select * from users where username='".$username.
"' and password='".$password."'";

$result = mysql_query($sql);
$row = mysql_fetch_row($result);
$loginOk = false;
if ($row) {

$loginOk = true;
$_SESSION["username"] = $row["username"];
$_SESSION["email"] = $row["email"];
}

mysql_close();

include("top.txt");

if ($loginOk) {
echo "You are logged in. Thank you, ".$username."!";
} else {
echo "There is no user account with that username and password.";
}

include("bottom.txt");

?>

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

Если вы используете версию PHP V4.3.2 или старше, то у вас может появиться предупреждение, касающееся области применения переменной $_SESSION. В этом случае нужно внести изменения в настройки сервера, для этого найдите файл php.ini и задайте в нем следующие переменные:

session.bug_compat_42 = 1
session.bug_compat_warn = 0

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



В начало


Использование сессии

Добавим операцию открытия сессии session_start() в начало включаемого файла top.txt:

                    <?
session_start();
?>

<html>
<head>
<title>Workflow System</title>
</head>
<body>
<table cellspacing="10">
<tr><td colspan="2"><h2>The Workflow System</h2></td></tr>
...

Когда PHP встретит эту функцию, то будет выполнено подключение к сессии, если она уже открыта или создана новая сессия. Теперь мы будем иметь доступ к зарегистрированным переменным сессии на других страницах, например, значение $_SESSION["username"] будет доступно в секции навигации:

<tr>
<td width="30%" valign="top">
<h3>Navigation</h3>

<?php
if (isset($_SESSION["username"]) || isset($username)){
?>
<p>You are logged in as <b><?=$_SESSION["username"].$username?></b>. You can
<a href="logout.php">logout</a> to login as a different user.</p>
<?php
} else {
?>
<p><a href="registration.php">Register</a></p>
<p><a href="login.php">Login</a></p>
<?php
}
?>
</td>
<td>
<td>

Обратите внимание на то, что при проверке мы используем не только переменную $_SESSION, но и $username. Это сделано потому, что значения переменных сессии меняются только при выполнении session_start(), таким образом на странице login_action.php они еще не будут заданы, а значение переменной $username уже будет определено, как показано ниже на Рисунке 2.


Рисунок 2.Страница login_action.php, переменная $username уже определена
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.



В начало


Очистка данных

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

<?
session_start();
unset($_SESSION["username"]);
unset($_SESSION["email"]);

include("top.txt");
echo "Thank you for using the Workflow System. You may <a href=\"login.php\
">log in again</a>.";
include("bottom.txt");
?>

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


Рисунок 3. Страница после очистки данных
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

Более простым способом очистки переменных является явное закрытие сессии:

<?
session_start();
session_destroy();

include("top.txt");
echo "Thank you for using the Workflow System. You may <a
href=\"login.php\">log in again</a>.";
include("bottom.txt");
?>

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



В начало



На предыдущую страницу Страница 2 из 10  На предыдущую страницу

Изучаем PHP: Часть 2. Загрузка файлов на сервер и использование XML для хранения информации о файлах

developerWorks
На предыдущую страницу Страница 3 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Загрузка файлов

Как происходит загрузка

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

  1. Пользователь вызывает форму, которая позволяет ему выбрать файл для загрузки.
  2. Пользователь выбирает файл и отсылает форму.
  3. Браузер отсылает сам файл и информацию о нем серверу как часть запроса.
  4. Сервер помешает файл во временный каталог.
  5. PHP проверяет и перемещает файл из временного каталога в основной каталог для постоянного хранения.

Начнем работу с создания HTML-формы.



В начало


HTML-форма для загрузки

Форма для загрузки файлов подобна формам для ввода нового пользователя и регистрации в системе, но здесь появляются два новых момента. Создадим новый файл для загрузки uploadfile.php и поместим в него следующий код:

<?php
include("top.txt");
?>

<h3>Upload a file</h3>

<p>You can add files to the system for review by an administrator.
Click <b>Browse</b> to select the file you'd like to upload,
and then click <b>Upload</b>.</p>

<form action="uploadfile_action.php" method="POST"
enctype="multipart/form-data">
<input type="file" name="ufile" \>

<input type="submit" value="Upload" \>
</form>

<?php
include("bottom.txt");
?>

Заметьте, у формы появился атрибут enctype, этот атрибут сообщает браузеру, что пересылаемая информация будет в особом формате, этот формат допускает передачу нескольких секций разнородных данных, а не только пар "имя-значение".

Атрибут file конструкции input обеспечивает появление окна для выбора файла, как показано ниже на Рисунке 4.


Рисунок 4. Выбор файла для загрузки
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

Обратите внимание, в наш файл, uploadfile.php включен файл с элементами интерфейса, top.txt.



В начало


Информация о загружаемом файле

Когда вы загружаете файл из браузера, PHP получает массив $_FILE, содержащий информацию о файле. Вы можете извлечь эту информацию, используя в качестве ключа имя, указанное в операторе input. В нашем случае имя было ufile, следовательно информация о файле будет содержаться в массиве $_FILE['ufile'].

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

В Части 1 нашего пособия мы уже сталкивались с ситуацией, когда элемент массива сам является массивом. В нашем примере в каждом элементе массива $_FILE будет размещена следующая информация:

  • $_FILE['ufile']['name'] -- имя выбранного файла (например, uploadme.txt)
  • $_FILE['ufile']['type'] -- тип выбранного файла (например, image/jpg)
  • $_FILE['ufile']['size'] -- размер выбранного файла в байтах
  • $_FILE['ufile']['tmp_name'] -- имя и каталог для временного размещения файла на сервере
  • $_FILE['ufile']['error'] -- код ошибки, возникает в том случае, если попытка загрузки была неудачной

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



В начало


Контроль загрузки файла

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

<?php
include("top.txt");

if(isset($_FILES['ufile']['name'])){
echo "Uploading: ".$_FILES['ufile']['name']."<br />";
} else {
echo "You need to select a file. Please try again.";
}

include("bottom.txt");
?>

Теперь перейдем к сохранению файла.



В начало


Сохранение файла

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

Создадим каталог /var/www/hidden/. Поскольку все временные файлы будут размещены в этом каталоге, то разумно будет определить константу с именем каталога. Добавьте строку с определением в файл scripts.txt:

...
mysql_select_db($db);

}
define(UPLOADEDFILES, "/var/www/hidden/");
?>

Поскольку файл scripts.txt включается в страницу, отвечающую за загрузку файла, мы можем использовать определенную выше константу:

<?php
include("top.txt");
include("scripts.txt");

if(isset($_FILES['ufile']['name'])){
echo "Uploading: ".$_FILES['ufile']['name']."<br>";

$tmpName = $_FILES['ufile']['tmp_name'];
$newName = UPLOADEDFILES . $_FILES['ufile']['name'];

if(!is_uploaded_file($tmpName) ||
!move_uploaded_file($tmpName, $newName)){
echo "FAILED TO UPLOAD " . $_FILES['ufile']['name'] .
"<br>Temporary Name: $tmpName <br>";
} else {
echo "File uploaded. Thank you!";
}

} else {
echo "You need to select a file. Please try again.";
}
include("bottom.txt");
?>

Первым шагом мы задаем две переменные, временное имя файла вместе с путем (на основе tmp_name) и новое имя, в котором используется определенная выше константа.

На втором шаге использован условный оператор, в котором мы делаем сразу две вещи. Во-первых, при помощи специальной функции мы проверяем, что временный файл, который мы планируем перемещать, действительно был загружен на сервер, что пользователь не пытается нас обмануть, предлагая загрузить в общедоступный каталог системный файл, например /etc/passwd. Если функция is_uploaded_file() определит, что указанный файл не загружался, то отрицание !is_uploaded_file() вернет значение TRUE, и PHP выведет сообщение об ошибке, не делая попытки переместить файл.

Если же проверка пройдет успешно, PHP попытается переместить файл на новое место при помощи функции move_uploaded_file(). Если во время перемещения возникли ошибки, то отрицание !move_uploaded_file() будет иметь значение TRUE, и появится сообщение об ошибке, как и в первом случае.

Если же обе проверки прошли успешно, то будет выведено сообщение о том, что файл успешно загружен, как можно видеть ниже на Рисунке 5.


Рисунок 5. Сообщение об успешной загрузке
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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



В начало



На предыдущую страницу Страница 3 из 10  На предыдущую страницу

Изучаем PHP: Часть 2. Загрузка файлов на сервер и использование XML для хранения информации о файлах

developerWorks
На предыдущую страницу Страница 4 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Использование XML: DOM

Что такое XML?

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

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<HTML>
<HEAD>
<TITLE>Workflow System</TITLE>
</HEAD>
<BODY>
<H1 align="center">Welcome to the Workflow System!</H1>
We're glad you could make it.
<P>
Don't forget to log in!
</BODY>
</HTML>

Эта страница имеет много общего с XML. Прежде всего, она состоит из элементов, таких как HEAD или BODY, начало и конец элемента обозначены тегами разметки, в начале стоит тег <BODY>, а в конце -- </BODY>. Эти элементы могут содержать в себе другие элементы (такие как TITLE) или текст (например, Workflow System). Элемент может иметь атрибуты, примером атрибута может служить align="center".

Но XML подходит к разметке документа строже, чем HTML. Документ в формате XML должен быть синтаксически корректным (well-formed), это означает в частности, что всякий открывающий тег (такой как <H1>) должен иметь парный закрывающий тег (а именно, </H1>). Каждый тег абзаца <P> должен быть закрыт тегом </P>. (Элемент может быть пустым, тогда его можно записать в краткой форме: <P/>). Кроме того, XML чувствителен к регистру, вы должны закрыть тег <P> тегом </P>, а не тегом </p>.

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



В начало


Как будет храниться информация

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

Структура нашего XML-файла будет следующая:

<?xml version="1.0"?>
<workflow>
<statistics total="2" approved="1"/>
<fileInfo status="approved" submittedBy="roadnick2">
<approvedBy>tater</approvedBy>
<fileName>timeone.jpg</fileName>
<location>/var/www/hidden/</location>
<fileType>image/jpeg</fileType>
<size>2020</size>
</fileInfo>
<fileInfo status="pending" submittedBy="roadnick">
<approvedBy/>
<fileName>timeone.jpg</fileName>
<location>/var/www/hidden/</location>
<fileType>image/jpeg</fileType>
<size>2020</size>
</fileInfo>
</workflow>

Мы добавили здесь переходы на новую строку и отступы для того, чтобы сделать структуру файла более наглядной, XML этого не требует. Каждому документу будет соответствовать элемент fileInfo с двумя атрибутами: владелец и статус. Информация о документе будет представлена элементами, входящими в fileInfo. Статистическая информация о загруженных документах будет храниться в элементе statistics.

Для работы с этим файлом мы будем использовать механизм DOM (Document Object Model -- Объектная модель документа).



В начало


Что такое DOM?

В основе технологии DOM (Document Object Model -- Объектная модель документа) лежит представление XML-файла в форме иерархического дерева. Рассмотрим объектную модель элемента fileInfo нашего XML-файла:

   <fileInfo status="approved" submittedBy="roadnick2">
<approvedBy>tater</approvedBy>
<fileName>timeone.jpg</fileName>
<location>/var/www/hidden/</location>
<fileType>image/jpeg</fileType>
<size>2020</size>
</fileInfo>

Каждый объект XML-документа является узлом (нодой) того или иного типа, например, fileInfo является узлом типа "элемент"; status является узлом тип "атрибут"; также наш документ содержит несколько текстовых узлов, примерами могут служить tater, timeone.jpg и 2020. Каждая порция пробелов между элементами также рассматривается как текстовый узел.

Узлы в DOM могут быть связаны между собой отношениями родитель/потомок. Например, элемент approvedBy является потомком элемента fileInfo, а текстовый узел tater является потомком элемента approvedBy. В нашем примере элемент fileInfo имеет 11 узлов-потомков; пять их них являются элементами и шесть -- порциями пробелов.

Существуют различные программные интерфейсы (API) для работы с DOM-объектами. Ранние версии PHP включали в себя работу с DOM-структурами, но они не полностью соответствовали стандартам (DOM является стандартом W3C). В PHP версии V5 включен новый набор средств работы с DOM, который в большей мере соответствует стандарту.

Мы будем использовать новый API для создания и модификации нашего XML-файла.



В начало


Несколько слов об объектах

В Части 3 нашей серии мы будем подробно говорить об объектах и объектно-ориентированном подходе в PHP, но обе технологи, которым будет посвящен следующий раздел, DOM API и SAX API, используют понятие объекта. Для того чтобы двигаться дальше, нам надо понять, что такое объект, и как с ним работать.

Для наших текущих целей достаточно думать об объекте просто как о наборе функций, рассмотрим это на примере. Создадим класс, он будет шаблоном для создания объектов в будущем:

class Greeting{
function getGreeting(){
return "Hello!";
};
function getPersonalGreeting($name){
return "Hello, ".$name."!";
};
}

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

$myObject = new Greeting();
$start = $myObject->getPersonalGreeting("Nick");

Итак, мы создали объект myObject типа Greeting, на него можно ссылаться как на обычную переменную, представляющую строку или число. Для вызова функций, определенных для данного объекта, используется знак ->, в остальном вызов функции объекта не отличается от вызова обычной функции.

Это все, что вам необходимо знать об объектах, для того чтобы двигаться дальше.



В начало


Подготовка к сохранению информации

Теперь мы готовы к тому, чтобы начать создание XML-файла, в котором будет сохраняться информация о загруженных файлах. Для записи этой информации мы создадим специальную функцию, save_document_info(), сначала добавим вызов этой функции в файл uploadfile_action.php:

  if(!is_uploaded_file($tmpName) ||
!move_uploaded_file($tmpName, $newName)){
echo "FAILED TO UPLOAD " . $_FILES['ufile']['name'] .
"<br>Temporary Name: $tmpName <br>";
} else {

save_document_info($_FILES['ufile']);

}
...

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

Теперь перейдем к созданию самой функции.



В начало



На предыдущую страницу Страница 4 из 10  На предыдущую страницу

Изучаем PHP: Часть 2. Загрузка файлов на сервер и использование XML для хранения информации о файлах

developerWorks
На предыдущую страницу Страница 5 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Функции

Создание документа DOM

Сначала создадим DOM-объект типа Document, он и будет использоваться для сохранения данных. Добавьте в файл scripts.txt следующий код:

...
define(UPLOADEDFILES, "/var/www/hidden/");

function save_document_info($fileInfo){

$doc = new DOMDocument('1.0');

}
?>

Для создания объекта типа Document с именем $doc, мы использовали класс DOMDocument, который описан в ядре PHP V5. Начиная с этого места переменная $doc будет использоваться для всех действий, связанных с нашими данными.



В начало


Создание элемента

Итак, у нас есть объект типа Document, добавим в него основной, корневой, элемент:

...
function save_document_info($fileInfo){

$doc = new DOMDocument('1.0');
$root = $doc->createElement('workflow');
$doc->appendChild($root);

}
...

Итак, мы вызываем функцию создания нового узла типа "элемент" для объекта $doc, этому узлу бедет соответствовать переменная $root. Затем созданный узел добавляется в качестве потомка к самому документу.

Давайте посмотрим, что произойдет, если мы выполним эту функцию и запишем документ в файл.



В начало


Запись XVL-документа в файл

Одним из удобств использования PHP для работы с файлами в формате XML является простота записи содержимого объекта типа Document в файл. (Не думайте, что это общее свойство, далеко не для всех языков это так просто.) Для просмотра сгененрированного документа добавьте в функцию save_document_info() следующий код:

...
function save_document_info($fileInfo){

$doc = new DOMDocument('1.0');
$root = $doc->createElement('workflow');
$doc->appendChild($root);

$doc->save(UPLOADEDFILES."docinfo.xml");

}
...

Константа UPLOADEDFILES была определена раньше, мы можем ссылаться на нее и разместить новый файл в том же каталоге. Если вы загрузите записанный файл docinfo.xml в браузер, то увидите:

<?xml version="1.0"?>
<workflow/>

Первая строка -- это просто декларация, сообщающая о том, что файл будет в формате XML, она стандартна.

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

Давайте добавим содержательную информацию.



В начало


Создание атрибутов

Итак, добавим в наш файл реальное содержание. На прошлом шаге мы уже создали файл docinfo.xml. Если мы дадим такую же команду записи, то файл будет перезаписан, но пока нас это устраивает, поскольку мы не закончили работу со структурой элемента.

Прежде всего, добавим новый элемент statistics:

...
function save_document_info($fileInfo){

$doc = new DOMDocument('1.0');
$root = $doc->createElement('workflow');
$doc->appendChild($root);

$statistics = $doc->createElement("statistics");
$statistics->setAttribute("total", "1");
$statistics->setAttribute("approved", "0");
$root->appendChild($statistics);

$doc->save(UPLOADEDFILES."docinfo.xml");

}
...

Мы начали с того, что создали новый элемент, принадлежащий объекту $doct (который относится к классу Document), этот элемент называется statistics, также названа и переменная, которая ему соответствует. Класс Document работает как "фабрика" наших объектов.

Вызовем для объекта типа Element с именем $statistics встроенную функцию, которая добавит два атрибута: total и approved. Затем определим для нового элемента надлежащее место в иерархии, укажем, что он является потомком корневого элемента $root (но не самого документа $doc). Сохраните файл и вы увидите изменения:

<?xml version="1.0">
<workflow><statistics total="1" approved="0"/></workflow>

Обратите внимание на два момента, во-первых, элемент statistics стал потомком элемента workflow и, во-вторых, дополнительных пробелов и новой строки добавлено не было. Элемент statistics является первым потомком элемента workflow.

Добавим в нашу структуру и другие элементы.



В начало


Создание элементов для хранения информации о файлах

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

...
function save_document_info($fileInfo){

$doc = new DOMDocument('1.0');
$root = $doc->createElement('workflow');
$doc->appendChild($root);

$statistics = $doc->createElement("statistics");
$statistics->setAttribute("total", "1");
$statistics->setAttribute("approved", "0");
$root->appendChild($statistics);

$filename = $fileInfo['name'];
$filetype = $fileInfo['type'];
$filesize = $fileInfo['size'];

$fileInfo = $doc->createElement("fileInfo");

$fileInfo->setAttribute("status", "pending");
$fileInfo->setAttribute("submittedBy", $_SESSION["username"]);

$approvedBy = $doc->createElement("approvedBy");

$fileName = $doc->createElement("fileName");
$fileNameText = $doc->createTextNode($filename);
$fileName->appendChild($fileNameText);

$location = $doc->createElement("location");
$locationText = $doc->createTextNode(UPLOADEDFILES);
$location->appendChild($locationText);

$type = $doc->createElement("fileType");
$typeText = $doc->createTextNode($filetype);
$type->appendChild($typeText);

$size = $doc->createElement("size");
$sizeText = $doc->createTextNode($filesize);
$size->appendChild($sizeText);

$fileInfo->appendChild($approvedBy);
$fileInfo->appendChild($fileName);
$fileInfo->appendChild($location);
$fileInfo->appendChild($type);
$fileInfo->appendChild($size);

$root->appendChild($fileInfo);

$doc->save(UPLOADEDFILES."docinfo.xml");

}
...

В приведенном выше фрагменте очень много кода, но нет практически ничего нового. Сначала мы создаем переменные для хранения переданной нам в вызове функции информации о документе. Затем создаем элемент fileInfo, в который и будет помещена вся информация. Этот элемент имеет два атрибута: status и submittedBy, у него есть несколько элементов-потомков.

Элемент approvedBy пока пуст, поскольку документ не одобрен. Элемент fileName имеет более сложную структуру, в него надо поместить узел-потомок с текстом. Для этого используем "фабрику" Document с функцией createTextNode(), которой передается в качестве аргумента строка с именем загружаемого файла. Затем текстовый узел приписывается в качестве потомка к элементу fileName.

Таким же образом создаются и другие элементы-потомки узла fileInfo. Наконец, сам элемент fileInfo занимает свое место в иерархии как потомок корневого элемента workflow.

Результатом работы этого кода будет следующий файл (для наглядности в него добавлены отступы):

<?xml version="1.0"?>
<workflow>
<statistics total="1" approved="0"/>
<fileInfo status="pending" submittedBy="roadnick">
<approvedBy/>
<fileName>signed.pem</fileName>
<location>/var/www/hidden/</location>
<fileType>application/octet-stream</fileType>
<size>2754</size>
</fileInfo>
</workflow>

Теперь, когда в наш XML-файл записана реальная информация, мы не хотим затирать его при каждой следующей записи. Рассмотрим, как можно дописать информацию в уже существующий файл.



В начало


Дополнение существующего файла

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

...
function save_document_info($fileInfo){

$xmlfile = UPLOADEDFILES."docinfo.xml";

if(is_file($xmlfile)){
$doc = DOMDocument::load($xmlfile);
$workflowElements = $doc->getElementsByTagName("workflow");
$root = $workflowElements->item(0);
} else{

$doc = new DOMDocument('1.0');
$root = $doc->createElement('workflow');
$doc->appendChild($root);

$statistics = $doc->createElement("statistics");
$statistics->setAttribute("total", "1");
$statistics->setAttribute("approved", "0");
$root->appendChild($statistics);
}

$filename = $fileInfo['name'];
$filetype = $fileInfo['type'];
$filesize = $fileInfo['size'];

$fileInfo = $doc->createElement("fileInfo");
...
$fileInfo->appendChild($size);

$root->appendChild($fileInfo);

$doc->save($xmlfile);

}

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

Функция load() является статической, статические функции рассматриваются подробно в Части 3 этой серии, сейчас нам надо только иметь в виду, что такие функции вызываются из класса, а не из объекта. Эта функция вернет объект типа Document, в котором будут содержаться все элементы, включенные в файл ранее.

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

Далее, добавим новый элемент fileInfo, как мы это уже делали выше. Результат (отступы вставлены) будет следующий:

<?xml version="1.0"?>
<workflow>
<statistics total="1" approved="0"/>
<fileInfo status="pending" submittedBy="roadnick">
...
</fileInfo>
<fileInfo status="pending" submittedBy="roadnick">
<approvedBy/>
<fileName>timeone.jpg</fileName>
<location>/var/www/hidden/</location>
<fileType>image/jpeg</fileType>
<size>2020</size>
</fileInfo>
</workflow>

Но элемент statistics нашего файла пока содержит старую информацию. Давайте исправим ситуацию.



В начало


Изменение существующих данных

Мы не только можем дописывать новые строки в созданный ранее файл, но и получать доступ к его элементам. В нашем случае нужно исправить значение атрибута total элемента statistics:

...
if(is_file($xmlfile)){
$doc = DOMDocument::load($xmlfile);
$workflowElements = $doc->getElementsByTagName("workflow");
$root = $workflowElements->item(0);

$statistics = $root->getElementsByTagName("statistics")->item(0);
$total = $statistics->getAttribute("total");
$statistics->setAttribute("total", $total + 1);

} else{
...

Сначала мы создадим переменную, которая будет ссылаться на элемент statistics, все делается также, как и в случае элемента workflow, но только два шага здесь объединены в один оператор. Получив ссылку на элемент, присвоим переменной текущее значение атрибута total, для этого использована функция getAttribute(). Увеличим это значение на 1 и запишем его в атрибут total, используя функцию setAttribute() с двумя аргументами.

Результат изменений вы можете видеть ниже, на этот раз пробелы не были добавлены:

<?xml version="1.0"?>
<workflow><statistics total="2" approved="0"/><fileInfo status="pending"
submittedBy="roadnick">...

Итак, мы научились создавать XML-файл с помощью DOM, рассмотрим теперь другой API, SAX, который предназначен для чтения информации из файлов в формате XML.



В начало



На предыдущую страницу Страница 5 из 10  На предыдущую страницу

Изучаем PHP: Часть 2. Загрузка файлов на сервер и использование XML для хранения информации о файлах

developerWorks
На предыдущую страницу Страница 6 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Использование XML: SAX

Что такое SAX?

В предыдущем разделе мы рассматривали технологию DOM, которая трактует XML-файл как совокупность объектов, организованных в иерархическое дерево. Соответствующий программный интерфейс был объектно-ориентированным API. В этом разделе мы рассматриваем SAX (Simple API for XML -- Простой API для XML), он является событийно-ориентированным API. Мы будем использовать этот API для отображения информации о файлах для конкретного пользователя.

В SAX используется обработчик содержания (content handler) который отслеживает поток событий. Рассмотрим для примера следующий XML-документ:

<workflow>
<statistics total="3" approved="2" />
<fileInfo submittedBy="roadnick" status="approved">
<fileName>timeone.jpg</fileName>
</fileInfo>
</workflow>

При разборе этого документа обработчик содержания SAX обнаружит следующие события:

Start document
Start element (workflow)
Characters (whitespace)
Start element (statistics)
End element (statistics)
Characters (whitespace)
Start element (fileInfo)
Characters (whitespace)
Start element (fileName)
Characters (timeone.jpg)
End element (fileName)
Characters (whitespace)
End element (fileInfo)
Characters (whitespace)
End element (workflow)
End document

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



В начало


Создание обработчика содержания

Итак, создадим обработчик содержания. Для обработчика мы будем использовать класс с именем Content_Handler, но имя класса может быть и другим. Откройте файл scripts.txt и добавьте в него следующий код:

class Content_Handler{
function start_element($parser, $name, $attrs){

echo "Start element: ".$name."<br />";

}
function end_element($parser, $name){

echo "End element: ".$name."<br />";

}
function chars($parser, $chars){

echo "Text: ".$chars."<br />";

}
}

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



В начало


Назначение обработчиков

В процессе разбора XML-файла PHP должен знать, какое действие надо выполнить при том или ином событии. Добавьте в файл scripts.txt функцию display_files(), описание которой приведено ниже:

...
function display_files(){

$handler = new Content_Handler();

$doc_parser = xml_parser_create();

xml_set_object($doc_parser, $handler);

xml_set_element_handler($doc_parser,
"start_element",
"end_element");
xml_set_character_data_handler($doc_parser, "chars");

xml_parser_set_option($doc_parser,
XML_OPTION_CASE_FOLDING, 0);

}
...

Рассмотрим эту функцию. Сначала в ней создается новый объект типа Content_Handler с именем $handler. Затем мы получаем ссылку на парсер XML, встроенный в PHP, и используем функцию xml_set_object() для того, чтобы сообщить парсеру, где находятся функции, которые он будет вызывать.

Затем мы должны указать парсеру, какие именно фукции должны вызываться при том или ином событии. Поскольку в данном API имена таких функций произвольны, вы должны явно указать их, используя функцию xml_set_element_handler(). Итак, мы сообщаем парсеру, что при появлении события "start element" должна быть вызвана функция start_element(), а при событии "end element" -- функция end_element().

Заметим, что функцию xml_set_object() можно пропустить и написать просто:

   xml_set_element_handler($doc_parser,
"$handler->start_element",
"$handler->end_element");

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



В начало


Открытие и разбор XML-файла

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

...
xml_parser_set_option($doc_parser,
XML_OPTION_CASE_FOLDING, 0);

$xmlfile = UPLOADEDFILES."docinfo.xml";

$file_to_parse = fopen($xmlfile, "r");
if (!$file_to_parse) die("Can't open XML file.");

while($data = fread($file_to_parse, 4096)){
xml_parse($doc_parser, $data, feof($file_to_parse));
}
}
...

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

Если файл успешно открыт, мы начинаем его разбор, передавая парсеру порции по 4,096 байтов. Цикл продолжается до тех пор, пока не будет достигнут конец файла.

Во-вторых, нам следует вызвать функцию display_files() из файла uploadfile_action.php:

...
echo "FAILED TO UPLOAD " . $_FILES['ufile']['name'] .
"<br>Temporary Name: $tmpName <br>";
} else {

save_document_info($_FILES['ufile']);
display_files();

}
...

Теперь посмотрим, как это работает.



В начало


Первые результаты

Итак, допустим, что мы загрузили документ, PHP сохранит наш файл и добавит информацию о документе в файл docinfo.xml, затем будет вызван парсер (попутно будет создан объект Content_Handler), который начнет разбор данных. Из парсера для каждого события будет вызвана соответствующая функция, и мы увидим следующий результат:

Start element: workflow
Start element: statistics
End element: statistics
Start element: fileInfo
Start element: approvedBy
End element: approvedBy
Start element: fileName
Text: timeone.jpg
End element: fileName
Start element: location
Text: /var/www/hidden/
End element: location
Start element: fileType
Text: image/jpeg
End element: fileType
Start element: size
Text: 2020
End element: size
End element: fileInfo
Start element: fileInfo
Start element: approvedBy
End element: approvedBy
Start element: fileName
Text: timeone.jpg
End element: fileName
Start element: location
Text: /var/www/hidden/
End element: location
Start element: fileType
Text: image/jpeg
End element: fileType
Start element: size
Text: 2020
End element: size
End element: fileInfo
End element: workflow

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



В начало


Что мы хотим получить в итоге

Мы научились вызывать обработчик содержания и получать от него данные, давайте тепреь подумаем, каким должен быть внешний вид полученной информации. Хотелось бы иметь вывод в табличной форме, давайте возьмем за образец форму, которую можно видеть на Рисунке 6.


Рисунок 6. Вывод информации в табличной форме
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

Посмотрим на исходный код HTML-страницы:

...
Uploading: bespin-lunch-1.jpg<br />
<!-- At the start of the document -->
<h3>Available files</h3>
<table width='100%'>
<!-- At the start of the document -->
<!-- For each fileInfo element -->
<tr><th>File Name</th>
<th>Submitted By</th><th>Size</th>
<th>Status</th></tr>
<!-- For each fileInfo element -->
<tr><td>timeone.jpg</td>
<td>roadnick</td><td>2020</td>
<td>pending</td></tr>
<tr><td>timetwo.jpg</td>
<td>roadnick</td><td>1416</td>
<td>pending</td></tr>
<tr><td>signupActivate.html</td><td>tater</td>
<td>37594</td><td>approved</td></tr>
<tr><td>signup.html</td><td>tater</td>
<td>32194</td>
<td>approved</td></tr>
<tr><td>bespin-lunch-1.jpg</td><td>roadnick</td>
<td>29304</td><td>pending</td></tr>
<!-- At the end of the document -->
</table>
<!-- At the end of the document -->
</td>

Просмотрев исходный код страницы и обратив внимание на комментарии, мы понимаем, что надо делать.



В начало


Начало и конец документа

В некоторых языках есть специальные функции, которые позволяют обрабатывать начало и конец документа, но в PHP это не так просто. Можно написать специальный обработчик для этих событий, но мы поступим иначе. Будем рассматривать в качестве начала и конца документа начало и конец элемента workflow соответственно. Добавьте следующий код в описание класса Content_Handler:

...
class Content_Handler{

function start_element($parser, $name, $attrs){

if ($name == "workflow"){
echo "<h3>Available files</h3>";
echo "<table width='100%'><tr>".
"<th>File Name</th><th>Submitted By</th>".
"<th>Size</th><th>Status</th></tr>";
}

}

function end_element($parser, $name){

if ($name == "workflow"){
echo "</table>";

}

}

function chars($parser, $chars){
//echo "Text: ".$chars."<br />";
}
}
...

Когда парсер встречает событие start element, он вызывает функцию start_element() и передает ей в качестве параметров ссылку на парсер, имя элемента и атрибуты. Вы можете использовать эти данные для организации табличного вывода.

Наша функция, встретив имя элемента workflow и событие start element, вставит открывающий тег таблицы и добавит в нее строку заголовков для столбцов. При закрытии элемента добавляется просто тег </table>, закрывающий таблицу, результат работы вы можете видеть ниже на Рисунке 7.


Рисунок 7. Отображение заголовков
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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



В начало


Метод start_element()

Специфической особенность работы событийно-ориентированного API является тот факт, что в каждый момент времени парсер имеет информацию только о последнем событии. Когда мы имеем дело с DOM, то можем перемещаться по дереву событий вверх и вниз, используя SAX мы движемся с потоком событий и вернуться назад не можем, поэтому мы должны сохранять всю необходимую нам информацию о прошедших событиях, если хотим ею воспользоваться. Допустим, мы встретили элемент fileName, но мы не знаем, должны ли мы отображать файл, поскольку информация об этом хранится в атрибуте элемента fileInfo, который был раньше. Чтобы исправить ситуацию, запишем значение соответствующего атрибута в переменную:

...
class Content_Handler{

private $available = false;
private $submittedBy = "";
private $status = "";


function start_element($parser, $name, $attrs){

if ($name == "workflow"){
echo "<h3>Available files</h3>";
echo "<table width='100%'><tr>".
"<th>File Name</th><th>Submitted By</th>".
"<th>Size</th><th>Status</th></tr>";
}

if ($name == "fileInfo"){
if ($attrs['status'] == "approved" ||
$attrs['submittedBy'] == $_SESSION["username"]){
$this->available = true;
}
if ($this->available){
$this->submittedBy = $attrs['submittedBy'];
$this->status = $attrs['status'];
}
}

}

function end_element($parser, $name){
...

Встретив элемент fileInfo, проверим значение атрибутов status и submittedBy и решим, нужно ли показывать этот документ. Для доступа к значениям атрибутов был использован массив $attrs с соответствующими ключами.

Если значение атрибута status равно "approved" или если файл был загружен текущим пользователем, то информацию следует отобразить. Для сохранения результата проверки будет использоваться переменная available, которая была определена внутри класса. (Не обращайте пока внимание на модификатор private, мы будем разбирать его значение в Части 3 этого пособия.) Поскольку переменная определена внутри объекта, мы можем ссылаться на нее, используя синтаксис $this->available, значение переменной не будет потеряно при перемещении к следующему элементу.

Если информация будет отображаться, то нам нужны и сами значения атрибутов, запишем их в переменные submittedBy и status.

Но нам нужно сохранять не только значения атрибутов, но и текстовые элементы.



В начало


Метод characters()

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

class Content_Handler{

private $available = false;
private $submittedBy = "";
private $status = "";

private $currentElement = "";

private $fileName = "";
private $fileSize = "";


function start_element($parser, $name, $attrs){
...
$this->currentElement = $name;

}

function end_element($parser, $name){

if ($name == "workflow"){
echo "</table>";
}

$this->currentElement = "";

}

function chars($parser, $chars){

if ($this->available){
if ($this->currentElement == "fileName"){
$this->fileName = $this->fileName . $chars;
}
if ($this->currentElement == "size"){
$this->fileSize = $this->fileSize . $chars;
}
}

}
}

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

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



В начало


Метод end_element()

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

...
function end_element($parser, $name){

if ($name == "workflow"){
echo "</table>";
}

if ($name == "fileInfo"){
echo "<tr><td>".$this->fileName."</td>".
"<td>".$this->submittedBy."</td>".
"<td>".$this->fileSize."</td>".
"<td>".$this->status."</td></tr>";

$this->fileName = "";
$this->submittedBy = "";
$this->fileSize = "";
$this->status = "";

$this->available = false;
}

$this->currentElement = "";

}
...

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

В результате работы нашей программы мы получим таблицу, приведенную на Рисунке 8.


Рисунок 8. Итоговая таблица информации о файлах
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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



В начало



На предыдущую страницу Страница 6 из 10  На предыдущую страницу

Изучаем PHP: Часть 2. Загрузка файлов на сервер и использование XML для хранения информации о файлах

developerWorks
На предыдущую страницу Страница 7 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Подведем итоги

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

  • Создание сессии
  • Работа в рамках сессии
  • Загрузка файла на сервер
  • Создание XML-файла с использованием DOM
  • Загрузка XML-данных с использованием DOM
  • Работа с данными в формате XML с использованием DOM
  • Создание обработчика содержания SAX
  • Разбор XML-файла с использованием SAX
  • Отслеживание данных XML-файла с использованием SAX

Перейдем к Части 3, в которой мы завершим работу над нашим учебным приложением.



В начало



На предыдущую страницу Страница 7 из 10  На предыдущую страницу

Изучаем PHP: Часть 2. Загрузка файлов на сервер и использование XML для хранения информации о файлах

developerWorks
На предыдущую страницу Страница 8 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Ресурсы

Научиться
  • Оригинал этой части пособия на английском языке Learning PHP, Part 2.

  • Прочтите отличный вводный учебник по объектам в PHP Using PHP Objects.

  • Освежить свои знания по основам синтаксиса PHP можно, обратившись к первой части нашей серии: "Изучаем PHP, Часть 1."

  • Прочтите официальное руководство PHP Manual.

  • За справками можно обратиться к PHP Manual: Session Handling Functions.

  • Также может быть полезно посмотреть PHP Manual: DOM Functions.

  • Посмотрите также PHP Manual: XML Parser Functions.

  • Еще одно руководство PHP Manual: php.ini directives.

  • Может быть полезно "SAX-like apps in PHP."

  • Посетите страницу IBM developerWorks PHP project resources и узнайте больше о разработках на PHP.

  • Познакомиться со свежими новостями в технологии PHP можно на странице developerWorks technical events and webcasts. .

  • Узнать о предстоящих конференциях, выставках и других событиях, о размещении в сети новых материалов, относящихся к разработкам с открытым исходным кодом, можно на странице Events.

  • Посетите страницы developerWork Open source zone, здесь можно найти практические руководства HOWTO, информацию об инструментальных средствах, свежие версии и обновления популярных пакетов, которые помогут вам применять технологии с открытым исходным кодом и использовать их в программных продуктах IBM.

  • Послушать интересные интервью и дискуссии разработчиков программного обеспечения можно на страницах developerWorks podcasts.


Получить продукты и технологии
  • Разработайте ваш следующий проект с открытым исходным кодом с использованием пробного программного обеспечения IMB, которое можно загрузить со страницы IBM trial software, или получить на DVD.


Обсудить

Изучаем PHP: Часть 2. Загрузка файлов на сервер и использование XML для хранения информации о файлах

developerWorks
На предыдущую страницу Страница 9 из 10  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Об авторах

Тайлер Андерсон (Tyler Anderson) получил степень бакалавра по компьютерным наукам в Brigham Young University в 2004 и степень магистра по компьютерной технике в декабре 2005 тоже в Brigham Young University. Сейчас он работает инженером в фирме Stexar Corp., расположенной в Beaverton, Ore.


Николас Чейз (Nicholas Chase), автор Studio B , занимается разработкой Web-сайтов для таких компаний, как Lucent Technologies, Sun Microsystems, Oracle и Tampa Bay Buccaneers. Ник работал школьным преподавателем физики, управляющим предприятия по утилизации малоактивных радиоактивных отходов, редактором онлайнового журнала по научной фантастике, специалистом по мультимедиа и инструктором Oracle. Не так давно он занимал пост главного директора по технологиям компании Site Dynamics Interactive Communications в Клируотере, Флорида, США. Автор четырех книг по Web-разработке, включая XML Primer Plus (издательство Sams). Ник всегда рад получить комментарии от читателей, которые можно присылать по адресу nicholas@nicholaschase.com.


Изучаем PHP: Часть 2. Загрузка файлов на сервер и использование XML для хранения информации о файлах

developerWorks
На предыдущую страницу Страница 10 из 10 

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить

Выскажите мнение об этом учебном пособии


Пожалуйста, найдите минутку и заполните форму, чтобы повысить уровень сервиса.



Да Нет Не знаю




1 2 3 4 5

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

Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
Страница 1 из 11  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Уровень сложности: средний

Никлас Чейз, Независимый автор, Backstop Media
Тайлер Андерсон, Независимый автор, Stexar Corp.

16.01.2007

Из этого учебного пособия, последнего из трех в серии "Изучаем PHP", вы узнаете, как использовать PHP для создания простейшего приложения, поддерживающего workflow. В этой части рассматривается HTTP-аутентификация, доступ к документам посредством обработки потоков данных, создание объектов и обработка исключений.

Прежде чем начать

Об этом учебном пособии

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

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

  • Подключение и использование HTTP-аутентификации
  • Перемещение файла с использованием потока данных
  • Создание классов и объектов
  • Использование методов и свойств объектов
  • Описание и обработка исключений
  • Использование ID атрибутов XML
  • Организация проверки синтаксической корректности XML-документов с использованием DTD (Document Type Definition -- Опеределние Типа Документа)
  • Обеспечение контроля доступа к данным с использованием информации об источнике запроса


В начало


Для кого написано это пособие?

Это учебное пособие является заключительной частью в серии из трех пособий, описывающих создание с помощью PHP приложения, поддерживающего workflow. Мы предполагаем, что у читателя есть базовые знания по PHP и желание познакомиться с более сложными темами, такими как объектно-ориентированное программирование, HTTP-аутентификация, обработка потоков данных, классы, объекты, описание и обработка исключений, дополнительные возможности для работы с форматом XML.

Базовые знания по PHP, на которые мы опираемся в этом учебном пособии, включают знание синтаксиса, умение работать с такими структурами как HTML-формы и базы данных, знакомство с форматом файлов XML. Все эти темы обсуждались в Части 1 и Части 2 этой серии. Дополнительную информацию по теме этого пособия можно найти в разделе Ресурсы.



В начало


Необходимые условия

Вам необходимо иметь Web-сервер, PHP и базу данных. Если у вас есть доступ к сети, в которой установлен сервер PHP V5 с доступом к базе данных MySQL, то вы можете им воспользоваться. В противном случае загрузите и установите следующие пакеты:

HTTP-сервер -- Вне зависимости от того, работаете ли вы под Windows®, Linux®, UNIX® или Mac OS X, вы имеете возможность использовать Apache HTTP-сервер. Можно использовать различные версии, но примеры HTTP-аутентификации в этой части пособия ориентированны на версии Apache V2.X. Загрузить HTTP-сервер можно со страницы Apache. Если вы работаете под Windows®, вы можете также использовать Windows® IIS.

PHP -- Вам необходим дистрибутив PHP. Во время написания этого пособия в ходу были версии PHP V4 и V5, но в примерах этой части используются новые возможности версии V5. Загрузить дистрибутив можно со страницы PHP.

База данных -- одной из тем этого учебника является получение доступа к базе данных. Соответственно, вам нужна та или иная база данных. Мы разбираем доступ к базе MySQL, поскольку именно эта база данных как правило используется вместе с PHP. Загрузите базу данных со страницы http://dev.mysql.com/downloads/index.html.



В начало



Страница 1 из 11  На предыдущую страницу

Изучаем PHP, Часть3: Аутентификация, работа с потоками данных, объекты и исключения

developerWorks

Уровень сложности: средний

Никлас Чейз, Независимый автор, Backstop Media
Тайлер Андерсон, Независимый автор, Stexar Corp.

16.01.2007

Перейти к учебному пособию

Из этого учебного пособия, последнего из трех в серии "Изучаем PHP", вы узнаете, как использовать PHP для создания простейшего приложения, поддерживающего workflow. В этой части рассматривается HTTP-аутентификация, доступ к документам посредством обработки потоков данных, создание объектов и обработка исключений.

В этом учебном пособии

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


Требуемый опыт

Базовые знания по PHP, на которые мы опираемся в этом учебном пособии, включают знание синтаксиса, умение работать с такими структурами как HTML-формы и базы данных, знакомство с форматом файлов XML. Убедитесь, что вы ознакомились с Частью 1 и Частью 2 этой серии.


Системные требования

Вам необходимо иметь Web-сервер, PHP и базу данных. Если у вас есть доступ к сети, в которой установлен сервер PHP V5 с доступом к базе данных MySQL, то вы можете им воспользоваться. В противном случае загрузите и установите следующие пакеты:

HTTP сервер
Вне зависимости от того, работаете ли вы под Windows®, Linux®, UNIX® или Mac OS X, вы имеете возможность использовать Apache HTTP-сервер. Можно использовать различные версии, но примеры HTTP-аутентификации в этой части пособия ориентированны на версии Apache V2.X. Загрузить HTTP-сервер можно со страницы Apache. Если вы работаете под Windows®, вы можете также использовать Windows® IIS.
PHP
Вам необходим дистрибутив PHP. Во время написания этого пособия в ходу были версии PHP V4 и V5, в своих примерах мы ориентировались на V5.
MySQL
Одной из тем этого учебника является получение доступа к базе данных. Соответственно, вам нужна та или иная база данных. Мы разбираем доступ к базе MySQL, поскольку именно эта база данных как правило используется вместе с PHP.


Форматы

html, pdf




В начало


IBM обладает всеми авторскими правами касательно информации, расположенной на developerWorks. Использование информации приведенной на этом ресурсе без явного письменного разрешения от IBM или первоначального автора запрещены. Если Вы желаете использовать информацию с developerWorks, пожалуйста воспользуйтесь регистрационной формой для того, чтобы связаться с нами запрос на использование материалов developerWorks Россия.
Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Отправить эту страницу по e-mail

Отправить эту страницу по e-mail

Опции документа, требующие включения JavaScript, не отображаются


Другие статьи из этой серии:
Изучаем PHP, Часть3


Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
На предыдущую страницу Страница 2 из 11  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Определим наши задачи и средства

Что уже было сделано

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

  • Страница регистрации новых пользователей. На этой странице с использованием HTML-формы пользователь системы может ввести регистрационное имя (оно должно быть уникальным), пароль и адрес электронной почты. Эти данные попадают на другую страницу нашей системы, проходят проверку, мы соединяемся с базой данных для того, чтобы убедиться в уникальности имени пользователя и записать в базу регистрационные данные.
  • Страница входа в систему для пользователей, которые уже имеют учетную запись. Имя пользователя и пароль проверяются с привлечением базы данных, если проверка прошла успешно, то для пользователя открывается сессия на сервере.
  • Простые элементы интерфейса. Возможности, которые предоставляет наш интерфейс, зависят от того, зарегистрировался ли пользователь в системе.
  • Страница загрузки, которая позволяет выбрать файл и отослать его на сервер из браузера. Служебная страница принимает файл и сохраняет его во временном каталоге на сервере, а также добавляет информацию о загруженном документе в XML-файл с использованием DOM (Document Object Model – Объектная модель документа).
  • Страница, на которой отображается информация о загруженных на сервер документах. Используя другой инструмент, SAX (Simple API for XML – Простой API для XML), мы написали функцию, которая читает информацию из XML-файла и отображает ее в форме таблицы на экране.

К концу второй части мы создали несложное, но работоспособное приложение, вы можете загрузить его тексты из "Изучаем PHP, Часть 2."



В начало


Каковы дальнейшие планы?

В заключительной части пособия мы планируем решить следующие задачи:

  • Добавить HTTP-аутентификацию, контролируемую Web-сервером. Мы сделаем доработки для нашей страницы регистрации, чтобы новые пользователи были зарегистрированы также и на Web-сервере.
  • К списку файлов, доступных на сервере, добавим ссылки, чтобы пользователь мог загрузить эти файлы. Для загрузки напишем новую функцию с использованием обработки потоков данных. Эта функция будет передавать браузеру документы из каталогов, к которым нет прямого доступа из сети.
  • Включить проверку адреса, с которого пришел запрос на загрузку документа. Для включения контроля загрузки будет использоваться тот факт, что перемещение файлов происходит посредством специальной программы, а не методом прямого доступа через HTTP-сервер.
  • Создать класс, который будет представлять документ, использовать объектно-ориентированные методы для доступа к файлу и его загрузки.
  • Добавить генерацию и обработку исключений для более точного решения проблем, которые могут возникнуть при работе приложения.
  • В XML-файл с описанием документов добавить информацию, которая позволит однозначно идентифицировать каждый файл. Использовать в форме для администратора checkbox'ы для отметки тех файлов, которые решено одобрить и открыть для публичного доступа.
  • Добавить в XML-файл ключевое поле для прямого доступа к элементам fileInfo и управления процессом приемки файлов.

Начнем с создания общей стартовой страницы для нашей системы..



В начало


Стартовая страница

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

<?php

include ("top.txt");
include ("scripts.txt");

display_files();

include ("bottom.txt");

?>

Первый оператор include() загружает интерфейс начальной части страницы и открывает сессию, второй загружает написанные нами до сих пор скрипты. Среди этих скриптов присутствует вызов функции display_files(), определенной выше в "Изучаем PHP, Часть 2", эта функция выводит на экран список всех документов, загруженных текущим пользователем или одобренных администратором системы. Последний оператор include() содержит закрывающие теги для формирования законченной HTML-страницы.

Сохраните этот файл в том же каталоге, где хранятся созданные вами ранее файлы. Например, вы можете сохранить его в головном каталоге для документов вашего сервера, если вы при запуске HTTP-сервера укажете адрес http://localhost/index.php, то увидите страницу, подобную приведенной ниже на Рисунке 1.

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


Рисунок 1. Базовая стартовая страница
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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



В начало



На предыдущую страницу Страница 2 из 11  На предыдущую страницу

Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
На предыдущую страницу Страница 3 из 11  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Использование HTTP-аутентификации

HTTP-аутентификация

До сих пор для регистрации в нашей системе пользователи вводили свой логин и пароль в HTML-форму и отсылали данные. Далее происходил контроль имени пользователя и пароля с использованием базы данных MySQL, если проверка проходила успешно, то для пользователя создавалась сессия на сервере и его имя записывалось в массив $_SESSION для использования в дальнейшем.

Эта процедура годилась для целей обучения, но в реальных системах такой подход может вызвать проблемы. Если наше приложение, поддерживающее workflow, является частью большой системы, то пользователь, возможно, уже прошел процедуру регистрации. С другой стороны, если пользователь зарегистрировался в нашей системе, то мы должны сохранить эту информацию для того, чтобы ему не пришлось заново вводить данные при входе в другие приложения. Системы, в которых пользователь вводит пароль один раз, называются Single Sign-On (Системы однократной регистрации).

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

Прежде всего, мы должны включить возможность HTTP-аутентификации на нашем сервере.



В начало


Включение HTTP-аутентификации

Следует иметь в виду, что настройка механизма аутентификации существенно зависит от сервера. В этом разделе мы будем рассматривать настройку аутентификации для сервера Apache 2.X, если вы используете другой сервер, то обратитесь за указаниями к документации. Если у вас возникнут трудности с настройкой HTTP-сервера, то вы можете просто пропустить этот раздел, наша система будет корректно работать и со старым типом аутентификации.

Для того чтобы система HTTP-аутентификации могла работать, должно быть известно, какой уровень безопасности установлен для того или иного каталога. Установить права доступа для конкретного каталога можно, сделав соответствующие изменения в главном конфигурационном файле сервера. Есть и другой путь, можно использовать файл .htaccess, который содержит инструкции для того каталога, в котором он размещен.

Рассмотрим пример. Допустим, вы хотите, чтобы к вашим документам имели доступ только те пользователи, которые прошли регистрацию. В том каталоге, в котором вы храните свои файлы, создайте специальный подкаталог loggedin. Если основной каталог /usr/local/apache2/htdocs, то надо создать каталог /usr/local/apache2/htdocs/loggedin.

Теперь мы должны сообщить серверу, что мы хотим ввести для этого каталога особые правила доступа, для этого в файл httpd.conf добавим следующие строки:

<Directory /usr/local/apache2/htdocs/loggedin>
AllowOverride AuthConfig
</Directory>

Разумеется, вы должны указать головной каталог в соответствии с настройками своего сервера.



В начало


Настройка аутентификации

Следующим шагом надо создать текстовый файл с именем .htaccess в каталоге loggedin и добавить в него следующие строки:

AuthName "Registered Users Only"
AuthType Basic
AuthUserFile /usr/local/apache2/password/.htpasswd
Require valid-user

Рассмотрим, что означают эти строки. За ключевым словом AuthName идет заголовок, который появится в окне для ввода имени пользователя и пароля. AuthType задает тип используемой аутентификации, в нашем случае тип будет Basic, то есть имя пользователя и пароль будут передаваться открытым текстом. AuthUserFile указывает на файл, в котором хранятся допустимые регистрационные данные. (Мы создадим этот файл чуть позже.) И наконец, директива Require определяет, кто именно может иметь доступ к этому каталогу. В нашем случае сказано, что доступ могут иметь все зарегистрированные пользователи, но вы можете также указать здесь группу или конкретного пользователя.

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

(Для Apache V2.0 надо выполнить последовательно команды <APACHE_HOME>/bin/apachectl stop и <APACHE_HOME>/bin/apachectl start.)

Теперь осталось создать файл с регистрационными данными.



В начало


Создание файла с регистрационными данными

Итак, нам нужен файл, содержащий данные, к которым сервер будет обращаться для проверки логина и пароля. Ниже, в разделе Добавление новых пользователей, мы рассмотрим работу с этим файлом посредством PHP, пока же создадим и заполним файл .htpasswd непосредственно.

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

Для того чтобы создать файл с логином и паролем, выполните команду (подставив свой каталог и имя пользователя):

htpasswd -c /usr/local/apache2/password/.htpasswd roadnick

Вы получите приглашения на ввод пароля и повтор пароля:

htpasswd -c /usr/local/apache2/password/.htpasswd roadnick
New password:
Re-type new password:
Adding password for user roadnick

Параметр команды -c означает, что мы просим создать новый файл. После выполнения команды появится файл, в котором буде записано нечто вроде:

roadnick:IpoRzCGnsQv.Y

Заметьте, что пароль зашифрован. Следует иметь это в виду при добавлении данных из приложения.

Теперь посмотрим, как это работает.



В начало


Вход в систему

Чтобы увидеть, как это работает, нам надо иметь доступ к файлу в защищенном каталоге. Переместите файлы uploadfile.php и uploadfile_action.php в каталог loggedin, а файл index.php скопируйте туда же с именем display_files.php.

Во всех трех файлах надо внести изменения в операторах include(), чтобы обеспечить правильный путь к включаемым файлам:

<?php

include ("../top.txt");
include ("../scripts.txt");

echo "Logged in user is ".$_SERVER['PHP_AUTH_USER'];

display_files();

include ("../bottom.txt");

?>

Откройте свой браузер, указав адрес http://localhost/loggedin/display_files.php, чтобы увидеть, как это работает. В момент вызова браузера имя пользователя и пароль не заданы, поэтому на экране появится окно для регистрации, которое можно видеть ниже, на Рисунке 2.


Рисунок 2. Окно для регистрации
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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



В начало


Использование регистрационных данных

Итак, мы видим привычную страницу, как на Рисунке 3, в верхней части которой есть сообщение о том, что пользователь зарегистрирован, и выведено имя пользователя. Однако прочее содержание страницы выглядит так, как если бы регистрация не произошла. Мы видим ссылки Register и Login, список файлов содержит только те, которые были уже одобрены администратором, но не содержат файлов, загруженных пользователем roadnick и имеющих состояние pending (то есть, ожидающих одобрения).


Рисунок 3. Пользователь зарегистрирован... почти
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

Проблема возникает потому, что имя пользователя хранится в элементе массива $_SERVER['PHP_AUTH_USER'], а не в $_SESSION["username"]. Конечно, мы можем пройти по тексту нашего приложения и сделать всюду замену одной переменной на другую, но это – масса работы и такой путь нас не привлекает.

Другой путь состоит в том, чтобы в начале сессии задать переменную $_SESSION["username"] на основе значения $_SERVER['PHP_AUTH_USER'] тогда приложение будет работать в точности так, как оно работало раньше. Давайте сделаем это в файле top.txt сразу после оператора открытия новой сессии или присоединения к уже существующей сессии:

<?
session_start();
if (isset($_SESSION["username"])){
//Do nothing
} elseif (isset($_SERVER['PHP_AUTH_USER'])) {
$_SESSION["username"] = $_SERVER['PHP_AUTH_USER'];
}


?>
<html>
<head>
<title>Workflow System</title>
</head>
<body>

Теперь единственный способ заставить браузер забыть имя пользователя –- это закрыть его. Однако, это не всегда удобно. Поэтому следует добавить к нашей системе опцию выхода, logout. (В рамках нашего пособия мы не будем разбирать процедуру выхода из системы, просто добавим опцию для полноты картины.)

Если имя пользователя не определено ни в массиве $_SESSION, ни в массиве $_SERVER, то это означает, что процедура регистрации не прошла и пользователь действительно не определен. Теперь, если пользователь зарегистрирован, мы увидим такую страницу, какая показана на Рисунке 4.


Рисунок 4. Пользователь зарегистрирован
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.



В начало


Изменения в интерфейсе

Прежде чем двигаться дальше, внесем небольшие изменения в файл top.txt. Заменим ссылку опции Login вместо старой страницы регистрации, login.php, используем новый файл, display_files.php, предназначенный для HTTP-аутентификации:

...
<tr>
<td width="30%" valign="top">
<h3>Navigation</h3>

<?php
if (isset($_SESSION["username"]) || isset($username)){
?>
<p>You are logged in as <?=$_SESSION["username"].$username?>. <!--
You can <a href="/logout.php">logout</a> to login as a different user.--></p>

<p><a href="/loggedin/uploadfile.php">Upload a file</a></p>
<p><a href="/loggedin/display_files.php">Display files</a></p>

<?php
} else {
?>
<p><a href="/registration.php">Register</a></p>
<p><a href="/loggedin/display_files.php">Login</a></p>
<?php
}
?>
</td>

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

Осталось только изменить процедуру регистрации нового пользователя.



В начало


Добавление новых пользователей

Итак, мы должны связать процедуру регистрации нового пользователя с файлом .htpasswd. Запись нового пользователя в файл надо сделать после того, как учетная запись включена в базу данных. Откройте файл registration_action.php и добавьте в него строки:

...
$passwords = $_POST["pword"];
$sql = "insert into users (username, email, password) values ('"
.$_POST["name"]."', '".$_POST["email"]
."', '".$passwords[0]."')";
$result = mysql_query($sql);

if ($result){
echo "It's entered!";

$pwdfile = '/usr/local/apache2/password/.htpasswd';
if (is_file($pwdfile)){
$opencode = "a";
} else {
$opencode = "w";
}
$fp = fopen($pwdfile, $opencode);
$pword_crypt = crypt($passwords[0]);
fwrite($fp, $_POST['name'].":".$pword_crypt."\n");
fclose($fp);

} else {
echo "There's been a problem: ".mysql_error();
}
} else {

echo "There is already a user with that name. Please try again. <br />";
...

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

Перед тем как открыть файл, мы проверили, существует ли он. Если файла нет, то мы открываем новый файл, а если он существует –- продолжаем добавлять строки в старый.

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

Проверьте, все ли в порядке. Для этого закройте браузер (имя пользователя и пароль будут очищены) и затем попробуйте открыть его с адресом http://localhost/index.php.

Нажмите ссылку Register и создайте новую учетную запись. Затем снова закройте браузер и попробуйте открыть защищенную страницу. Новый логин и пароль должны работать.



В начало



На предыдущую страницу Страница 3 из 11  На предыдущую страницу

Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
На предыдущую страницу Страница 4 из 11  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Потоки данных

Что такое потоки данных?

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

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

Способ доступа к файлам может быть различен в зависимости от того, каким образом они были сохранены. Доступ к файлу на локальном сервере отличается от доступа к удаленным ресурсам посредством HTTP или FTP.

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

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

<?php

print_r(stream_get_wrappers());

?>

Функция print_r() очень удобна для распечатки содержимого массива. Скорее всего, вы увидите следующий вывод:

Array
(
[0] => php
[1] => file
[2] => http
[3] => ftp
)

Таким образом, у вас есть простой путь сохранения файлов на удаленном Web-сервере и на FTP-сервере, при этом способ сохранения ничем не будет отличаться от способа сохранения файлов на локальном сервере, и весь код этого раздела будет работать. Единственное отличие будет в имени вызываемой оболочки.



В начало


Загрузка файлов с сервера

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

<?php

include ("../scripts.txt");

$filetype = $_GET['filetype'];
$filename = $_GET['file'];
$filepath = UPLOADEDFILES.$filename;

if($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$filetype);
print($file_contents);
}

?>

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

Эту строку мы можем передать браузеру, но при этом могут возникнуть проблемы. Поскольку браузер не знает, в какой форме отобразить переданные данные, то он будет отображать их просто как текст. Хорошо, если файл был текстовый, а что если это была картинка или сложный HTML-файл? Поэтому, прежде чем передавать содержимое файла, мы передадим браузеру заголовок header, который будет содержать информацию о типе файла (Content-type), например, image/jpeg.

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

Для того чтобы посмотреть как это работает, просто добавьте имя и тип файла к адресу URL:

http://localhost/loggedin/download_file.php
?file=timeone.jpg&filetype=image/jpeg

Откройте браузер с таким адресом (подберите вместо timeone.jpg подходящую картинку из своих запасов) и вы увидите результат, наподобие Рисунка 5.


Рисунок 5. Загрузка файла
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.



В начало


Добавим ссылки на файлы

Поскольку вся информация, необходимая для страницы загрузки, может быть добавлена в URL, достаточно добавить ссылки в список файлов, чтобы пользователи могли их загружать. При выводе на экран списка файлов, доступных для загрузки, мы использовали технологию SAX, то есть наш экранный вывод создавался при помощи обработчика содержания, точнее, класса с именем Content_Handler. Добавим следующий код к нашему классу в файле scripts.txt:

class Content_Handler{

private $available = false;
private $submittedBy = "";
private $status = "";

private $currentElement = "";

private $fileName = "";
private $fileSize = "";
private $fileType = "";

function start_element($parser, $name, $attrs){
...
}

function end_element($parser, $name){

if ($name == "workflow"){
echo "</table>";
}

if ($name == "fileInfo"){
echo "<tr><td><a href='download_file.php?file=".$this->fileName."
&filetype=".$this->fileType."'>
"
.$this->fileName."</a></td>".
"<td>".$this->submittedBy."</td>".
"<td>".$this->fileSize."</td>".
"<td>".$this->status."</td></tr>";

$this->fileName = "";
$this->submittedBy = "";
$this->fileSize = "";
$this->status = "";
$this->fileType = "";

$this->available = false;
}

$this->currentElement = "";

}

function chars($parser, $chars){

if ($this->available){
if ($this->currentElement == "fileName"){
$this->fileName = $this->fileName . $chars;
}
if ($this->currentElement == "fileType"){
$this->fileType = $this->fileType . $chars;
}
if ($this->currentElement == "size"){
$this->fileSize = $this->fileSize . $chars;
}
}

}
}

Во-первых, к той информации, которую мы собираем о каждом элементе fileInfo, мы добавили поле fileType, в которое и будет помещен тип файла.

Используя функцию chars(), мы сохраняем значение типа в нашу переменную и используем ее при выводе строки с информацией о файле. Обратите внимание на то, что имя файла теперь служит текстовым элементом ссылки в формате HTML на сам файл. Результат изменений на странице можно видеть на Рисунке 6.


Рисунок 6.Ссылки на файлы
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

Попробуйте нажать на ссылку и проверить, работает ли она.

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



В начало



На предыдущую страницу Страница 4 из 11  На предыдущую страницу

Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
На предыдущую страницу Страница 5 из 11  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Использование объектов

Что такое объект?

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

Центральной концепцией объектно-ориентированного программирования является объединение свойств и функций некоторого предмета в самодостаточную конструкцию, которая и называется объектом. Рассмотрим в качестве примера электрический чайник. Он имеет свойства, такие как цвет и максимальная температура, и функции, а именно, нагревание воды и отключение при закипании.

Если мы захотим описать чайник как объект, то должны определить его свойства или переменные (пусть цвет будет представлен переменной color, а максимальная температура -- переменной maximumTemperature) и функции или методы, в нашем случае есть два метода: heatWater() -- нагреть воду и turnOff() -- отключиться при закипании. Если вы будете писать программу взаимодействия с электрическим чайником, то в ней будет просто вызван метод чайника heatWater(), при этом вам не нужно думать о том, какие произойдут физические действия, поскольку метод есть составляющая самого объекта.

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

Надеемся, что вы сориентировались в объектно-ориентированном подходе, а сейчас нам предстоит подняться еще на один уровень абстракции. Дело в том, что для формального описания свойств и функций используется не объект, а класс объектов. Класс работает как образец или форма для создания объектов определенного типа. Об объектах говорят как об экземплярах данного класса.

Итак, перед нами стоит задача создать описание нашего объекта, создать класс.



В начало


Создание класса WFDocument

Для того чтобы работать с объектами, необходимо создать класс, содержащий описание переменных и методов. Мы могли бы поместить определение нашего класса в файл scripts.txt file, но, поскольку мы поставили перед собой задачу улучшить структуру нашего приложения, мы поместим его в отдельный файл, WFDocument.php, и запишем этот файл в основной каталог. Добавьте в этот файл следующий код:

<?php

include_once("scripts.txt");

class WFDocument {

function download($filename, $filetype) {

$filepath = UPLOADEDFILES.$filename;

if($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$filetype);
print($file_contents);
}
}
}

?>

Поскольку нам необходима константа UPLOADEDFILES, первым шагом мы включили файл scripts.txt. Затем приступили к созданию класса. Наш класс, WFDocument будет иметь только один метод, download(), совпадающий с функцией, которая была описана в файле download_file.php, за одним исключением: имя файла и тип файла будут передаваться методу как параметры, а не извлекаться непосредственно из массива $_GET.

Теперь мы готовы создать экземпляр класса, объект.



В начало


Создание объекта типа WFDocument

В Части 2 этой серии мы уже занимались созданием объектов определенного класса, когда работали с DOM, но тогда мы не стали давать объяснений по этому поводу. Разберем теперь этот процесс подробнее.

Откройте файл download_file.php и внесите следующие изменения:

<?php

include ("../WFDocument.php");

$filetype = $_GET['filetype'];
$filename = $_GET['file'];

$wfdocument = new WFDocument();
$wfdocument->download($filename, $filetype);


?>

Во-первых, мы заменили включаемый файл, вместо файла со скриптами мы включаем файл WFDocument.php, в котором содержится описание класса WFDocument. (Некоторые разработчики считают удобным собирать определение всех классов своей системы в один файл и включать этот файл, а некоторые -- создавать отдельный файл для каждого класса.)

Затем создается новый объект типа WFDocument, для этого используется ключевое слово new, для ссылок на этот объект будет использована переменная $wfdocument.

Получив ссылку на объект, мы можем вызывать его публичные методы, в нашем случае есть только один метод, download(), для вызова метода объекта используется оператор ->. Появление этого оператора можно интерпретировать как указание: "использовать метод (или свойство), принадлежащий данному объекту".

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



В начало


Создание свойств

Методы представляют собой только одну сторону объекта. Главная идея объекта состоит в том, что он содержит в себе всю информацию о себе самом. Для того чтобы наш объект был полноценным, он должен включать в себя в качестве свойств имя и тип файл, а не получать их как параметры метода download(). Включим в определение класса эти свойства:

<?php

include_once("../scripts.txt");

class WFDocument {

public $filename;
public $filetype;


function download() {

$filepath = UPLOADEDFILES.$this->filename;

if($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$this->filetype);
print($file_contents);
}
}
}

?>

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

Для того чтобы обращаться к свойству объекта, надо знать, какому объекту свойство принадлежит. Если обращение происходит извне, то мы указываем имя объекта, а если изнутри, то можно использовать ключевое слово $this, которое указывает на то, что свойство принадлежит данному объекту. Таким образом, конструкция $this->filename ссылается на свойство filename того объекта, в котором содержится этот код.

Теперь разберем вопрос, как приписать значения свойствам объекта.



В начало


Значения свойств объектов

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

<?php

include ("../WFDocument.php");

$filetype = $_GET['filetype'];
$filename = $_GET['file'];

$wfdocument = new WFDocument();
$wfdocument->filename = $filename;
$wfdocument->filetype = $filetype;

$wfdocument->download();

?>

Итак, для того чтобы присвоить значение свойству объекта, мы создаем этот объект и затем ссылаемся на его свойство, используя его имя, $wfdocument, оператор -> и имя свойства, обратите внимание на то, что знак $ перед именем свойства не ставится. После того как значение свойства задано, мы можем использовать его внутри объекта, поэтому мы вызываем метод нашего объекта download() без аргументов.

Существует и другой способ определения свойств объекта, мы рассмотрим его ниже.



В начало


Сокрытие свойств

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

<?php

include_once("../scripts.txt");

class WFDocument {

private $filename;
private $filetype;

function setFilename($newFilename){
$this->filename = $newFilename;
}
function getFilename(){
return $this->filename;
}

function setFiletype($newFiletype){
$this->filetype = $newFiletype;
}
function getFiletype(){
return $this->filetype;
}

function download() {

$filepath = UPLOADEDFILES.$this-> getFilename()

if($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$this->getFiletype())
print($file_contents);
}
}
}

?>

Прежде всего, мы снабдили наши свойства модификатором private, это означает, то они будут доступны только внутри того класса, в котором определены. Тем не менее, нам необходимо присвоить нашим свойствам значения и затем использовать эти значения, поэтому мы описали четыре метода: getFilename(), setFilename(), getFiletype() и setFiletype(). Обратите внимание, что использование методов внутри функции download() ничем не отличается от использования свойств.

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



В начало


Обращение к скрытым свойствам

После того как мы сделали свои свойства скрытыми, нам необходимо внести изменения в файл download_file.php:

<?php

include ("../WFDocument.php");

$filetype = $_GET['filetype'];
$filename = $_GET['file'];

$wfdocument = new WFDocument();
$wfdocument->setFilename($filename);
$wfdocument->setFiletype($filetype);
$wfdocument->download();

?>

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



В начало


Создание конструктора

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

...
function getFiletype(){
return $this->filetype;
}

function __construct(){
echo "Creating new WFDocument";
}


function download() {

$filepath = UPLOADEDFILES.$this->filename;
...

Если вы запустите этот скрипт, то результат будет не слишком хороший, это можно видеть на Рисунке 7. Ошибка возникла потому, что скрипт пытается вывести на экран текст (Creating new WFDocument) до того, как выведен заголовок страницы, header.


Рисунок 7. Ошибка при запуске скрипта
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

Мы видим, что наш конструктор отработал, хотя мы явно и не вызывали метод __construct(), он был вызван в момент создания нового объекта. Теперь давайте используем конструктор для передачи объекту полезной информации.



В начало


Присваивание значений при создании объекта

Конструкторы используются чаще всего именно для задания начальных значений свойств объекта. Добавим в наш класс WFDocument конструктор, который будет задавать значения свойств filename и filetype при создании нового экземпляра этого класса:

...
function getFiletype(){
return $this->filetype;
}

function __construct($filename = "", $filetype = ""){
$this->setFilename($filename);
$this->setFiletype($filetype);

}

function download() {

$filepath = UPLOADEDFILES.$this->filename;

...

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

Но как вызывается функция __construct()?



В начало


Создание объекта: вызов конструктора

Нет необходимости вызывать конструктор непосредственно, поскольку он неявно вызывается при создании любого нового объекта. Таким образом, именно в момент создания нового экземпляра класса $ WFDocument мы и должны передать наши начальные значения:

<?php

include ("../WFDocument.php");

$filetype = $_GET['filetype'];
$filename = $_GET['file'];

$wfdocument = new WFDocument($filename, $filetype);
$wfdocument->download();

?>

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



В начало



На предыдущую страницу Страница 5 из 11  На предыдущую страницу

Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
На предыдущую страницу Страница 6 из 11  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Обработка исключений

Встроенные исключения

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

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

<?php

include_once("../scripts.txt");

class WFDocument {
...
function download() {

$filepath = UPLOADEDFILES.$this->filename;

try {

if(file_exists($filepath)){

if ($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$this->filetype);
print($file_contents);
}
} else {
throw new Exception ("File '".$filepath."' does not exist.");
}

} catch (Exception $e) {

echo "<p style='color: red'>".$e->getMessage()."</p>";

}
}
}

?>

Прежде всего заметим, что исключения не происходят сами по себе, они генерируются (или возбуждаются). Для генерации исключения используется ключевое слово throw (основное его значение, "бросать", редко используется в документации на русском языке, он англичане бросают-ловят свои исключения). Генерация исключения должна быть включена в блок try, а возникшее исключение затем перехватывается и его обработка его происходит в одном из блоков catch. В блок try помещается весь код, который должен быть выполнен. Если что-то идет не так, в нашем случае -- не существует файла с указанным именем, то в этом блоке генерируется исключение и PHP переходит к выполнению блока обработки исключения, catch.

Исключение обладает известным набором свойств, среди них номер строки и имя файла, в которых сгенерировано исключение, и сообщение. Как правило, текст сообщения задается при генерации исключения, как вы могли видеть выше. Для получения сообщения в блоке обработки исключения используется метод getMessage() самого исключения $e. Например, если вы попытаетесь загрузить с сервера файл, который не существует, вы увидите сообщение, выделенное красным цветом на Рисунке 8.


Рисунок 8. Результат обработки базового исключения
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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



В начало


Создание пользовательских исключений

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

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

class NoFileExistsException extends Exception {

public function informativeMessage(){
$message = "The file, '".$this->getMessage()."', called on line ".
$this->getLine()." of ".$this->getFile().", does not exist.";
return $message;
}

}

(Для простоты мы включили код нового класса в файл WFDocument.php, но он может быть расположен и в другом месте.)

Итак, описание нового класса, NoFileExistsException, содержит в себе только один метод: informativeMessage(). Но, поскольку этот класс является расширением класса Exception, все все публичные методы и свойства объекта Exception также доступны и для объектов нашего класса.

Например, вы могли заметить, что внутри функции informativeMessage() вызываются методы getLine() и getFile(), хотя они тут и не определены. Это возможно потому, что они определены в базовом классе, Exception.

Теперь перейдем к обработке исключений.



В начало


Обработка пользовательских исключений

Генерация пользовательского исключения ничем не отличается от генерации базового:

function download() {

$filepath = UPLOADEDFILES.$this->filename;

try {

if(file_exists($filepath)){
if ($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$this->filetype);
print($file_contents);
}
} else {
throw new NoFileExistsException ($filepath);
}

} catch (NoFileExistsException $e) {

echo "<p style='color: red'>".$e->informativeMessage()."</p>";

}
}

Обратите внимание на то, что при генерации исключения мы передали только переменную $filepath , но несмотря на это, получили полное сообщение об ошибке, как можно видеть ниже на Рисунке 9.


Рисунок 9. Пользовательское исключение
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.



В начало


Обработка нескольких исключений

Пользовательские классы исключений нужны для того, чтобы иметь возможность точнее распознавать ошибочную ситуацию. Часто в одном фрагменте кода генерируется несколько видов исключений, тогда мы будем иметь один блок try и несколько блоков catch. Давайте рассмотрим пример:

...
function download() {

$filepath = UPLOADEDFILES.$this->filename;

try {

if(file_exists($filepath)){
if ($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$this->filetype);
print($file_contents);
} else {
throw new Exception ("Cannot open file ".$filepath);
}

} else {
throw new NoFileExistsException ($filepath);
}

} catch (NoFileExistsException $e) {

echo "<p style='color: red'>".$e->informativeMessage()."</p>";

} catch (Exception $e){

echo "<p style='color: red'>".$e->getMessage()."</p>";
}
}
}

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

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

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



В начало


Распространение исключений

Способность исключений распространяться тесно связана с механизмом наследования классов. Дело в том, что всякий объект, который является экземпляром класса-потомка, может трактоваться также и как экземпляр базового класса. Например, вы можете сгенерировать исключение NoFileExistsException, а перехватить его как общее, Exception:

...
function download() {

$filepath = UPLOADEDFILES.$this->filename;

try {

if(file_exists($filepath)){
if ($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$this->filetype);
print($file_contents);
} else {
throw new Exception ("Cannot open file ".$filepath);
}
} else {
throw new NoFileExistsException ($filepath);
}

} catch (Exception $e){

echo "<p style='color: red'>".$e->getMessage()."</p>";
}
}
}

Если возбуждено исключение, то PHP двигается вниз по списку блоков catch до тех пор, пока не встретит первый, способный обработать данный тип исключение. В нашем случае блок только один и он обработает наше исключение, как показано ниже на Рисунке 10. Дело в том, что все исключения являются потомками встроенного класса Exception, и поэтому могут рассматриваться как его экземпляры.


Рисунок 10 Распространение исключений
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.



В начало



На предыдущую страницу Страница 6 из 11  На предыдущую страницу

Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
На предыдущую страницу Страница 6 из 11  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Обработка исключений

Встроенные исключения

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

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

<?php

include_once("../scripts.txt");

class WFDocument {
...
function download() {

$filepath = UPLOADEDFILES.$this->filename;

try {

if(file_exists($filepath)){

if ($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$this->filetype);
print($file_contents);
}
} else {
throw new Exception ("File '".$filepath."' does not exist.");
}

} catch (Exception $e) {

echo "<p style='color: red'>".$e->getMessage()."</p>";

}
}
}

?>

Прежде всего заметим, что исключения не происходят сами по себе, они генерируются (или возбуждаются). Для генерации исключения используется ключевое слово throw (основное его значение, "бросать", редко используется в документации на русском языке, он англичане бросают-ловят свои исключения). Генерация исключения должна быть включена в блок try, а возникшее исключение затем перехватывается и его обработка его происходит в одном из блоков catch. В блок try помещается весь код, который должен быть выполнен. Если что-то идет не так, в нашем случае -- не существует файла с указанным именем, то в этом блоке генерируется исключение и PHP переходит к выполнению блока обработки исключения, catch.

Исключение обладает известным набором свойств, среди них номер строки и имя файла, в которых сгенерировано исключение, и сообщение. Как правило, текст сообщения задается при генерации исключения, как вы могли видеть выше. Для получения сообщения в блоке обработки исключения используется метод getMessage() самого исключения $e. Например, если вы попытаетесь загрузить с сервера файл, который не существует, вы увидите сообщение, выделенное красным цветом на Рисунке 8.


Рисунок 8. Результат обработки базового исключения
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.

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



В начало


Создание пользовательских исключений

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

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

class NoFileExistsException extends Exception {

public function informativeMessage(){
$message = "The file, '".$this->getMessage()."', called on line ".
$this->getLine()." of ".$this->getFile().", does not exist.";
return $message;
}

}

(Для простоты мы включили код нового класса в файл WFDocument.php, но он может быть расположен и в другом месте.)

Итак, описание нового класса, NoFileExistsException, содержит в себе только один метод: informativeMessage(). Но, поскольку этот класс является расширением класса Exception, все все публичные методы и свойства объекта Exception также доступны и для объектов нашего класса.

Например, вы могли заметить, что внутри функции informativeMessage() вызываются методы getLine() и getFile(), хотя они тут и не определены. Это возможно потому, что они определены в базовом классе, Exception.

Теперь перейдем к обработке исключений.



В начало


Обработка пользовательских исключений

Генерация пользовательского исключения ничем не отличается от генерации базового:

function download() {

$filepath = UPLOADEDFILES.$this->filename;

try {

if(file_exists($filepath)){
if ($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$this->filetype);
print($file_contents);
}
} else {
throw new NoFileExistsException ($filepath);
}

} catch (NoFileExistsException $e) {

echo "<p style='color: red'>".$e->informativeMessage()."</p>";

}
}

Обратите внимание на то, что при генерации исключения мы передали только переменную $filepath , но несмотря на это, получили полное сообщение об ошибке, как можно видеть ниже на Рисунке 9.


Рисунок 9. Пользовательское исключение
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.



В начало


Обработка нескольких исключений

Пользовательские классы исключений нужны для того, чтобы иметь возможность точнее распознавать ошибочную ситуацию. Часто в одном фрагменте кода генерируется несколько видов исключений, тогда мы будем иметь один блок try и несколько блоков catch. Давайте рассмотрим пример:

...
function download() {

$filepath = UPLOADEDFILES.$this->filename;

try {

if(file_exists($filepath)){
if ($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$this->filetype);
print($file_contents);
} else {
throw new Exception ("Cannot open file ".$filepath);
}

} else {
throw new NoFileExistsException ($filepath);
}

} catch (NoFileExistsException $e) {

echo "<p style='color: red'>".$e->informativeMessage()."</p>";

} catch (Exception $e){

echo "<p style='color: red'>".$e->getMessage()."</p>";
}
}
}

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

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

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



В начало


Распространение исключений

Способность исключений распространяться тесно связана с механизмом наследования классов. Дело в том, что всякий объект, который является экземпляром класса-потомка, может трактоваться также и как экземпляр базового класса. Например, вы можете сгенерировать исключение NoFileExistsException, а перехватить его как общее, Exception:

...
function download() {

$filepath = UPLOADEDFILES.$this->filename;

try {

if(file_exists($filepath)){
if ($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$this->filetype);
print($file_contents);
} else {
throw new Exception ("Cannot open file ".$filepath);
}
} else {
throw new NoFileExistsException ($filepath);
}

} catch (Exception $e){

echo "<p style='color: red'>".$e->getMessage()."</p>";
}
}
}

Если возбуждено исключение, то PHP двигается вниз по списку блоков catch до тех пор, пока не встретит первый, способный обработать данный тип исключение. В нашем случае блок только один и он обработает наше исключение, как показано ниже на Рисунке 10. Дело в том, что все исключения являются потомками встроенного класса Exception, и поэтому могут рассматриваться как его экземпляры.


Рисунок 10 Распространение исключений
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.



В начало



На предыдущую страницу Страница 6 из 11  На предыдущую страницу

Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
На предыдущую страницу Страница 7 из 11  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Собираем все вместе

Что нам осталось сделать

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

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

Начнем с создания класса Counter для хранения уникального номера файла.



В начало


Идентификация документов

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

Для этого мы создадим новый класс, Counter, при помощи которого будет генерироваться уникальный ключ для каждого файла. Затем мы добавим новое ключевое поле в XML-файл, который хранит информацию о документах, и это позволит нам непосредственно обращаться к нужному элементу fileInfo. Сначала создадим определение нового класса, Counter. Вы можете поместить его, например, в файл scripts.txt:

class Counter{

function getNextId(){
$filename = "/usr/local/apache2/htdocs/counter.txt";
$handle = fopen($filename, "r+");
$contents = fread($handle, filesize($filename));

$nextid = $contents + 1;
echo $nextid;
rewind($handle);
fwrite($handle, $nextid);
fclose($handle);

return $nextid;
}

}

У этого класса есть один метод, getNextId(), который открывает уже существующий файл, counter.txt, читает его содержимое, которое должно быть числом, и увеличивает это число на 1, именно это, увеличенное, значение и будет возвращаться методом. Затем указатель перемещается в начало файла, новое значение записывается на место старого и файл закрывается. Прежде чем начинать работать с этим классом, нужно создать указанный файл и записать в него значение равное 0.

Теперь мы готовы добавить ключевое поле в XML-файл.



В начало


Ключевое поле в XML-файле

Как вы помните, в "Изучаем PHP, Часть 2" мы создали XML-файл с информацией о загруженных на сервер документах. Теперь мы хотим добавить к элементу fileInfo атрибут ID, который будет ключевым полем и позволит быстро и однозначно находить в XML-файле нужный элемент. Включите в код функции save_document_info() следующие строки:

function save_document_info($fileInfo){

$xmlfile = UPLOADEDFILES."docinfo.xml";
...
$filename = $fileInfo['name'];
$filetype = $fileInfo['type'];
$filesize = $fileInfo['size'];

$fileInfo = $doc->createElement("fileInfo");

$counter = new Counter();
$fileInfo->setAttribute("id", "_".$counter->getNextId());


$fileInfo->setAttribute("status", "pending");

$fileInfo->setAttribute("submittedBy", getUsername());
...
$doc->save($xmlfile);

}

При загрузке нового документа на сервер и записи информации о нем в XML-файл будет создан новый объект типа Counter, вызван его метод getNextId() и полученный уникальный номер записан в атрибут id элемента fileInfo. Знак подчеркивания перед номером поставлен потому, что этот элемент будет иметь тип ID, значение которого не может начинаться с цифры.

В результате в XML-файл будет записана следующая информация (для наглядности в текст добавлены пробелы):

<fileInfo 
id="_13" status="pending" submittedBy="roadnick">
<approvedBy/>
<fileName>timeone.jpg</fileName>
<location>/var/www/hidden/</location>
<fileType>image/jpeg</fileType>
<size>2020</size>
</fileInfo>

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

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



В начало


Администраторы системы

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

alter table users add status varchar(10) default 'USER';
update users set status = 'USER';
update users set status = 'ADMIN' where id=3;

Первая команда добавляет новое поле, status, к таблице пользователей. Для вновь создаваемых пользователей системы это поле будет заполняться «по умолчанию» значением USER . Вторая команда присваивает всем уже зарегистрированным в системе пользователям статус USER, а третья изменяет это значение на ADMIN для одного конкретного пользователя (подберите разумный id для администратора в своей системе).

Теперь напишем функцию, которая возвращает статус текущего пользователя:

function getUserStatus(){
$username = $_SESSION["username"];
db_connect();
$sql = "select * from users where username='".$username."'";

$result = mysql_query($sql);
$row = mysql_fetch_array($result);

$status = "";

if ($row) {
$status = $row["status"];
} else {
$status = "NONE";
}

mysql_close();

return $status;

}

Имя текущего пользователя мы берем из массива $_SESSION, затем устанавливаем соединение с базой данных при помощи функции, которую мы написали в первой части нашего пособия, db_connect(). Далее мы формируем строку с SQL-запросом и выполняем этот запрос.

Если в результате запроса была получена строка данных (в нашем случае может быть только одна строка), то переменной $status присваивается значение статуса из строки таблицы, в противном случае мы присваиваем ей значение NONE. Затем соединение с базой закрывается и функция возвращает значение статуса.

Поместите эту функцию в файл scripts.txt, чтобы она была доступна при работе со списком файлов.



В начало


Процедура одобрения файлов: форма

Сделаем дополнения в нашу форму. Если текущий пользователь является администратором системы, ему надо дать возможность изменить статус документа. Для этого внесем следующие изменения в класс Content_Handler:

class Content_Handler{

private $available = false;
private $submittedBy = "";
private $status = "";

private $currentElement = "";

private $fileId = "";
private $fileName = "";
private $fileSize = "";
private $fileType = "";

private $userStatus = "";

function start_element($parser, $name, $attrs){


if ($name == "workflow"){

$this->userStatus = getUserStatus($_SESSION["username"]);

if ($this->userStatus == "ADMIN"){
echo "<form action='approve_action.php' method='POST'>";
}

echo "<h3>Available files</h3>";
echo "<table width='100%' border='0'><tr>".
"<th>File Name</th><th>Submitted By</th>".
"<th>Size</th><th>Status</th>";
if ($this->userStatus == "ADMIN"){
echo "<th>Approve</th>";
}
echo "</tr>";
}

if ($name == "fileInfo"){
if ($attrs['status'] == "approved" ||
$attrs['submittedBy'] == $this->username){
$this->available = true;
}
if ($this->available){
$this->submittedBy = $attrs['submittedBy'];
$this->status = $attrs['status'];
$this->fileId = $attrs['id'];
}
}

$this->currentElement = $name;

}

function end_element($parser, $name){

if ($name == "workflow"){
echo "</table>";

if ($this->userStatus == "ADMIN"){

echo "<input type='submit' value='Approve Checked Files' />";

echo "</form>";
}

}

if ($name == "fileInfo"){
echo "<tr>";
echo "<td><a href='download_file.php?file=".
$this->fileName."&filetype=".
$this->fileType."'>".
$this->fileName."</a></td>".
"<td>".$this->submittedBy."</td>".
"<td>".$this->fileSize."</td>".
"<td>".$this->status."</td><td>";

if ($this->userStatus == "ADMIN"){
if ($this->status == "pending") {
echo "<input type='checkbox' name='toapprove[]' value='".
$this->fileId."' checked='checked' />";
}
}

echo "</td></tr>";

$this->fileId = "";
$this->fileName = "";
$this->submittedBy = "";
$this->fileSize = "";
$this->status = "";
$this->fileType = "";

$this->available = false;
}

$this->currentElement = "";

}

function chars($parser, $chars){
...
}
}

Итак, мы определили два новых свойства: $fileId и $userStatus. Статус пользователя определяется один раз, при открытии головного элемента документа, workflow. Если пользователь является администратором, то для него добавляется элемент form, который содержит ссылку на соответствующую страницу, и столбец таблицы с заголовком Approve.

Форма, которая была открыта при обнаружении парсером начала документа, закрывается при обнаружении конца элемента workflow.

checkbox выводится в последнем столбце таблицы для тех документов, статус которых pending, поскольку у нас может быть несколько входов с одним и тем же именем, то поле представлено массивом toapprove[].

Результат нашей работы с дополнительной кнопкой и checkbox'ами можно видеть ниже на Рисунке 11.


Рисунок 11. Форма для одобрения файлов
XML error: The image is not displayed because the width is greater than the maximum of 500 pixels. Please decrease the image width.



В начало


Назначение ID

Итак, мы добавили в наш XML-файл атрибут id для элемента fileInfo, теперь мы должны сделать еще один шаг. В отличие от документов в формате HTML, в XML-документах недостаточно просто назвать атрибут "id" для того, чтобы он начал работать как ключ. Мы должны сопоставить документу некоторую схему, которая будет определять свойства нашего атрибута (обратите внимание, речь не идет о XML Schema -- стандарте описания структуры XML документа). Для этой цели мы будем использовать формат DTD (Document Type Definition – Определение типа документа). Прежде всего, добавим в наш документ ссылку на файл DTD:

function save_document_info($fileInfo){

$xmlfile = UPLOADEDFILES."docinfo.xml";

if(is_file($xmlfile)){
$doc = DOMDocument::load($xmlfile);
$workflowElements = $doc->getElementsByTagName("workflow");
$root = $workflowElements->item(0);

$statistics = $root->getElementsByTagName("statistics")->item(0);
$total = $statistics->getAttribute("total");
$statistics->setAttribute("total", $total + 1);

} else{

$domImp = new DOMImplementation;
$dtd = $domImp->createDocumentType('workflow', '', 'workflow.dtd');

$doc = $domImp->createDocument("", "", $dtd);

$root = $doc->createElement('workflow');
$doc->appendChild($root);

$statistics = $doc->createElement("statistics");
$statistics->setAttribute("total", "1");
$statistics->setAttribute("approved", "0");
$root->appendChild($statistics);
}
...
}

Вместо того чтобы создавать документ прямо как экземпляр класса DOMDocument, мы создаем сначала объект DOMImplementation, затем используем его метод DcreateDocumentType() для создания объекта типа DTD. Наконец, создаем новый документ, используя созданную ранее схему DTD.

Если вы удалите старый файл docinfo.xml и загрузите на сервер новый документ, то в файле docinfo.xml появится следующая информация:

<?xml version="1.0"?>
<!DOCTYPE workflow SYSTEM "workflow.dtd">
<workflow><statistics total="3" approved="0"/>
...

Теперь мы должны создать файл схемы workflow.dtd.



В начало


Схемы DTD

Описание технологии работы со схемами для формата XML-выходит за рамки данного пособия, мы ограничимся тем, что просто создадим файл DTD, в котором будет определена структура файла docinfo.xml. Итак, откройте новый файл с именем workflow.dtd и сохраните его в том же каталоге, что и docinfo.xml. Добавьте в него следующий текст:

<!ELEMENT workflow (statistics, fileInfo*) >
<!ELEMENT statistics EMPTY>
<!ATTLIST statistics total CDATA #IMPLIED
approved CDATA #IMPLIED >
<!ELEMENT fileInfo (approvedBy, fileName, location, fileType, size)>
<!ATTLIST fileInfo id ID #IMPLIED>
<!ATTLIST fileInfo status CDATA #IMPLIED>
<!ATTLIST fileInfo submittedBy CDATA #IMPLIED>
<!ELEMENT approvedBy (#PCDATA)>
<!ELEMENT fileName (#PCDATA)>
<!ELEMENT location (#PCDATA)>
<!ELEMENT fileType (#PCDATA)>
<!ELEMENT size (#PCDATA)>

Строки файла DTD содержат описание элементов нашего документа и содержимое каждого элемента. Элемент workflow должен иметь в точности одного элемента-потомка типа statistics и некоторое (возможно, нулевое) число элементов-потомков типа fileInfo.

Сам элемент statistics описан как пустой, но имеющий два необязательных атрибута: total и approved, оба они имеют тип "строка".

Следующим идет описание элемента fileInfo, его атрибут id и будет необходимым нам ключом для поиска нужного элемента, поэтому его тип определен как ID.



В начало


Процедура одобрения файлов: запись данных в XML-файл

Страница для обработки значений из checkbox, approve_action.php, на которую ссылается наша форма, будет очень простой:

<?php

include "../scripts.txt";

$allApprovals = $_POST["toapprove"];
foreach ($allApprovals as $thisFileId) {
approveFile($thisFileId);
}
echo "Files approved.";

?>

Для каждого checkbox'а просто вызывается функция approveFile() поместим эту функцию в файл scripts.txt:

function approveFile($fileId){

$xmlfile = UPLOADEDFILES."docinfo.xml";

$doc = new DOMDocument();
$doc->validateOnParse = true;
$doc->load($xmlfile);

$statisticsElements = $doc->getElementsByTagName("statistics");
$statistics = $statisticsElements->item(0);

$approved = $statistics->getAttribute("approved");
$statistics->setAttribute("approved", $approved+1);

$thisFile = $doc->getElementById($fileId);
$thisFile->setAttribute("status", "approved");

$approvedByElements = $thisFile->getElementsByTagName("approvedBy");
$approvedByElement = $approvedByElements->item(0);
$approvedByElement->appendChild($doc->createTextNode($_SESSION["username"]));

$doc->save($xmlfile);

}

Прежде чем загрузить документ, мы устанавливаем значения свойства validateOnParse равным TRUE, это служит указанием парсеру, что документ должен быть проверен на соответствие схеме DTD. Благодаря этому атрибут id распознается как ключевой. После того как файл загружен, мы получаем ссылку на элемент statistics, соответственно, мы можем изменять значение его атрибутов при выполнении процедуры одобрения новых файлов. Вследствие того, что атрибут id описан как ключ, то есть, имеет тип ID, мы можем использовать метод getElementById() для доступа к конкретному элементу fileInfo и изменения его атрибута status.

И, наконец, нам нужна ссылка на элемент-потомок approvedBy текущего элемента, получив эту ссылку, мы добавляем новый узел типа Text и помещаем в него имя администратора, принявшего данный файл.

Последним оператором мы сохраняем наш XML-файл.

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



В начало


Проверка адреса при загрузке

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

Организуем контроль путем создания нового исключения. Добавим код в файл WFDocument.php:

<?php

include_once("../scripts.txt");

class NoFileExistsException extends Exception {

public function informativeMessage(){
$message = "The file, '".$this->getMessage()."', called on line ".
$this->getLine()." of ".$this->getFile().", does not exist.";
return $message;
}

}

class ImproperRequestException extends Exception {

public function logDownloadAttempt(){
//Additional code here
echo "Notifying administrator ...";
}

}

class WFDocument {

private $filename;
private $filetype;

function setFilename($newFilename){
$this->filename = $newFilename;
}
function getFilename(){
return $this->filename;
}

function setFiletype($newFiletype){
$this->filetype = $newFiletype;
}
function getFiletype(){
return $this->filetype;
}

function __construct($filename = "", $filetype = ""){
$this->setFilename($filename);
$this->setFiletype($filetype);
}

function download() {

$filepath = UPLOADEDFILES.$this->filename;

try {

$referer = $_SERVER['HTTP_REFERER'];
$noprotocol = substr($referer, 7, strlen($referer));
$host = substr($noprotocol, 0, strpos($noprotocol, "/"));
if ( $host != 'boxersrevenge' &&
$host != 'localhost'){
throw new ImproperRequestException("Remote access not allowed.
Files must be accessed from the intranet.");
}

if(file_exists($filepath)){
if ($stream = fopen($filepath, "rb")){
$file_contents = stream_get_contents($stream);
header("Content-type: ".$this->filetype);
print($file_contents);
} else {
throw new Exception ("Cannot open file ".$filepath);
}
} else {
throw new NoFileExistsException ($filepath);
}
} catch (ImproperRequestException $e){

echo "<p style='color: red'>".$e->getMessage()."</p>";
$e->logDownloadAttempt();

} catch (Exception $e){

echo "<p style='color: red'>".$e->getMessage()."</p>";

}
}
}

?>

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

Далее мы начинаем обрабатывать переменную HTTP_REFERER. Эта переменная, как правило, пересылается вместе с запросом и содержит адрес той страницы, с которой был сделан запрос. Например, если вы сделаете запрос из своего блога к ресурсам developerWorks, то в журналах сервера IBM появится запись адреса URL вашего блога именно в HTTP_REFERER.

Нас интересует имя сервера, с которого пришел запрос, поэтому мы отрезаем название протокола в начале адреса "http://" и сохраняем текст до первого символа "/". Полученная строка и будет содержать имя сервера.

Если запрос был внешний, то он имя сервера будет иметь вид, подобный boxersrevenge.nicholaschase.com. Но мы пропускаем запросы только для сервера с именем boxersrevenge или localhost, поэтому в случае внешнего запроса будет сгенерировано исключение ImproperRequestException, затем это исключение будет перехвачено и обработано в соответствующем блоке.

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



В начало



На предыдущую страницу Страница 7 из 11  На предыдущую страницу

Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
На предыдущую страницу Страница 8 из 11  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Подведем итоги

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

  • Использование HTTP-аутентификации
  • Перемещение файла с использованием потока данных
  • Создание классов и объектов
  • Методы и свойства объекта
  • Конструкторы объектов
  • Использование исключений
  • Создание пользовательских исключений
  • Использование ID атрибутов XML
  • Обеспечение контроля доступа с использованием информации об источнике запроса


В начало



На предыдущую страницу Страница 8 из 11  На предыдущую страницу

Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
На предыдущую страницу Страница 9 из 11  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Ресурсы

Научиться

Получить продукты и технологии
  • Разработайте ваш следующий проект с открытым исходным кодом с использованием пробного программного обеспечения IMB, которое можно загрузить со страницы IBM trial software, или получить на DVD.


Обсудить

Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
На предыдущую страницу Страница 10 из 11  На предыдущую страницу

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Об авторах

Никлас Чейз (Nicholas Chase) участвовал в разработке Web-сайтов для таких компаний, как Lucent Technologies, Sun Microsystems, Oracle и Tampa Bay Buccaneers. Ник успел побывать школьным учителем физики, редактором электронного научно-фантастического журнала, инженером в области мультимедиа, инструктором по Oracle и главным инженером в интерактивной коммуникационной компании. Он является автором нескольких книг, в том числе XML Primer Plus (Sams).


Тайлер Андерсон (Tyler Anderson) прежде работал в DPMG.com, SEO-компании, для которой он писал программное обеспечение. Получил диплом по вычислительной технике в Brigham Young University в 2004 и степень магистра наук по вычислительной технике в декабре 2005, тоже в Brigham Young University. В настоящее время работает инженером в компании Stexar Corp., расположенной в Beaverton, Oregon. Вы можете связаться с ним по адресу tyleranderson5@yahoo.com.


Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
На предыдущую страницу Страница 11 из 11 

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить

Выскажите мнение об этом учебном пособии


Пожалуйста, найдите минутку и заполните форму, чтобы повысить уровень сервиса.



Да Нет Не знаю




1 2 3 4 5

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

Изучаем PHP: Часть3. Аутентификация, работа с потоками данных, объекты и исключения

developerWorks
На предыдущую страницу Страница 11 из 11 

Опции документа
Для печати установите альбомную (landscape) ориентацию

Распечатать эту страницу

Обсудить

Выскажите мнение об этом учебном пособии


Пожалуйста, найдите минутку и заполните форму, чтобы повысить уровень сервиса.



Да Нет Не знаю




1 2 3 4 5

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