документы - 430808.ru

advertisement
Версия для печати
Урок 1. Начала...
Первые 90 процентов работы занимают
10 процентов времени, а последние 10 процентов оставшиеся 90 процентов времени.
NN
Итак, вы решились на этот смелый шаг изучения PHP. Только за это вы достойны
похвалы. Не стоит понимать это как какое-то запугивание, просто по моему
мнению, человек, решивший серьезно заняться программированием, заслуживает
уважение за смелость, а человек, научившийся программированию, достоен
медали за терпение и упорство.
Ну все, для вступлений хватит, пора непосредственно переходить к нашим
урокам.
Прежде всего нужно сказать: PHP-скрипт для его выполнения должен быть
заключен в следующие последовательности символов:
<? собственно скрипт ;?>
либо
<?php скрипт ;?>
или
<script language="php"> сам скрипт </script>
Все они работают одинаково, все дело только в личных предпочтениях.
Например, мне больше нравится первый вариант, но вы можете пользоваться
любым другим.
Между этими символами будет выполняться каждая строка кода, кроме строккомментариев, которые обзначаются следующим образом:
// - не выполняются команды от данных символов до конца строки;
/* комментарий */ - не выспринимаются команды между данными символами
независимо от количества строк комментария;
# - комментарием считаются все символы от данного знака до конца строки.
Теперь мы познакомимся с одной из самых распространенных функций PHP функцией вывода echo. Например, скрипт
<? echo "Сейчас мы проходим урок 1";?>
выведет на экран текст "Сейчас мы проходим урок 1" (без кавычек) без какоголибо форматирования. Вы можете опробывать это сами. Для этого необходимо
создать файл с расширением .php, .php3, .php4 (хотя последние два использовать
не рекомендуется) или .phtml (именно эти расширения дают понять серверу, что в
файле содержится php скрипт) и скопировать туда предыдущий пример. После
закачки файла на хостинг, набрав в браузере соответствующий URL, вы можете
увидеть плод вашего небольшого труда. Добавлю, что внутри кавычек функции
echo можно размещать любой текст, в том числе HTML-теги.
Но, конечно же, функция echo была бы бесполезна, если бы выводила одно и то
же сообщение. Поэтому мы плавно переходим к понятию переменной. Все без
исключения переменные в PHP начинаются со знака $ (доллар). Причем имя
переменной не должно начинаться с цифры, хотя внутри себя может её
содержать. Заметьте, что имена переменных чувствительны к регистру.
Операция, неразрывно связанная с переменными, - операция присваивания = .
Рассмотрим следующий пример:
<? $A=7.135;
$a=15; $mess4="Четверг";
echo "Сегодня $mess4, $a -ое" ;
$9sad=194 //Неправильное имя переменной
?>
Результат выполнения скрипта можно посмотреть здесь.
Интересная особенность PHP заключается в том, что от пользователя не требуется
предварительно определять тип переменной. В вышеуказанном примере мы
присвоили переменной $A действительное, нецелое число, и она автоматически
стала типа double, или, по-другому, float. Переменной $a мы присвоили целое
числовое значение, теперь её тип - integer. $mess4 присвоено строковое
значение - её тип, соответственно, string. Данная особенность, безусловно,
облегчает жизнь разработчика PHP-скриптов.
Как вы заметили выше, каждая операция в PHP отделяется знаком ; (точка с
запятой). Это обязательное условие для всех операций, кроме некоторых
функций, которые мы рассмотрим в следующих уроках.
Но опять же все переменные в примере выше фиксированы, для их изменения
нам необходимо самим менять их значение в скрипте. Как же этого избежать?
Ответ - стандартные формы HTML.
<HTML>
<form action="www.ваш_домен.ru/test.php" method="post">
Ваше имя <input type="text" name="name"><br>
Ваша фамилия <input type="text" name="fam"><br>
<input type="submit" value="Да!">
</form></HTML>
Если мы укажем в свойстве action тега Form в качестве скрипта-обработчика
наш файл test.php, содержащий следующие строки:
<? echo "Поздравляю, $name $fam,<br>Вы только что выиграли
1 000 000 японский йен. Забрать вы их можете в г.Токио, улица Красных
Самураев, д.15" ;?>
то имена полей формы и их значения передадутся скрипту, который
автоматически сделает названия полей переменными, а значения полей значениями соответствующих переменных.
Вот как будет работать наш пример:
Ваше имя
Ваша фамилия
? ?!
Замечу, что данный пример будет работать только если в настройках PHP (они
хранятся в файле php.ini) в качестве значения параметра "register_globals"
установлено "On".
На этом мы закончим наш первый урок. Поздравляю вас с этим и надеюсь на то,
что вам было интересно и вы продолжите свое обучение. На следующем уроке мы
поближе познакомимся с формами и со связкой форма->скрипт.
Урок 2. Формы всего сущего.
Форма (эйдос, "морфэ") - специфический
принцип вещи, её сущность, цель и
движущая сила, актуализирующая первоматерию
как простую возможность бытия...
Русская народная поговорка
На прошлом уроке мы научились передавать данные в php скрипт, что является
удивительным явлением с точки зрения программирования: ввод данных происходит с
помощью одного языка, обработка и вывод - посредством другого.
Существует два самых распространенных метода передачи данных из формы в скрипт:
GET и POST. Не вдаваясь в технические подробности, скажу лишь, что пользователь
может различить их только по виду адресной строки. Например, если URL выглядит
http://www.server.ru/registration.php?name=Vasia&surname=Pupkin, то мы можем
сказать, что в данной форме использован метод GET. Этот метод предполагает
присоединение к URL имён и значения форм. Причем делается это по следующей
схеме:
http://какой-либо_URL/страница.php?имя_переменной=её_значение&имя=значение...
Метод POST, в отличие от GET, незаметен для пользователя и ничего не прибавляет к
URL. На данный момент нам этих знаний вполне достаточно.
Перейдем теперь непосредственно к самим формам и рассмотрим различные способы
ввода данных в скрипт.
<Input type="TEXT"> <TextArea>
Два схожих элемента формы, предназначенные для ввода текста. В скрипт значения
передаются следующим образом: значение атрибута name становится именем
переменной, а введенные в элемент данные - её значением этой переменной.
<SELECT>
??????? 1
В HTML это записывается следующим образом:
<form ...><select name="variants">
<option value="1">Вариант1</option>
<option value="2">Вариант 2</option>
<option value="etc">и т.д.</option>
</select></form>
В скрипт передается переменная variants с одним из значений (1, 2 или etc) в
зависимости от выбора пользователя. Если value не указаны, передается текст между
тегами <option>.
<Input Type="CheckBox">
В случае установки флажка в обработчик передается переменная с именем,
соответствующим имени самого checkbox, со значением On. Если checkbox пуст, то в
скрипте эта перенная вообще не будет определена.
<Input type="Radio">
Самый распространенный способ использования этого элемента - выбор только одного
из альтернативных вариантов. Для него соответствует следующий HTML код:
<form ...>
<Input type="radio" name="var" value="1">Вариант 1
<Input type="radio" name="var" value="variant_2">Вариант 2
</form>
Вариант 1
Вариант 2
В скрипт передастся переменная var со значением либо 1, либо variant_2.
<Input type="HIDDEN">
Невидимый элемент формы, но тем не менее несущий значение в скрипт. В phpпрограммировании его часто используют для передачи данных через несколько
страниц. Для этого на каждой странице необходимо расположить php-скрипт,
считывающий значение элемента Hidden с предыдущей страницы и присваивающий это
значение элементу Hidden текущей страницы. Типичный пример - двухстраничные
формы, где все данные формы с первой страницы заносятся в Hidden-элементы формы
со второй страницы. Кроме того, этот элемент используется при использовании одного
и того же скрипта несколькими формами, что обеспечивает распознавание формы, из
которой были переданы данные. Например, формы оценки статей на сайте.
<Input type="SUBMIT">
<Input type="Image" src="адрес">
?????? ???????
Оба элемента выполняют одинаковую функцию - подачу запроса на обработку формы.
Без одного из этих элементов функциональная форма просто не может существовать.
Если с Submit, я думаю, все понятно, то Image имеет одну особенность: этот элемент
может передавать скрипту два значения - координаты X и Y пикселя изображения, на
который был совершен клик.
До следующего урока я рекомендую попрактиковаться в использовании всех элементов
и выводом значений форм функцией ECHO. Кстати, для вывода всех значений форм
вместе можно использовать предопределенные переменные $HTTP_GET_VARS или
$HTTP_POST_VARS для соответствующих методов передачи данных. О
предопределенных переменных мы поговорим попозже, а пока лишь скажу, что это
зарезервированные переменные, которые сами берут значения из окружения сервера.
Вышеуказанные переменные лучше выводить функцией PRINT_R, а не ECHO. Это
должно выглядеть так:
<? print_r ($HTTP_POST_VARS); ?>
А в следующем уроке мы рассмотрим такой тип данных, как массив, а также
познакомимся с управляющими конструкциями PHP
Урок 3. Выражаемся по-ПиЭйчПовски
Верить в наше время нельзя никому. Даже себе. Мне - можно.
Итак, на прошлом уроке я обещал начать урок 3 с изучения следующего, четвертого
типа данных - массива (три предыдущих см. в уроке 1). Ну что ж, приступим.
По сути, массив (array) можно представить в виде таблицы:
0
1
2
3
4
Вася
Маша
Дима
Лена
Андрей
Вся эта таблица - массив, назовем его names. Таблица состоит из ячеек с номерами от 0
до 4, в каждой ячейке имеется свое значение. Доступ к какому-либо значению массива
достигается следующим образом: $names[индекс], где индекс в нашем примере - число
от 0 до 4. Задаваться массив может несколькими способами:
$names[0] = "Вася";
$names[1] = "Маша";
// и так все другие элементы
// либо
$names=array(0=>"Вася", 1=>"Маша", "Дима", "Лена","Андрей");
/* в способе выше необязательно писать 0=>... и 1=>..., так как им
автоматически присваиваются эти индексы по порядку (см. аналог ниже) */
// либо
$names[] = "Вася"; /* если индекс не указан, присваивается индекс, на
единицу больший максимального индекса до операции*/
$names[] = "Маша";
Иногда использование массива с ключами в виде чисел 0,1,2... становится неудобным.
В этом случае создается ассоциативный массив. Ассоциативный массив - это массив, в
котором индекс не обязательно должен быть последовательным рядом числовых
значений. Индексом массива может быть любое число или строка. Например:
$a["мебель"] = "диван";
$fruits = array( 'банан' => 'желтый', "помидор" => "красный" );
$months = array( array( 'январь', 'февраль' ), "весна" =>
array("март","апрель","май"));
Как видно выше, элементами массива могут быть не только константы и переменные,
но и массивы, которые имеют те же свойства, что и обыкновенные. Элементами
массива можно манипулировать, как и переменными.
Вот мы познакомились с основными типами данных в PHP. Но, согласитесь, вводить
данные, затем совершать на ними некоторые простенькие операции и выводить их на
экран - не очень интересно, да и не для этого PHP был разработан. Теперь, когда мы
познакомились с основами языка, переходим к более сложной, но одновременно и
более интересной части нашего обучения. И сегодня на уроке мы рассмотрим основные
управляющие конструкции PHP.
Управляющие конструкции.
Оператор условия IF.
Синтаксис:
IF (условие) {
операции, выполняющиеся в случае, если условие верно ;
} else {
операции, выполняющиеся при неверном условии ;
}
Логический оператор IF подобен выражению "Если ..., то ..., иначе...". Оператор может
существовать и в урезанном виде: без else и последующих за ним операций. Тогда,
если условие окажется неверным, оператор никак не отреагирует .
Заметьте, что если оператор IF содержит только одну операцию, заключенную в
фигурные скобки, то эти скобки ставить необязательно.
Условием может быть любое выражение, способное возвращать значения либо TRUE
(правда), либо FALSE (ложь). Чаще всего в условии используются операторы
сравнения, например $a>0. Далее представлены другие возможные операторы
сравнения.
Операторы
==
!= , либо <>
<
>
<=
>=
Значение
Проверка на равенство
Не равно
Меньше
Больше
Меньше или равно
Больше или равно
Условий в операторе IF может несколько. Тогда они отделяются логическими
операторами. Ниже приведена таблица логических операторов.
Операторы
&& или AND
|| или OR
XOR
! (напр. !$a)
Значение
Условие верно, если два выражения верны
Условие верно, если хотя бы одно выражение верно
Верно, если только одно выражение верно
Условие верно, если выражение неверно
Заметьте, что условием может выступать любая функция, возвращающая TRUE в
случае успешного выполнения, (такие функции можно посмотреть в Мануале или на
этом сайте) или все переменные, которые будут возвращать FALSE в случае, если
переменная равна нулю или вообще не имеет значения, и TRUE при всех других
значения.
Внутри оператора IF может быть вложен другой оператор IF. Тогда слово ELSE
заменяется на ELSEIF и далее по стандартному синтаксису.
Пример:
if ( $a > 3 && $a <= 20 && $a != 5) {
$result = $a * $b ;
echo "Умножаем";
} elseif ( ($a > 20 || $a < 3 ) && $b ) {
/* $b - то же самое, что и $b != 0 */
$result = $a / $b ;
echo "Делим";
} else echo "Invalid number" ;
Оператор выбора SWITCH.
Синтаксис:
switch (переменная или выражение) {
case условие :
команды ;
break;
case условие :
команды ;
break;
// и так далее
}
По сути, оператор SWITCH заменяет последовательность операторв IF... ELSEIF...
ELSEIF и так далее. Просто все это короче записывается и удобнее читается.
Пример:
switch ( $a ) {
case (int)$a:
echo "Число $a является целым" ;
break;
case "0" :
echo "Число $a равно нулю" ;
break;
default :
echo "Число $a не равно нулю"; //default - все другие значения
break;
}
Цикл FOR.
Синтаксис:
for (выражение 1; выражение 2; выражение 3) {
команды цикла ;
}
Цикл FOR выполняет команды заданное количество раз. Как только выражения
начинают противоречить друг другу, цикл прекращает свою работу.
В циклах часто используются выражения следующего рода:
Выражения
$a += 2 или $a -= 3
$a *= 10 или $a /=5
$a++ или $a-++$a или --$a
Эквивалент
$a
$a
$a
$a
=
=
=
=
$a
$a
$a
$a
+ 2 или $a = $a - 3
* 10 или $a = $a / 5
+ 1 или $a= $a - 1
+ 1 или $a= $a - 1
Отличие последних двух выражений заключается в том, что при использовании
выражения типа $a++ PHP сначала берет оригинальное значение, а затем увеличивает
его на единицу. А при использовании выражения вида ++$a значение переменной
сначала увеличивается, а затем уже берется это увеличенное значение. Например:
<?
$a=2;
$a++;
echo $a;
$a = 4;
--$a;
echo $a;
?>
//значение $a равно 2
//значение $a равно 3
//значение $a равно 3
//значение $a по-прежнему равно 3
Эти выражения могут использоваться как самостоятельные выражения вне цикла.
Пример:
<?
$p=0;
for ($i = 1; $i <= 4; $i++) {
$p += $i;
echo "P равно $p \n" ;
}
?>
Результат выполнения этой программы будет выглядеть так:
P равно 1
P равно 3
P равно 6
P равно 10
Цикл WHILE.
Синтаксис:
while (условие) {
команды, выполняющиеся при верности условия ;
}
Цикл WHILE в отличие от цикла FOR используется в случае, если число повторений
выполнения команд неизвестно.
Пример:
<?
$p = 10;
$n = 0;
while ($p <= 20) {
$n++;
$p+= 0.1*$p;
}
echo "Цикл выполнился ".$n." раз"; ?>
Выше вы могли заметить новый оператор . (точка). Это так называемый оператор
"склеивания". Он работает только со строковыми типами данных.
На этом заканчиваем урок, а на следующем мы познакомимся с функциями PHP.
Урок 4. Функционируем?
Язык PHP, как и практически любой современный язык программирования,
поддерживает функции. Функции отличаются от других конструкций языка "хвостом"
на конце в виде скобок (), между которыми могут присутствовать некоторые аргументы.
В PHP существует два типа функций:


зарезервированные функции;
функции, определяемые пользователем.
С первым типом мы уже сталкивались, например, когда упоминали функцию print_r().
Эти функции уже заранее разработаны производителем, и мы можем беспрепятственно
ими воспользоваться. Они имеют самое разнообразное применение: это и
математические функции, и функции работы со строками, массивами, файлами,
временем и так далее. Все эти функции вы можете посмотреть в Мануале PHP, либо с
некоторыми наиболее используемыми функциями, переведенными на русский язык, вы
можете познакомиться прямо на этом сайте.
Больше возможностей, а значит и проблем, предоставляют функции, определяемые
пользователем. Эти функции разрабатываются самим программистом в процессе
написания кода. Они могут быть самыми разнообразными по сложности и
разноплановыми по применению.
Особенность этих функций состоит в том, что их необходимо определять, в отличие от
зарезервированных функций.
Синтаксис определения таких функций в общем виде выглядит так:
function имя_функции(аргумент1, аргумент2 и так далее)
{
некоторый операции, выполняемые при вызове функции ;
}
Имя функции - оригинальный идентификатор, может содержать все буквы латинского
алфавита, числа и знак подчеркивания. Следите, чтобы имя вашей функции не
совпадало с зарезервированной функцией PHP, иначе программа выведет ошибку.
Заметьте, что имя функции, в отличие от имени переменной, нечувствительно к
регистру.
Аргументами функций могут являться переменные и/или константы. Это те значения,
которые будут использоваться командами в теле функции.
Внутри функции может быть любой верный php код, включая функции обоих типов, а
также классы, которые мы разберем немного позже.
Использование функций, определенных пользователем, полностью идентично
использованию зарезервированных функций.
Приведу в качестве примера определение простенькой функции:
function sum($a, $b)
{
$c = $a + $b;
echo "$a + $b = $c <br>";
return $c;
}
Конструкция return аргумент заставляет данную функцию возвращать после своего
выполнения значение аргумента. Посмотрим пример использования вышеуказанной
функции:
$a = sum(12, 5);
echo $a;
/* этот код выведет
12 + 5 = 17
17
*/
В этом примере переменной $a присвоилось значение, возвращенное функцией sum
(т.е. значение $c). Если бы мы не указали в определении функции return $c, то
пример выше вывел бы только 12 + 5 = 17.
Нужно учесть, что все переменные внутри функции являются локальными,
действующими только в теле функции. Поэтому в примере выше не было ошибкой
строчка $a = sum(12, 5), где в функции sum уже есть своя переменная $a. Эти
переменные никоем образом не пересекаются и имеют совершенно разное значение.
Для того, чтобы переменная, определенная внутри функции, действовала вне ее
пределов, необходимо указать, что она является глобальной. Это делается следующим
образом:
function word ($word)
//функция word() и переменная $word - совершенно разные вещи
{
global $string;
//определение $string как глобальной
$string = $word." - отличная вещь!";
}
word ("PHP");
echo $string;
//выведет "PHP - отличная вещь!"
Как видите, мы не применяли return, так что функция ничего не возвращает, но мы
определили переменную $string как глобальную, поэтому она действует в пределах
всего кода php, так что в данном случае $string в функции равна $string вне
функции.
Еще одна проблема, которая может возникнуть при использовании определяемых
функций, - то, что функция после своего выполнения теряет все свои значения и при
ее новом вызове нельзя использовать данные, полученные при предыдущем
выполнении функции. Но эта проблема решаема, используя определение переменное
как статичной.
function mult ($a)
{
static $result=1;
//определение $result как статичной
$result *= $a;
echo $result.'<br>';
}
for ($i=2; $i<=4; $i++)
{ mult($i) ;}
/*выведет
2
6
24
*/
Если бы мы не написали static, то пример вывел бы числа 2, 3 и 4.
Следует заметить, что в некоторых случаях нам нужно передавать в качестве аргумента
функции одно и то же значение. В этом случае в определении функции можно указать
аргумент функции по умолчанию. Тогда, если при вызове функции аргумент не указан,
то будет использоваться это значение по умолчанию.
function arr_count ($a, $b=3) /* значение по умолчанию должно указываться
справа */
// такая запись: function arr_count ($b=3, $a) - будет работать неверно
{
global $array;
$result = $array[$a] + $array[$b];
return $result;
}
$array = array(2, 5, 4, 7, 2);
echo arr_count(2)." + ".arr_count(1, 4)." = ".(arr_count(2) + arr_count(1, 4));
// выведет
11 + 7 = 18
Функцию можно сделать значением переменной. Например, продолжаем пример выше:
$CountArray='arr_count';
echo $CountArray(0);
//выведет число 9
На этом все. До следующего урока.
Урок 5. Наша первая считалка.
Итак, мы уже достаточно узнали, чтобы перейти к практике. И сегодня на уроке мы с
вами создадим несложный калькулятор.
Хорошо. Давайте сначала продумаем, что будет делать наш калькулятор. В первую
очередь, он, конечно, должен выполнять арифметические действия: сложение,
вычитание, деление и умножение. Неплохо. Но где вы сейчас увидите калькулятор
только с такими функциями? Поэтому снабдим его еще несколькими функциями, но
перегружать его не будем. Договоримся, что наша "считалка" будет еще вычислять
корень из числа, возводить его в указанную степень и, наконец, выводить процент от
числа.
Решили? Теперь приступаем. Создаем файл, который называем calc.php. Использование
функций позволит нам совместить в этом файле как форму для ввода чисел и других
данных, так и вывод результата.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Калькулятор</title>
</head>
<body>
<?
/* приступаем непосредственно php коду */
function show()
{
global $action;
?>
<FORM method=Get action="calc.php" target="_blank">
/* target="_blank" - результат обработки формы будет выводится в новом окне
*/
Первое число
<input type="text" name="first">
Второе число (Степень, процент)
<input type="text" name="second">
<select size="1" name="action">
<option value="sum">Сложить</option>
<option value="min">Вычесть</option>
<option value="mult">Умножить</option>
<option value="dev">Разделить</option>
<option value="stepen">Возвести в степень</option>
<option value="procent">Процент от числа</option>
<option value="koren">Корень</option>
</select>
<br><input type="submit" value="Выполнить">
</form>
<?php
}
//конец функции show()
Итак, мы определили функцию show, которая, как вы поняли, будет выводить форму.
Заметьте, как мы разбили php скрипт, вставив блок HTML, но все же это все относится к
одной функции show, это подобно тому, если бы не прерывали скрипт, а вместо этого
написали бы echo "<FORM и так далее </html>";. Пока ни о каком дизайне формы нет
и речи, это лишь "скелет" Калькулятора.
Продолжаем дальше писать файл calc.php.
function calc()
{
global $action, $result, $first, $second;
switch($action)
{
case "sum": $result = $first+$second; break;
case "min": $result = $first-$second; break;
case "mult": $result = $first*$second; break;
case "dev":
if (!$second)
/* если второе число равно "0" или вообще не введено
*/
{
exit("Извините, программа не может выполнить действие: на ноль делить
нельзя");
}
$result=$first/$second; break;
case "procent": $result = $first*($second/100); break;
case "stepen": $result = pow($first, $second); break;
case "koren": $result = pow($first,0.5); break;
}
?>
//заканчиваем оператор switch
Результат Вашего действия равен <b>
<? echo $result; ?>
//вывод результата
</b>
</font>
<?
}
//конец функций calc()
Написали функцию calc, занимающуюся непосредственно вычислением результата и
вывода его на экран пользователя.
Наконец, заканчиваем файл:
if ($action) calc(); else show();
?>
Если в переменной $action есть какое-либо значение (а оно может появиться только в
случае нажатия кнопки "Выполнить"), то выполняется функция вычисления calc(), если
же $action пуста, то выведется форма Калькулятора.
Конечно, можно было дополнить наш калькулятор еще тригонометрическими
функциями (php функции sin(), cos(), tan(), котангенс - 1/tan() ), возможно было
бы пристроить всяческие Java-навороты. Но это бы не меняло бы сути дела, а лишь
сделало пример более громоздким. Так что если желаете использовать всяческие
примочки на свой калькулятор - пожалуйста.
Также этот пример можно было сделать в двух отдельных файлах: допустим,
show_calc.htm и calc.php, где первый файл содержал бы функцию show, а второй,
соответственно, calc. Но мне хотелось показать использование, что называется, userdefined functions, то есть функций, определенных пользователем.
Файлы, директории и PHP
Работа с файлами
Необходимость в операциях с файлами встает перед программистом очень часто. Если
ваши скрипты не используют баз данных, то файлы остаются единственными
приемлемыми хранителями информации для скрипта. Применение файлов как
хранилищ информации выполнения скрипта позволяет использовать их в самых
разнообразных ситуациях. Практически все скрипты-счетчики чего-либо написаны на
основании работы с файлами. Также возможно привести кучу других примеров, но пора
переходить непосредственно от слов к делу.
Сразу хочу сказать, что работа с файлом должна быть санкционирована. По умолчанию
PHP не допускает работу с файлом в целях безопасности. Чтобы снять этот запрет в
FTP-менеджере CuteFTP в свойствах файла выставьте все галочки, в других менеджерах
должно быть что-то подобное.
file_exists
Прежде, чем производить операции с файлом, часто необходимо убедиться, что
указанный файл вообще существует. Этим и занимается функция file_exists. Эта
функция может возвращать только два значения, как вы сами понимаете, TRUE (если
указанный файл существует) и FALSE. Обычно использование данной функции
выглядит так:
if ( !file_exists("somefile.txt") ) exit("Указанный файл не существует");
Заметьте, что функция действует только на локальных файлах, то есть если вы
возжелаете проверить, обзавелся ли Яндекс файлом robot.txt, то ваши усилия будут
тщетны. Но зато возможно проверить любой файл, лежащий на локальном сервере
независимо от директории его расположения.
Вот некоторые правила описания пути к файлу.
filename
Просто указание имени файла означает, что он
находится в текущей директории.
./files/filename
Файл содержится в папке files, находящейся в
текущей директории. Длина цепочки из папок
не ограничена.
../filename
Файл лежит в предыдущей директории. Каждый
знак ../ расценивается как возврат в
родительскую директорию.
../files/filename
Файл находится в папке files, которая лежит в
предыдущей директории.
filesize
Как видно из названия, функция определяет размер файла и возвращает его в байтах.
Полезно, если вы хотите проверить файл на наличие в нем информации (как вы
понимаете, пустой файл содержит 0 байт), а также возможно проверить размер файла
на превышения определенного лимита.
file
Эта функция уже непосредственно работает с файлом. Она возвращает содержимое
указанного файла, причем делает она это в виде массива, где каждый его элемент
является строкой файла. Функция полезна, когда в одном файле необходимо сохранить
несколько разных значений, которые не должны пересекаться. Тогда каждое значение
сохраняется на отдельной строке и читается функцией file, которая возвращает
массив, вследствие чего обращение к заданной переменной происходит с помощью
чтения значения элемента массива с индексом, соответствующим строке в файле.
Кроме того, возможно воссоединение всех элементов возвращенного массива в одну
переменную. Это делается с помощью функции работы с массивами implode.
$text_file = implode("", file("somefile"));
echo $text_file;
readfile
Как и предыдущая функция, readfile выводит содержание указанного файла, а также
возвращает число символов в файле (или количество байт, кому как нравится, так как
один символ равен одному байту). Но учтите, что в отличие от file, эта функция не
предусматривает присваивание прочитанного содержимого файла переменной. Если вы
попытаетесь это сделать, то переменной присвоится только число прочитанных
символов.
$bytes = readfile ("somefile");
echo "Итого - ".$bytes." символов";
fopen
Если предыдущая функция самодостаточна и в общем не связана с другими функциями,
то последующие функции работы с файлами работают в связке с fopen. Эта функция
открывает указанный файл и возвращает идентификатор соединения с файлом,
который используется в служебных целях. С содержимым файла эта функция никаким
образом не связывается.
Функция fopen имеет несколько режимов работы с файлом. Они указываются после
имени файла и представляют из себя следующие обозначения:






"r"
Файл открывается только для чтения его содержимого.
"r+"
Открытие файла как для чтения, так и для записи.
"w"
Файл открывается с целью записи.
"w+"
Открыть файл для чтения и записи.
"a"
Файл открывается для записи в конец файла (дозаписи).
"a+"
Открывается для дозаписи и чтения.
fgets
Функция чтения файла, открытого функцией fopen. Но в отличие от file, эта функция
за каждый раз своего выполнения возвращает только одну строку файла, при этом она
перемещает внутренний указатель файла на следующую строку, которую она прочитает
при следующем обращении к функции. Поэтому, если вам необходимо прочитать файл
целиком, необходимо использовать эту функцию в цикле.
Заметьте, что функция fgets использует дополнительный параметр length, который
указывает максимальную длину строки файла для чтения. Если объем строки
превышает это число, то функция возратит ее в "урезанном" виде объемом в число
length байт. По умолчанию этот параметр установлен в 1024 байт, или в один
килобайт. Тем более обратите внимание на этот параметр, если вы используете файлы
больших размеров, так как при чтении таких файлов может переполниться буфер
выполнения PHP (его объем указывается в файле конфигурации), что приведет к
зависанию.
$file = fopen ("somefile", "r");
while ($line = fgets($file, 4096) )
{
$line.="\n";
echo $line;
}
следующие команды работы с файлом
Обратите внимание, что в качестве указания файла для чтения необходимо указывать
не имя файла, а идентификатор соединения с файлом, возвращенный функцией fopen
(в нашем примере это значение переменной $file).
fputs
Функция записи информации в файл, причем делает она это по принципу работы
функции fgets, то есть начинает запись с позиции внутреннего файлового указателя.
Вообще, эта функции во многом схожа с вышеуказанной: она также использует
параметр длины записываемых данных, который также является опциональным.
fclose
Как вы догадались, эта функция закрывает указанный файл. Вообще-то, по
завершении выполнения скрипта, PHP сам закрывает все открытые им файлы, но все
же лучше это делать вручную. В качестве параметра функции необходимо указать
идентификатор соединения с файлом.
Для иллюстрации связки вышеуказанных функций приведем пример создания простого
счетчика посещений.
<?
$file = fopen("counter.txt", "r");
$c = fgets ($file, 150);
fclose ($file);
$c++;
$file = fopen("counter.txt", "w");
fputs ($file, $c);
fclose ($file);
echo $c;
?>
Работа с директориями
Тесно связаны с действиями над файлами операции с директориями. Алгоритм работы с
ними схож с операциями над файлами: сначала директорию необходимо открыть,
выполнить какие-либо действия и, наконец, закрыть ее.
opendir
Эта функция открывает указанную директорию и возвращает служебный
идентификатор соединения с директорией. Пути к директории следует указывать
следующим образом:
.
Точка означает открытие текущей директории
./files/
Открытие папки files, находящейся в текущей
директории
..
Открытие папки на уровень выше текущей
readdir
Функция читает директорию, открытую opendir. За каждый проход она возвращает имя
файла или папки, лежащих в указанной директории, и перемещает внутренний
указатель на следующий объект директории. Так что для прочтения всей директории ее
необходимо использовать в цикле.
Также необходимо заметить, что эта функция возвращает служебные объекты папки . и
.., которые можно отсекать при выводе оператором IF.
closedir
Закрываем директорию, указывая в качестве аргумента идентификатор соединения с
папкой.
Иногда использование функций работы с директориями очень облегчают жизнь.
Например, в разделе Функции вы можете видеть список функций в алфавитном
порядке. Представляете, сколько времени нужно было бы потратить, чтобы вручную
написать весь этот список с ссылками, да еще и в алфавитном порядке. И вот как раз
здесь мне помогли функции работы с директорией. Каждая функция была помещена в
отдельный файл с именем, соответствующим названию функции, без каких-либо
расширений.
$i = 0;
$handle = opendir ('.');
while($file = readdir($handle))
{
if ($file != '.' && $file != '..')
{
$func[$i] = $file; //формируем массив названий файлов с функциями
$i++;
}
}
sort ($func);
for ($q = 0; $q<sizeof($func); $q++)
{
echo "<a href='index.php?".$func[$q]."'>".$func[$q]."</a><br>\n";
}
Так что, при каждом заходе на страницу вы получаете только что сгенерированный
список функций.
На этом все. Встретимся на следующем уроке.
Добро пожаловать, или пишем Гостевую книгу.
Итак, мы добрались до чего-то серьезного. Если раньше мы занимались больше
"отстраненными" делами, то сегодня мы будем решать реальную задачу, с которой
может столкнутся практический любой вебмастер, - написание собственной гостевой
книги.
Конечно, можно использовать "гостевые" специализированных серверов, например,
какой-нибудь http://guestbook.land.ru/, и лицезреть каждый раз их баннеры; или же
взять готовые скрипты, допустим, с CGI.ru, но это тоже не так интересно, потому что
автор скрипта писал гостевую книгу не для вашего сайта, а следовательно, такая
"гостевуха" может нарушить общую концепцию вашего обожаемого детища.
Ну, думаю, я вас убедил, что "своя рубашка ближе к телу", так что приступим.
Итак, с чего начинается любая программа? Правильно, с постановки задачи. Так, наша
гостевая книга прежде всего должна делать следующее:
1. Сохранять добавленное сообщение.
2. Производить проверку добавляемого сообщения перед сохранением (в целях
безопасности).
3. Выводить все сообщения на экран.
Это общие формулировки задач, к которым по мере написания кода будут добавляться
новые.
Теперь, что нам понадобится? Как вы понимаете, серьезные вещи не пишутся в одном
файле. Так что создадим в одной папке следующие файлы:
1. config.php - здесь будут храниться общие настройки гостевой книги.
2. base.txt - файл, где будут храниться все сообщения, наша своеобразная база
данных.
3. guest.php - здесь будет содержаться непосредственно наш скрипт.
Все, приготовления на этом окончены и пора переходить к сути дела.
Начинаем писать файл guest.php. Сразу хочу сказать, что практически все действия
мы будет записывать в функциях, которые мы изучили на уроке 4. Поверьте, это очень
удобно.
Прежде всего сделаем HTML шапку, которая будет выводиться независимо от того,
какие действия мы совершаем.
<HTML>
<HEAD>
<TITLE>Гостевая книга</TITLE>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
</HEAD>
<BODY>
<?
require("config.php");
Далее пишем функцию, которая будет выводить форму для добавления сообщения. Она
основана на HTML. Код становится более "размашистым", поэтому дальнейшее
использование таблиц для его отображения становится невозможным, так что я
перехожу к <TextArea>.
function show _form()
{
?>
<form method="post" action="">
Имя<br>
<input type="text" name="name" maxlength="22">
<br>
E-mail<br>
<input type="text" name="email" maxlength="21">
<br>
Сообщение<br>
<textarea cols="70" row s="20" name="mess"></textarea><br>
Ну, я думаю, здесь все понятно: мы прерываем PHP код и пишем HTML, хотя эта часть
по-прежнему относится к функции. Свойство action тега Form равно пустой строке, то
есть обработчик формы находится в этом же файле. Уже при написании формы мы
думаем о защите скрипта и ограничиваем количество вводимых символов свойством
maxlength.
Итак, пользователь ввел сообщение и нажал на кнопку "Послать". Что же далее?
Понятно, что нам нужно теперь сохранить это сообщение, чем сейчас и займемся.
Напишем в файле config.php следующие строки:
<?
$base = "base.txt";
Вместо base.txt вы можете использовать любой другой файл, но я буду называть этот
файл именно так.
Теперь название файла-базы занесено в переменную, что значительно облегчит нам
жизнь, если мы пожелаем использовать другой файл-базу.
Возвращаемся обратно к нашему основному файлу и пишем в нем следующее:
function save_mess()
{
global $name, $email, $mess, $base;
$date = date("d.m.y - H:i:s");
$text = $name."^^".$email."^^".$date."^^".$mess."\n";
$fp = fopen($base,"a");
fputs($fp, $text);
fclose($fp);
}
Ну, это, наверное, тоже несложно. Мы объявляем наши переменные как глобальные, то
есть именно те, которые пользователь послал из формы. Далее мы присваиваем
переменной $date текущую дату в виде день.месяц.год - часы:минуты:секунды (о
функции date() читайте в статье Функция date() - вывод даты и времени в PHP).
Затем имя отправившего сообщения, его адрес и само сообщение присваиваем
переменной $text, причем разделяем их знаком ^^, который мы будем использовать
для обратного процесса - из строки получать имя, адрес и сообщение. Для этого можно
использовать любой символ или сочетание символов, которые не используются на
письме, но я выбрал именно ^^.
Далее открываем файл-базу для дозаписи и приписываем в конце файла полученную
строку из всех данных.
Итак, что мы имеем? Мы имеем форму для ввода сообщения и функцию для его
сохранения. А теперь напишем функцию вывода сообщений.
Договоримся, что сообщения у нас будут выводиться в порядке убывания даты и
времени их написания. Кроме того, ограничим число выводимых сообщений
определенным числом, указанным в файле config.php, куда и добавляем строку:
$MessOnScreen = 10;
Вы можете установить здесь любое число.
Возвращаемся к guest.php и пишем довольно объемную функцию:
function show _mess()
{
global $base, $MessOnScreen;
$file = file($base);
$file = array_reverse($file);
echo "<table>";
if(sizeof($file) < $MessOnScreen) $MessOnScreen = sizeof($file);
for ($i = 0; $i < $MessOnScreen; $i++)
{
$mess = explode("^^",$file[$i]);
Функцией file() мы получаем массив, где в каждом элементе содержится строка с
одним "блоком" - именем писавшего, адресом и его сообщением соответственно. Потом
мы "переворачиваем" полученный массив, изменяем порядок элементов на обратный,
так что самая последняя запись становится у нас первым элементом массива. Далее мы
сравниваем количество элементов в $file (а значит и количество сообщений) с
указанным в $MessOnScreen лимитом, и если оно меньше этого значения, то
$MessOnScreen устанавливаем в число элементов $file.
Далее выполняется цикл вывода сообщений. С каждым проходом цикла значение
переменной цикла увеличивается на единицу. Таким образом самые свежие сообщения
(те, что были приписаны позже всех и находятся внизу файла-базы) выведутся
первыми, а более старые - последними.
После этого переменной $mess присваиваем массив с элементами:
0-ой элемент - имя
1-ый элемент - адрес электронной почты
2-ой элемент - время добавления сообщения
3-й элемент - само сообщение
Это делается с помощью функции explode(), которая разбивает строку по символам ^^
(помните, говорили об этом выше).
Затем мы выводим в таблице все полученные значения. Если не встречались, то запись
<?=$mess[2];?> эквивалентна <? echo $mess[2];?>
На сегодня все, продолжим на следующем уроке.
Снова здрасьте, или дописываем Гостевую книгу.
Итак, мы сегодня продолжаем писать нашу гостевую книгу и пора вспомнить, на чем мы
остановились. Мы написали три функции: по выводу формы, сохранению сообщения и
отображению всех ранее написанных сообщений.
Казалось бы, на этом можно и остановиться. В общем-то этого достаточно для самой
примитивной гостевой книги. Но зачем нам такая нужна? Мы же не хотим, чтобы наше
любимое детище взламывал каждый "типа хакер". В конце концов, нам нужна удобная
функциональная "гостевая".
Так что продолжаем.
А продолжим мы с очень важной функции проверки введенных данных. У нас будет
даже не одна функция проверки, а больше.
Значит, открываем файл guest.php и пишем:
function check_mess()
{
global $name, $email, $mess;
$mess=trim($mess);
$email=trim($email);
$name=trim($name);
$name = htmlspecialchars($name);
$email = htmlspecialchars($email);
$mess = htmlspecialchars($mess);
$mess = str_replace("\n","<br>",$mess);
Это еще не конец функции.
Здесь мы "отрезали" с помощью функции trim() все пустые символы (пробелы,
переводы строк, символы табуляции) в начале и в конце строк. Далее мы заменили с
помощью функции str_replace все символы перевода строки на тег <br>. Это облегчит
нам задачу сохранения форматированных строк и их дальнейшего вывода.
Далее в функции следует очень важный элемент - первое, с чего следует начать
строить защиту программы - функция htmlspecialchars(), которая обрабатывает все
специальные символы HTML, то есть символ < переходит в < и так далее. Никогда не
забывайте об этом элементе!
Мне вот подумалось: мы выполняем проверку данных, а ведь в случае неверности
данных нам требуется выдать сообщение о соответствующей ошибке. Так давайте это
тоже оформим в виде функции. Не закрывая предыдущей функции, ниже пишем новую,
которая будет отвечать за вывод ошибки:
function output_err($num)
{
global $err;
?>
<center><h1>Oшибка!</h1></center>
<p><?=$err[$num];?>
<?
exit();
}
В файле config.php в массиве $err мы будем перечислять сообщения для вывода при
соответствующей ошибке. Обратите внимание, что мы при помощи функции exit()
прекращаем работу скрипта после вывода сообщения об ошибке.
Дописываем функцию check_mess():
if (empty($name)) output_err(2);
if (!preg_match("/[0-9a-z_]+@[0-9a-z_^\.]+\.[a-z]{2,3}/i", $email))
{
output_err(1);
}
if (preg_match("/[^(\w )|(\x7F-\xFF)|(\s)]/",$name)) output_err(2);
}
И тут же, пока не забыли, пишем в файл config.php следующие строки:
$err[1] = "Неверно введен e-mail";
$err[2] = "Неверно введено имя";
Как видите, в предыдущей функции мы активно работали с регулярными выражениями.
Сначала мы проверяли, чтобы e-mail был вида something@server.com, а затем проверке
подвергнулось и имя: оно должно содержать только буквы латинского и русского
алфавита и знак подчеркивания (_). При несоблюдении этих условий задействуется
функция output_err() с аргументом, представляющим собой индекс массива $err для
соответствующей ошибки.
Далее нам необходимо проверить данные на соответствие допустимой длине. Так что
пишем новую функцию check_for_length():
function check_for_length()
{
global $mess, $email, $name, $MessLength;
if (strlen($mess)>$MessLength) output_err(3);
$email=substr($email, 0, 21);
$name=substr($name, 0, 22);
}
Здесь мы сначала сверяем длину сообщения с указанной в переменной $MessLength с
помощью функции strlen(), которая возвращает количество символов в строке, и
используем функцию output_err() в случае превышения длины сообщения над
лимитом. Не забудьте в файле config.php написать следующие строки:
$MessLength = 1000;
$err[3] = "Недопустимая длина сообщения";
Далее мы "обрубаем" строку с помощью функции substr(), оставляя в переменной
$email первые 21 символов, а в $name - 22. Вообще-то в нормальных условиях длина
этих двух строк не может превышать эти числа, так как мы еще в форме для ввода
сообщения указали этот лимит, но все-таки стоит перестраховаться.
Продолжаем блок проверки и пишем следующую функцию, которая будет заниматься
отслеживанием флуда, то есть публикации одного и того же сообщения.
function check_mess_for_flud()
{
global $mess,$base;
$file=file($base);
$file=implode("",$file);
$mess=preg_quote($mess);
if (eregi($mess, $file)) output_err(4);
$mess = stripslashes($mess);
}
Эта функция основана на работе с регулярными выражениями. Сначала мы получаем
массив строк файла-базы с помощью функции file(), затем объединяем весь массив в
одну переменную. Далее мы "квотируем" сообщение пользователя (см. функцию
preg_quote в соответствующем разделе) и смотрим, встречается ли этот текст в базе
или нет. Если встречался, выводим ошибку. Следующая строка возвращает текст в
переменной $mess в первоначальное состояние, то есть убирает все обратные слеши \
перед специльными символами, которые были добавлены функцией preg_quote. В
файле config.php напишите:
$err[4] = "Такое сообщение уже существует";
Следующая функция заканчивает проверки, хотя она сама не является функцией
проверки сообщения (она, скорее, функция проверки файла-базы), но все же мы
отнесем ее сюда.
Эта очень полезная функция облегчит жизнь администратору гостевой книги. Она сама
себя будет "подчищать", удаляя старые сообщения.
function del_mess_from_file()
{
global $base, $MessInFile;
$file = file($base);
$k = 0;
if($MessInFile<sizeof($file))
{
for($i=sizeof($file)-$MessInFile; $i<sizeof($file); $i++)
{
$ResFile[$k]=$file[$i];
$k++;
}
$fp=fopen($base,"w ");
Сразу добавим строку в файл config.php:
$MessInFile = 20;
Это то количество сообщений, которые следует хранить в файле. Если вы не желаете
иметь "архив" сообщений, установите здесь то же число, что и в переменной
$MessInFile.
Теперь разберем функцию.
Сначала мы присваиваем переменной $file массив из строк файла базы. После мы
ставим условие, что все дальнейшие действия функции будут выполняться только если
количество сообщений в файле больше числа, указанного в $MessInFile. Далее
запускаем цикл, который выполнится столько раз, сколько указано в той же
переменной $MessInFile. Затем мы формируем массив $ResFile из последних 20 (так
указано в $MessInFile) элементов массива $file. После этого мы переписываем файлбазу сохраняя там полученный массив $ResFile. Тем самым после выполнения функции
мы отсеем старые сообщения, оставив 20 последних в том же порядке, что и до вызова
функции.
Ну что же, функции написаны. Теперь осталось их правильно вызвать, так как сами по
себе, как вы понимаете, они выполняться не будут.
Все дополнительные функции проверки у нас будут вызываться главной функцией
проверки check_mess(). Теперь она должна выглядеть так:
function check_mess()
{
global $name, $email, $mess;
$mess=trim($mess);
$email=trim($email);
$name=trim($name);
$name=htmlspecialchars($name);
$email=htmlspecialchars($email);
$mess=htmlspecialchars($mess);
$mess = str_replace("\n","<br>",$mess);
check_for_length();
//добав или
А теперь завершающий этап. После всех записей внизу файла guest.php пишем:
if ($mess) {
check_mess();
save_mess();
}
show_mess();
show_form();
?>
</BODY>
</HTML>
Если в переменной $mess есть какое-либо значение (а оно могло появиться только при
нажатии в форме кнопки "Послать"), то сначала выполняется функция проверки
сообщения, а затем, если не происходит ошибки, выполняется функция сохранения
сообщения. И независимо от того, есть ли сообщение или нет, выводятся сообщения из
файла-базы и форма для ввода.
Все, гостевая книга готова к использованию. Сделайте для нее дизайн по вашему вкусу
и все. Пользуйтесь.
Готовые файлы guest.php и config.php вы можете скачать здесь.
Урок 9. Сессии и с чем их едят.
Сессия (от лат. sessio - заседание),
организационная форма работы
представительного органа, суда,
научной или общественной организации.
На сегодняшнем уроке мы рассмотрим такой важный механизм PHP, как сессии.
Так зачем же нужны эти самые сессии и с чем их "едят"? Рано или поздно практически
перед каждым вебмастером встает проблема передачи данных сквозь несколько
страниц. Как же сделать так, чтобы пользователь, бродя по сайту, не "терял" однажды
введенных данных?
Один из таких способов - использование скрытых элементов форм "hidden". На каждой
странице сайта мы размещаем эти элементы, внося в них с помощью PHP значения и
передавая эти значения далее, другой странице. Конечно, такой способ вполне
возможен, но он нерационален. Представляете, сколько таких элементов надо вставить
на сайте объемом, например, 50 страниц.
Но в PHP реализован очень удобный и функциональный механизм работы с сессиями.
Он позволяет сохранять любые данные, связанные с пользователем, и использовать их
на протяжении всего времени нахождения пользователя на данном сайте.
Реализация механизма сессий в PHP.
Любая сессия открывается с помощью функции session_start(), создающей
специальный служебный файл с именем, соответствующим ID сессии, в который
впоследствии будут записаны все данные, связанные с текущей сессией. Место
размещения этих файлов зависит от настроек PHP. Так что если вы используете в своих
скриптах сессии, не забывайте иногда подчищать директорию с этими временными
файлами, так как там со временем может накопиться солидное количество ненужных
файлов.
Также эта функция используется для продолжения текущей сессии. Таким образом, она
должна быть вызвана на каждой странице, использующей данные текущей сессии.
В PHP предусмотрено два способа передачи ID сессии (сокращенно SID):


Через метод GET.
Тогда посетитель будет видеть в своем броузере адресную строку следующего
типа:
http://server.com/main.php?PHPSESSID=bdd95bcd4e1e2ef5ec57fc83a69bba86
Через Cookie.
Здесь, соответственно, посетитель не будет видеть признаков существования
сессии, SID передается через Cookie.
Следующий шаг в работе с сессиями - запись данных в сессию. Этим занимается
функция session_register(). Она сохраняет в файл текущей сессии значения
указанных переменных, которыми вы в любой момент можете воспользоваться.
Регистрация данных в сессию должна выглядеть примерно следующим образом:
session_start();
session_register('name', 'birth');
$name = "Вася Пупкин";
$birth = "4 марта";
Теперь на любой странице данного сайта мы можем обратиться к посетителю по имени.
Обратной функции session_register() является функция session_unregister(),
которая удаляет данные из текущей сессии. Эта функция используется довольно редко,
но иногда бывает очень полезной. Например, в том случае, если вы регистрируете в
сессию большое количество переменных, чтобы не перезагружать файл текущей
сессии, можно удалить оттуда уже ненужные значения.
Дополнительные функции работы с сессиями
session_id
Нередко при работе с сессиями нам требуется определить ее ID. Этим занимается
функция с соответствующим названием session_id(), которая в качестве результата
возвращает ID текущей сессии. На этой функции основан механизм подсчета
посетителей на сайте в данный момент (см. статью "Сейчас на сайте"), где мы с
помощью данной функции сверяем ID текущей сессии с сессиями, записанными в
файле.
session_name
Бывают случаи, когда становится очень неудобным использовать ID сессии, например,
из-за его громозкости и ненаглядности, так как id сессии вида
7542b069d57510a99eaeb31391b15cbf нам практически ничего не скажет. В этом случае
более разумным становится использование функции session_name, которая может
выполнять две роли. Во-первых, она может возвращать имя текущей сессии (по
умолчанию - PHPSESSID). В этом случае ее следует использовать без аргументов. Вовторых, эта функция может устанавливать имя текущей сессии. Рассмотрим пример:
session_start();
echo session_name();
session_name("MySession");
echo session_name();
Данный пример выведет:
PHPSESSID
MySession
Безусловно, такие названия сессии воспринимаются намного лучше, чем страшные ID.
session_destroy
Завершает работу сессии функция session_destroy(). Она уничтожает файл,
связанный с текущей сессией, что является очень удобным. Но здесь возникает
проблема: часто мы не знаем, где именно необходимо уничтожить сессиию. Например,
если данные сессии используется страницами всего сайта, то мы не можем уничтожить
сессию на определенной странице, так как не знаем, какая именно страница будет
последней просмотренной страницей посетителем на сайте. Поэтому использование
данной функци возможно лишь в том случае, если мы заранее знаем, на какой именно
странице действие сессии должно прекратиться.
На этом с сессиями все. До встречи.
PHP и HTTP: headers
Заголовки удваивают размер событий.
Джон Голсуорси
PHP, будучи языком вебпрограммирования, поддерживает реализацию механизма
отправки заголовков HTTP.
Сначала скажем несколько слов о самих HTTP заголовках.
В соответствии со спецификацией HTTP, этот протокол поддерживает передачу
служебной информации от сервера к броузеру, оформленной в виде специальных
заголовков.
Таким образом, HTTP headers - это средство общения сервера с удаленным клиентом.
Каждый заголовок обычно состоит из одиночной линии ASCII текста с именем и
значением. Сами заголовки никак не отображаются в окне броузера, но зачастую могут
сильно изменить отображение сопутствующего документа.
Механизм отправки HTTP заголовков в PHP.
Механизм отправки заголовков в PHP представлен функцией header(). Особенность
протокола HTTP заключается в том, что заголовок должен быть отправлен до посылки
других данных, поэтому функция должна быть вызвана в самом начале документа и
должна выглядеть следующим образом:
header("HTTP заголовок", необязательный параметр replace);
Опциональный параметр replace может принимать значения типа bool (true или
false) и указывает на то, должен ли быть замещен предыдущий заголовок подобного
типа, либо добавить данный заголовок к уже существующему.
В отношении функции header() часто применяется функция headers_sent(), которая в
качестве результата возвращает true в случае успешной отправки заголовка и false в
обратном случае.
Рассмотрим наиболее используемые HTTP заголовки.
Cache-control.
"Cache-control: " значение
Заголовок управления кешированием страниц. Вообще, данная функция является
одной из самых распространенных в использовании заголовков.
Данный заголовок может быть использован со следующими значениями:




no-cashe - Запрет кеширования. Используется в часто обновляемых страницах и
страницах с динамическим содержанием. Его дейсвтие подобно META тегу
"Pragma: no-cache".
public - Разрешение кеширования страницы как локальным клиентом, так и
прокси-сервером.
private - Разрешение кеширования только локальным клиентом.
max-age - Разрешение использования кешированного документа в течение
заданного времени в секундах.
header("Cache-control: private, max-age = 3600") /* Кеширование локальными
клиентами и использование в течение 1 часа */
Expires.
"Expires: " HTTP-date
Устанавливает дату и время, после которого документ считается устаревшим. Дата
должна указываться в следующем формате (на английском языке):
День недели (сокр.) число (2 цифры) Месяц (сокр.) год часы:минуты:секунды GMT
Например, Fri, 09 Jan 2002 12:00:00 GMT
Текущее время в этом формате возвращает функция gmdate() в следующем виде:
echo gmdate("D, d M Y H:i:s")."GMT";
Возможно использование данного HTTP заголовка для запрета кеширования. Для этого
необходимо указать прошедшую дату.
Last-Modified.
"Last-Modified: " HTTP-date
Указывает дату последнего изменения документа. Дата должна задаваться в том же
формате, что и в случае с заголовком Expires. Данный заголовок можно не
использовать для динамических страниц, так как многие серверы (например, Apache)
для таких страниц сами выставляют дату модификации.
Возможно сделать страницу всегда обновленной:
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
Location.
"Location :" абсолютный URL
Полезный заголовок, который перенаправляет броузер на указанный адрес. Его
действие сравнимо с META тегом Refresh:
<META HTTP-EQUIV="Refresh" CONTENT="0; URL=someURL">
Например, этот заголовок может быть использован так:
if ($login != $admin_login) header("Location: http://www.server.com/login.php");
else header("Location: http://www.server.com/admin.php?login=$login");
if (!headers_sent()) exit("Произошла ошибка! Пройдите <a
href='http://www.server.com/login.php'>авторизацию</a> заново");
Мы разобрали конечно же не все HTTP заголовки (на это нам несколько уроков не
хватит), но рассмотрели наиболее полезные и самые используемые. Полный список
HTTP заголовков вы можете посмотреть здесь.
PHP и HTTP: cookie.
На предыдущем уроке мы разобрали взаимосвязь протокола HTTP и языка PHP на
уровне HTTP заголовков. На этом уроке мы познакомимся еще с одним специфическим
HTTP заголовком - cookie.
Что такое cookies?
Дело в том, что в процессе развития www-технологий и внедрения языков
программирования в Интернет, перед разработчиками программ возникла очень
серьезная проблема - как сохранять результаты выполнения алгоритма для каждого
конкретно взятого пользователя на долгое время? Сам по себе протокол HTTP не имеет
возможности фиксирования результатов программных процессов. Использование
сессий также не является решением проблемы, так как их действие прекращается
сразу после разрыва соединения с сервером.
Проблема разрешилась с внедрением механизма cookies (то есть, в переводе с
английского, - "печенье"). Cookies обладают замечательным свойством - они
сохраняются на винчестере пользователя и могут храниться там практически
неограниченное время.
По своей сути cookies - это обычные текстовые файлы, хранящиеся в специальной
директории, используемой броузером (обычно эта папка называется Temporary Internet
Files), и вы можете увидеть их, зайдя в эту директорию (быстрый доступ к ней для
броузера IE осуществляется через пункты меню Сервис -> Свойства обозревателя ->
Временные файлы Интернета -> Настройка -> Просмотр файлов).
Реализация механизма cookies в PHP.
Реализация механизма cookies представлена единственной функцией setcookie(). Как
и в случае с HTTP заголовками, эта функция должна быть вызвана до отправки какихлибо данных удаленному клиенту, не допускаются даже "пустые" символы, то есть
пробел, символы перевода строки и так далее.
Функция имеет следующий синтаксис:
setcookie(имя куки, значение, срок годности, информация о пути, домен,
защищенность)
Все параметры, кроме имени cookie, являются необязательными. Если cookie
посылается только с этим параметром, то она сразу же уничтожается удаленным
клиентом, поэтому сам по себе этот параметр не несет информационной нагрузки.
Полнофункциональной cookie делают два следующих параметра: значение, заложенное
в куке, и время, до которого эта cookie может быть использована.
Значением, которое несет cookie, может быть любая строка ASCII символов. Например,
можно установить cookie с именем и фамилией посетителя, которые он до этого ввел в
поле формы.
$data = $name."||".$surname;
setcookie("username", $data);
Заметьте, что отсылаемые данные должны быть оформлены в виде строки, попытка
прочитать отосланный ранее массив значений ни к чему не приведет.
Cookie, установленная в вышеуказанном примере, будет уничтожена сразу после
закрытия броузера пользователем, так как по умолчанию срок жизни cookie
устанавливается в ноль. Чтобы изменить этот порядок, необходимо указать третий
параметр expire. Определение этого параметра можно произвести двуми способами:

Задать отностительный срок действия с помощью функции time(), к которой
прибавляется время в секундах для хранения cookie. Например, чтобы
определить cookie на два часа необходимо написать:
setcookie("test 1", "это тестовая куки", time() + 3600 * 2); // 3600 - количество
секунд в часе

Второй способ - задание абсолютного срока истечения годности cookie. Он
устанавливается с помощью функции mktime(), которая возвращает конкретную
дату удаления куки. Если необходимо задать срок жизни cookie до полуночи 1
сентября 2003 года, то следует определить cookie так:
setcooikie("test 2", "куки с абсолютной датой удаления", mktime(0, 0, 0, 9, 1,
2003);
Необязательный параметр пути ограничивает область действия cookie в пределах
определенных директорий. Причем в эту область входят все пути, начинающиеся со
значения в этом параметре. Например:
setcookie("test 3, "", 0, "/mus");
Мы установили куку, пропустив параметры значения и времени и определив область
действия всеми путями, начинающимися со строки "/mus", то есть сюда входят и
директория "/music/", и "/museums/". Чтобы однозначно определить путь, необходимо
завершить путь слешем. То есть для ограничения действия куки каталогом "/mus",
необходимо было написать в параметре "/mus/".
Следующим опциональным параметром является параметр определения действия cookie
в пределах указанного домена. Причем значению этого параметра "someserver.com"
соответствует только сайт с адресом http://someserver.com, а значению
".someserver.com" соответствуют уже и http://someserver.com, и
http://mail.someserver.com, и http://my-someserver.com, то есть все домены,
кончающиеся данной строкой.
Последний параметр функции setcookie() указывает на то, что данная cookie должна
быть послана через защищенное соединение (HTTPS). Этот параметр необходим при
установке cookie с конфеденциальными данными.
setcookie("my_cookie", $value, time() + 3600 * 24 * 5, "/", ".myphp.dem.ru", 1);
Чтение cookie.
Обращение к установленной cookie идет через ее имя. Например, продолжая пример
выше, прочесть cookie можно следующим образом:
echo "У вас сохранены следующие данные:<br>";
echo $my_cookie;
Обращение к данным, сохраненным в cookie, также может происходить через массив
$HTTP_COOKIE_VARS. Он схож с другими подобными массивами, такими как
$HTTP_POST_VARS и другими, и содержит все значения, прочтенные из cookie.
Удаление cookie.
Удаление cookie производится отправкой новой cookie с именем удаляемой без какихлибо дополнительных параметров.
Например:
$data = $my_cookie;
setcookie("my_cookie");
echo "Следующие данные были удалены:<br>" . $data;
На этом закончим сегодняшний урок. До встречи.
Работа с удаленными документами.
Служебные секретные документы
существуют не для того,
чтобы защищать секреты, а для
того, чтобы защищать служащих.
Джонатан Линн и Энтони Джей
Интересная особенность PHP заключается в том, что он может выступать не только в
роли обработчика и исполнителя сценария, но и в качестве клиента сети. Если до этого
урока мы манипулировали только локальными объектами, такими как файлы и
директории, то на этом мы познакомимся со способами взаимодействия с удаленными
объектами.
Обращение к веб-документам.
Обращение к документам, расположенным на удаленном сервере, производится уже
знакомой нам функцией fopen(). Как вы понимаете, в этом случае она может быть
вызвана только в режиме чтения.
Далее с полученным дескриптором файла можно делать все то же, что и с
дескриптором локального файла. Например, можно вывести все содержимое на экран
пользователя:
if (!($fp = fopen("http://someurl.com/", "r") ) exit("Не могу соединиться");
fpassthru($fp);
Функцией fpassthru() мы просто вывели все содержимое веб-документа, то есть на
своей странице мы получим точную копию желаемой страницы.
Извлечение информации из документа.
Чаще всего работа с удаленными файлами проводится для получения необходимой
информации из целого документа. Примером может служить установленный на сайте
прогноз погоды, получаемый, например, с Метеобюро, либо курс доллара с
http://www.rbc.ru/, либо афоризм дня или лучший анекдот. Все это и многое другое
можно создать с помощью сетевых возможностей PHP.
Извлечение части информации производится с помощью регулярных выражений.
Использование регулярных выражений основано на расположении желаемых данных в
HTML-коде документа.
Скрипт по показу анекдотов.
В качестве иллюстрации к вышесказанному предлагаю написать скрипт, который будет
выводить анекдоты с сайта Анекдоты на Кроватке в соответствии с указанным
пользователем рейтингом.
Сперва напишем функцию, которая выводила бы форму для задания диапазона
рейтингов анекдотов для вывода.
<?
function show _form() {
?>
<form method="post" action="">
<table w idth="200" border="0" cellspacing="0" cellpadding="2">
<tr>
<td>Минимальный рейтинг</td>
<td>
<select name="min">
<?
for ($i = 0; $i < 11; $i++) {
echo "<option";
if ($i == 5) echo " selected";
Мы вставили элемент Select, с помощью которого пользователь будет определять
нижний предел для рейтинга. Теперь добавим второй Select - для верхней границы
рейтинга.
<tr> <td>Максимальный
рейтинг</td> <td>
<select name="max">
<?
for ($i = 6; $i < 17; $i++) {
echo "<option";
if ($i == 16) {
echo " value='no' selected>";
echo "Неограниченный</option>\n";
continue;
}
echo ">".$i."</option>\n";
}
Теперь напишем главную функцию, которая будет читать удаленный документ,
обрабатывать его и выводить в соответствии с указанными пожеланиями пользователя.
function show _anecdots($min, $max) {
if ( !($fp = fopen("http://w w w .umora.ru/", "r")) ) exit("Не могу соедин
$file = fread($fp, 1500000);
$file = trim(chop($file));
$file = convert_cyr_string($file, "k", "w ");
preg_match_all("!<center><hr w idth=80%></center>([^<]+)<br><br>!s
preg_match_all("!Рейтинг: <b>([^<]+)</b>!si", $file, $rating);
Данная функция начинается с получения дескриптора удаленного файла. Если
происходит ошибка, выходим из скрипта, выводя соответствующее предупреждение.
Далее мы считываем файл, указывая немыслимо большой размер документа, чтобы
гарантированно считать весь файл. Напомню, что мы не можем использовать функцию
filesize(), так как она работает только с локальными объектами.
Следующей строкой мы обрезаем пустые символы по краям текста, а также удаляем
повторяющиеся пробелы. Обратите внимание на следующую строку: меняем кодировку
текста с koi-8 на windows-1251, так как файл читается с удаленного сервера именно в
этой нежелательной для нас кодировке.
После идет ключевой момент всей функции: с помощью регулярных выражений мы
сначала помещаем в массив $anecdots найденные анекдоты, а в массив $rating - их
рейтинг. Рассмотрим, как это делается.
Заглянув в HTML-код страницы с анекдотами, мы увидим, каким образом они
располагаются:
...
<br>
<center><hr width=80%></center> Сам анекдот.
<br><br>
Рассказал(а) <a href="mailto:some@email.ru"><b>Имя
рассказавшего</b></a>      
Рейтинг: <b>Число</b>      
Оценить: ...
Отсюда мы можем сделать вывод, что интересующие нас данные находятся между
выделенными последовательностями символов. Причем мы можем с достаточной долей
уверенностью утверждать, что они однозначно определяют необходимую информацию:
для тела анекдота таким уникальным указателем является тег <hr ...>, а для рейтинга слово "Рейтинг". Больше таких последовательностей символов в документе не
встречается.
Исходя из полученных умозаключений, мы составили регулярные выражения, причем с
использованием подшаблонов, заключенных в скобках. Напомню, что функция
preg_match_all() помещает строки, соответствующие подшаблонам, в массив с
индексом 1, то есть сами анекдоты (без посторонних HTML тегов) будут храниться в
массиве $anecdots[1], а числа-рейтинги - в массиве $rating[1].
Ну, а теперь осталось самое легкое - вывести подходящие условию анекдоты на экран
пользователя:
if ($max == 'no') $max = 999;
for ($i = 0; $i < sizeof($rating[1]); $i++) {
if ((int)$rating[1][$i] >= $min && (int)$rating[1][$i] <= $max) {
echo $anecdots[1][$i] . "<br><br>";
echo $rating[0][$i] . "<hr>";
}
}
}
Если максимум указан как неограниченный, то верхний предел мы устанавливаем
невероятно большим. Далее мы просматриваем весь массив с рейтингами, и если его
элемент находится в заданном диапазоне, то мы выводим анекдот, соответствующий
этому рейтингу, и внизу пишем сам рейтинг, причем используем массив $rating[0], то
есть выводим строку, соответствующую всему шаблону поиска рейтинга. Таким
образом, у нас выведется не просто цифра, а "Рейтинг: <b>цифра</b>".
И, наконец, нам осталось только грамотно вызвать написанные функции:
if ( isset($min) && isset($max) ) show_anecdots($min, $max);
else show_form();
?>
Я хотел дать возможность читателям испытать скрипт в действии, но, к сожалению,
хостинг этого сайта не дает возможности обращения к файлам других серверов. Так что
пробуйте сами.
До встречи на следующем уроке!
Урок 13. Принципы ООП и классы.
Масса явлений, оставшихся без сознания,
становятся объектами представлений.
Эмиль Дюркгейм
Сегодня мы с вами познакомимся с принципами объектно-ориентированного
программирования и их реализацией в PHP.
Многие недолюбливают эту тему и изучают ее поверхностно. Но все же без понимания
ООП будет трудно создать что-то существенное, тем более практически все
современные языки программирования являются обектно-ориентированными. Тем
самым, поняв эту тему однажды, не придется к ней возвращаться, если вы захотите
изучить другой язык.
Принципы ООП.
Давайте вдумаемся в эти слова "объектно-ориентированный". Они подразумевают, что
какая-то деятельность направлена на определенный объект. Объектами в нашей
повседневной жизни выступают все окружающие нас предметы: автомобили, книги,
стол, CD, домашние тапочки в конце концов.
Рассмотрим такой объект, как телевизор. Внутри этого объекта находятся множество
других объектов: микросхемы, провода, электронно-лучевая трубка и так далее. Но при
взаимодействии с телевизором мы об этом даже и не задумываемся. В этом
заключается первый принцип ООП - инкапсуляция.
Мы также знаем, что, нажав на определенную кнопку, мы включим телевизор, а
удерживая другую - увеличим или уменьшим громкость. При этом от объекта мы
получаем только результат его работы, не задумываясь о его внутренних процессах.
Это составляет второй принцип - абстракцию.
Наконец, третий принцип, составляющий парадигму ООП, называется наследованием.
Он заключается в том, что, например, цветной телевизор произошел от черно-белого, а
телевизор с плазменным экраном - от обыкновенного. При этом каждый потомок
наследовал свойства и функции предшественника, дополняя их своими, качественно
новыми. Наследование позволяет расширить возможности объекта, не создавая при
этом новый объект с нуля.
Классы в PHP.
Класс служит шаблоном для объекта. Он создается следующим образом:
class Myclass {
// определение класса
}
Класс может содержать внутри себя собственные определяющие этот класс
переменные, называемые свойствами класса. Кроме того, класс, как правило, содержит
функции, которые называются методами класса. Для разграничения методов и свойств
следует запомнить, что методы ассоциируются с глаголами в нашем обыденном языке,
а свойства - с прилагательными или существительными. Тем самым метод всегда
подразумевает действие, а свойство - признак объекта.
К примеру, возьмем такой объект, как шариковая ручка. Его свойствами могут являться
слова "пластмассовая", "синяя", "новая" и так далее. А методом будет лишь то, что она
пишет (конечно, если вы не найдете другой способ ее применения).
Доступ к свойствам и методам класса достигается с помощью указания пути к нему, где
первым его элементом является название класса, а вторым - название самого свойства
или метода. Элементы пути разделяются знаком ->. Давайте рассмотрим, как это
делается:
class Array_class {
var $array = array();
// Определение свойства
function getUniqSum() {
// Получает сумму уникальных элементов
return array_sum(array_unique($this->array));
}
function getSortedMerge() { /* Возвращает отсортированный массив из
ключей и элементов массива */
$result = array_merge(array_keys($this->array), array_values($this>array));
sort($result);
return $result;
}
}
Вот мы написали небольшой класс работы с массивами. Он имеет одно свойство $array. Обратите внимание, как мы прописываем к нему путь: как уже говорилось,
первым элементом пути должно быть название класса, но ввиду того, что это свойство
и так находится в самом классе, то название меняется на слово this.
Далее мы определяем два метода класса, представляющие собой функции getUniqSum()
и getSortedMerge(). Обратите внимание, что свойства классов всегда являются
глобальным в пределах этого класса, то есть нам не требовалось в каждой функции
писать global $this->array. Также заметьте, где ставится знак $: его место в самом
начале описания пути. Причем он ставится даже перед указанием пути к методу класса
(то есть фактически к функции).
Теперь создадим новый класс, который будет наследовать все возможности
родительского класса.
class Advanced_array extends Array_class {
function advanced_array($size) { /* заполняет массив подряд
идущими числами, чередуя их знаки */
$z = 1;
for ($i = 0; $i < $size; $i++) {
$this->array[$i] = $i *$z;
$z = - $z;
}
}
function getSizeofMerge() {
/* возвращает число
неповторяющихся элементов массива, полученного getSortedMerge() */
$merge = $this->getSortedMerge();
return sizeof(array_unique($merge));
}
}
Обратите внимание на функцию с названием самого класса. Это так называемый
конструктор класса, который автоматически вызовется при создании экземпляра
класса.
Теперь рассмотрим ход работы с полученными классами.
$my = new Array_class;
$my->array = array(1, 2, 6, 1);
echo $my->getUniqSum();
$my = new Advanced_array(4);
echo $my->getSizeofMerge();
Сначала мы создаем новый экземпляр класса Array_class и определяем его свойство
array, затем выводим сумму элементов без учета повторяющихся (в нашем примере
выведется 9). Далее мы создаем экземпляр класса Advanced_array, который наследует
все свойства и методы Array_class.
Обратите внимание, что при создании класса не требуется указывать никаких
параметров, но так как у Advanced_array есть конструктор, требующий параметры, то в
скобках мы указываем эти данные.
Таким образом не пришлось определять свойство класса Advanced_array, так как за
нас это сделал конструктор. Затем мы применяем метод getSizeofMerge(), который сам
по себе использует метод родителя getSortedMerge(), и выводим полученные данные.
Использование классов.
По правде говоря, PHP не является в полной мере объектно-ориентированным языком,
поэтому очень часто можно обойтись и без использования классов, но иногда они
сильно облегчают жизнь.
Но не стоит и злоупотреблять ими, так как неоправданно большое количество
используемых классов не только затрудняет понимание кода программы, но нередко
приводит к снижению ее производительности.
А на сегодня все. До встречи.
Урок 14. Регулярные заявки.
Этот урок немного не вписывается в логическую цепь наших занятий, а был создан
"под давлением общественности", то есть по просьбам читателей, и будет посвящен
регулярным выражениям и функциям работы с ними. Наверное, мои расчеты, что
статьи Александра Грималовского "Регулярные выражения" будет достаточно для
понимания этой темы, оказались неверны.
Итак, надеясь, что вы уже прочитали вышеуказанную статью, начинаем сегодняшний
урок.
Сами регулярные выражения не являются новинкой, превнесенной PHP. В том или ином
виде они используются уже достаточно большой срок и являются очень мощным
механизмом работы со строковыми данными.
Регулярное выражение представляет из себя некий шаблон, который используется для
различных действий в зависимости от функции, использующей это регулярное
выражение. Для задания шаблона используются специальные символы.
Ниже представлена краткая таблица специальных символов и их значений.
Символы Значение
Примеры
Символы, указывающие расположение искомого элемента в строке.
Указывает на то, что символы после знака должны
^заголовок
^
находиться в начале строки.
$
Символы до знака должны находиться в конце строки. содержание$
Escape последовательности.
\.
Шаблону соответствует знак точки в строке.
Смеркалось\.
\n
Символ перевода строки.
Строка\nЕще строка
\r
Символ возврата каретки.
Текст\r
\t
Символ табуляции.
\tКрасная строка
\v
Символ вертикальной табуляции.
\vнекоторый текст
Задание группы символов.
Задают группу символов. Соответствует любому
[а-яa-z_123]
символу из перечисленных в группе. Есть
[]
возможность задания диапазона символов с помощью
знака - (минус, тире).
В группе символов соответствует отрицанию
[^\n\t]
^
последующих символов, то есть указывает символы,
не соответствующие шаблону.
Соответствует любому символу, кроме перевода
.оза
.
строки.
Количественные показатели
Символ перед знаком не присутствует либо
текст\n*дальше
*
повторяется любое число раз.
текст
?
Символ перед знаком встречается ноль или один раз. длинн?ое
Предыдующий символ повторяется один или большее
число раз.
{n}
Символ перед знаком повторяется n-ое число раз.
Задает диапазон числа повторений предыдущего
{min,max}
символа.
Предыдущий символ повторяется min или большее
{min,}
число раз.
Логическое определение.
Эффект подобен оператору || (OR) в логическом
|
выражении.
+
()
Логическая группировка выражений.
100+
длин{2}оше{3}
^ab{3,7}
слово.{5,}
раз|два|три
(может)+
повторяться
Функции работы с регулярными выражениями.
Необходимо сказать, что PHP располагает как собственным механизмом работы с
регулярными выражениями (POSIX), так и заимствованным у другого серверного языка
программирования Perl. Внешне их легко различить по названиям функций: функции
первого типа начинаются с символов "ereg", а второго - "preg".
Но названия функций не единственное их отличие. Прежде всего они содержат
некоторые различия в синтаксисе регулярных выражений. Так, Perl-подобные функии
требуют разделители:
$str = "регулярное выражение";
// просто строка
$preg = preg_replace("/р.+е/i", "<i>[вырезано]</i>", $str);
$ereg = eregi_replace("р.+е", "<i>[вырезано]</i>", $str);
echo $preg."<br>".$ereg;
Как видите, мы используем функции замены части строки с помощью регулярных
выражений. Обратите внимание на шаблон функции preg_replace: в качестве
разделителя здесь выступают слеши, причем после закрывающего разделителя следует
модификатор i, указывающий, что шаблон является нечувствительным к регистру. Тот
же эффект достигается при использовании POSIX функции с суффиксом i
(eregi_replace).
Результат выполнения этих функций одинаков:
[вырезано]
[вырезано]
Функция preg_replace в нашем примере проявила так называемую "жадность", и
охватила всю строку, которая начинается с буквы "р" и заканчивается "е". Заставить
функцию не "жадничать" помогает модификатор U. В этом случае результат ее
выполнения будет:
[вырезано] вы[вырезано]ние
Функция нашла минимальное расстояние между буквами "р" и "е" и заменило его
указанной строкой.
Функция eregi_replace также проявила "жадность", но изменить этот порядок уже
нельзя, так как в POSIX-функциях не предусмотрено использование модификаторов.
Бегло рассмотрим и другие функции работы с регулярными выражениями.
Функции
ereg, eregi,
preg_match
Синтаксис
функция(pattern,
string, [regs])
preg_match_all preg_match_all(pattern,
subject, matches,
[order])
split, spliti,
функция(pattern,
preg_split
string, [limit])
preg_grep
preg_grep(pattern,
input)
Описание
Ищет в строке string соответствия с
регулярным выражением pattern, и сохраняет
их в массиве regs (если указано).
Осуществляет глобальное сопоставление с
шаблоном, результаты заносит в matches.
Разбивает строку в массив посредством
регулярного выражения.
Возвращает массив из элементов массива input,
соответствующих шаблону pattern.
Примеры на регулярные выражения.
Перевод времени в стандартное время Unix.
Предположим, у нас в базе данных или в другом источнике хранится дата в следующем
формате:
часы:минуты:секунды - день.месяц.год
Но по некоторым причинам (например, произведен редизайн сайта) нам понадобилось
отображать дату в следующем виде:
день.месяц.год часы:минуты
Как вы понимаете, вручную заниматься этим - сумасшествие, так что напишем
сценарий, который будет на первом этапе приводить дату к виду
часы:минуты:секунды месяц/день/год, а затем с помощью функции
strtotime() переведить эту запись в стандартное время UNIX, которое мы сможем
отображать, как захочется.
Самое интересное - первый этап. Он-то нас и интересует в плане использования
регулярных выражений.
$str = "12:57:43 - 10.03.02"; // $str содержит некоторую дату
$str = preg_replace("!(\d{2})\.(\d{2})\.(\d{2})!", "\\2/\\1/\\3", $str);
С помощью регулярного выражения мы изменяем формат записи дня, месяца и года,
причем каждый этот элемент обособляем скобками. Во втором параметре функции мы
ссылаемся на найденные соответствия в скобках. Вот как это происходит:




\\0 - содержит строку, соответствующую всему шаблону (в нашем примере
"10.03.02").
\\1 - содержит символы, соответствующие только первому элементу,
заключенному в скобки (то есть "10").
\\2 - содержит символы, соответствующие только второму элементу,
заключенному в скобки (то есть "03").
и так далее.
На этом этапе мы получем дату "12:57:43 - 03/10/02". Теперь доводим это до конца.
$str = str_replace("-", "", $str); // вырезаем знак "-"
$time = strtotime($str);
Теперь можно использовать переменную $time, как заблагорассудится.
На этом и закончим наш урок. Встретимся на следующем!
Урок 15. Основы MySQL.
Человек - единственное животное на свете,
способное смеяться и рыдать, ибо из всех живых
существ только человеку дано видеть разницу
между тем, что есть, и тем, что могло бы быть.
Уильям Хэзлитт
Вот мы и дошли с вами до этого долгожданного для многих момента, когда наши вебприложения (а уже не просто скрипты), разрастаясь, уже не могут удовлетвориться
теми средствами, которые использовались ранее. Работа с файлами становится
непомерно мучительной, те разбухают до необыкновенных размеров, делая
невозможным их дальшейшее использование. Команды работы с файлами и
содержащимися в них данными занимают сотни строк кода, и вы в конце концов
понимаете, что так продолжаться больше не может. Требуется более удобное и
надежное хранилище для многомегабайтной информации. И в итоге приходите к
использованию баз данных, что вполне закономерно.
При программировании на PHP наиболее часто используемой базой данных является
MySQL. И это неспроста. Их взаимодействие реализовано на высшем уровне, так что
иногда кажется, что они были специально созданы друг для друга.
Установка MySQL.
Прежде, чем мы начнем знакомство с MySQL, вам необходимо установить эту базу
данных на своей машине. Скачать дистрибьютив можно с этой страницы. Рекомендуется
выбирать те версии, напротив которых написано "stable release". В помощь прочтите
главу из официальной документации "MySQL Installation".
Итак, надеясь, что инсталляция MySQL прошла успешно, продолжаем наш урок и
сейчас научимся без посредников в лице PHP, через консоль взаимодействовать с этой
базой данных. Для этого зайдите в директорию, в которую вы установили MySQL,
найдите там папку bin и откройте в ней файл mysql.exe.
В этом окне мы и будем обучаться MySQL.
Краткий справочник по командам MySQL.
Прежде всего настоятельно рекомендуется прочитать главу из документации "Tutorial
Introduction",где изложены основы взаимодействия с MySQL, и статью "Реализация
языка SQL в СУБД MySQL". Мы же рассмотрим лишь небольшую часть команд MySQL.
Сначала давайте рассмотрим структуру баз данных. Схематично она выглядит
следующим образом:
база данных
таблица
таблица
таблица
база данных
таблица
...
база данных
таблица
1
1.1
1.2
1.n
2
2.1
n
n.1
Таким образом, данные по сути содержатся в таблицах, которые в свою очередь
объединены в группу базы данных. Тем самым, чтобы начать работу с MySQL нам
требуется прежде всего создать базу данных, с которой мы будем взаимодействовать.
CREATE.
Итак, в консоли пишем запрос:
CREATE database myphp;
Мы говорим MySQL создать базу данных с именем "myphp", причем регистр ключевых
слов несущественен, но порядок слов в запросе не может быть изменен.
Теперь выберем только что созданную базу для работы с ней:
USE myphp;
И, как вы наверное уже догадались, нам необходимо создать таблицу, с которой мы
будем работать. Пусть это будет таблица "lessons".
CREATE table lessons (
id int auto_increment primary key,
title varchar(250),
body mediumtext,
pub_date datetime default now(),
enable enum('0', '1') default '1'
);
Заметьте: то, сколько строк занимает запрос, несущественно: концом запроса является
лишь символ ";".
Итак, мы создали таблицу lessons с 5 полями: id урока, представяющий собой
уникальное число большее нуля, которое будет генерироваться автоматически при
добавлении новой записи в таблицу путем увеличения максимального id на единицу
(auto_increment); title - название урока длиной до 250 символов; body - собственно,
сам урок, представляющий собой текст; pub_date - дата и время публикации урока;
enable - флаг, который указывает, доступен ли урок посетителям, поле может иметь
только два значения: '0' и '1', причем по умолчанию установлено '1'.
INSERT.
Теперь все готово, и мы можем приступить непосредственно к работе с данными.
Сначала добавим в таблицу новую запись:
INSERT INTO lessons VALUES (
"",
"Начала",
"Прежде всего нужно сказать: PHP-скрипт для его выполнения должен быть
заключен в следующие последовательности символов...",
'now()',
"1"
);
Итак, в таблице lessons у нас появилась новая запись, значения которой для каждого
поля указываются в скобках после слова values (...). Причем заметьте, что мы
пропустили ("") поле, которое заполняется автоматически - это поле id. Обратите
внимание, на MySQL-функцию now(), которая возвращает текущее значение времени.
Но чтобы не писать пустых строк, также можно указать MySQL, в какие именно поля
таблицы мы хотим сохранить данные. Например, введем таким образом данные урока
2:
INSERT into lessons (title, body) values (
"Формы всего сущего",
"На прошлом уроке мы научились передавать данные в php скрипт..."
);
Все остальные поля, не указанные нами, будут заполнены автоматически, причем для
полей pub_date и enable применятся значения по умолчанию.
SELECT.
Теперь давайте посмотрим, что у нас оказалось в базе данных. Для этого применем
команду SELECT, которая выводит указанные данные на экран.
SELECT * from lessons;
Значок "*" указывает, что мы хотим увидеть все поля таблицы lessons. Но также можно
производить и выборочный вывод:
SELECT title, pub_date from lessons;
Также возможно выбирать не все записи в таблице, а удовлетворяющие определенным
условиям:
SELECT title FROM lessons WHERE id = '1';
Тем самым мы выберем название только первого урока.
Также мы можем отсортировать данные перед выводом, используя команду ORDER.
SELECT id, title FROM lessons ORDER BY title;
Сортировка в обратном порядке производиться путем указания директивы DESC.
MySQL позволяет ограничить количество записей для вывода. Для примера выведем id
и название последнего добавленного урока, подразумевая, что мы не знаем, какой id
он имеет:
SELECT id, title FROM lessons ORDER BY id DESC LIMIT 1;
Сначала мы отсортировали все записи по id в обратном порядке, так что запись с
максимальным id оказалась первой, и вывели только одну эту запись. Должна
возвратиться таблица с id, равным 2, и title "Формы всего сущего".
MySQL также предоставляет функции COUNT(поле) для подсчета элементов,
занесенных в указанное поле, MAX(поле) и MIN(поле) для определения максимального
и минимального элемента соответственно, а также SUM(поле) для подсчета суммы
элементов поля. К примеру:
SELECT COUNT(*), MAX(pub_date), SUM(id) FROM lessons;
UPDATE.
Предположим, что по какой-либо причине мы хотим на время скрыть урок 2 от
посетителей. Для этого изменим поле enable, установив его равным нулю.
UPDATE lessons SET enable = '0' WHERE id = 2;
Таким образом можно изменить любое поле таблицы.
ALTER.
Команда ALTER TABLE позволяет изменить уже созданную таблицу. Например, в нашей
таблице lessons после поля body вставим поле author.
ALTER TABLE lessons ADD author VARCHAR(200) DEFAULT 'Артем Акатов' AFTER
body;
А теперь удалим эту колонку:
ALTER TABLE lessons DROP author;
Тем самым мы свободно можем манипулировать как создаваемыми объектами, так и
уже давно созданными.
Как видите, синтаксис MySQL не отличается сложностью. Если вы более или менее
владеете английским языком, вы без проблем освоите команды MySQL, к тому же они
вполне интуитивно понятны. Мы разобрали лишь часть из них, для получения полной
информации прочтите соответствующую официальную документацию.
А на следующем уроке мы займемся уже работой с MySQL посредством PHP. До встречи!
Урок 16. MySQL и PHP.
Человек живет только для того, чтобы учиться...
Карлос Кастанеда. Учение Дона Хуана.
Итак, на прошлом уроке мы познакомились с базой данных MySQL и рассмотрели
использование некоторых ее операторов. Теперь же настало время применить
полученные знания в программировании на PHP, и на сегодняшнем уроке мы
рассмотрим взаимодействие MySQL с этим языком вебпрограммирования.
Соединение с MySQL.
Прежде, чем начать работу с базой данных, необходимо создать соединение с сервером
MySQL. Этим и занимается функция mysql_connect(). Она должна быть вызвана до
каких-либо других функций работы с MySQL, в противном случае они возвратят
ошибки.
Аналогом функции mysql_connect является функция mysql_pconnect. Их отличие
состоит в том, что последняя открывает постоянное соединение с MySQL, благодаря
чему снижаются расходы времени и ресурсов на создание новых соединений. Это
постоянное соединение не уничтожается ни функцией закрытия соединения, ни какимлибо другим образом. Использование этой функции требуется в случае, если вашему
веб-приложению приходится часто обращаться к MySQL. Но учтите, что mysql_pconnect
работает только на сервере, где PHP установлен как модуль.
Выбор или создание базы данных.
Вторым шагом при работе с MySQL чаще всего становится выбор базы данных для
дальнейшей работы, либо создание новой базы данных.
Создание базы данных осуществляет функция mysql_create_db().
mysql_create_db(имя базы данных, [идентификатор соединения]);
Имя базы данных может состоять из любых символов, используемых для задания имени
директорий на сервере, исключая символы '/', '\', '.'.
Идентификатор соединения есть результат выполнения функции mysql_connect или
mysql_pconnect.
Для дальшейшей работы с базой данных вы должны ее выбрать. Выбор базы данных
осуществляется функцией mysql_select_db(). Ее синтаксис выглядит следующим
образом:
mysql_select_db(имя базы данных, [идентификатор соединения]);
Запросы в MySQL.
Пожалуй, самая универсальная функция для работы с MySQL - mysql_query(). Она
может заменить практически любую функцию работы с базами данных MySQL,
исключая mysql_connect() и mysql_close().
В качестве одного из аргументов ей передается запрос MySQL - наподобие тех,
которыми мы занимались на прошлом занятии.
Например, с помощью нее мы можем создать базу данных, не используя
вышерассмотренную функцию mysql_create_db().
$link = mysql_connect('somehost.ru', 'artem', '123456') or die("Не могу
соединиться");
mysql_query("CREATE database my_db", $link) or die("Ошибка запроса");
Предположим, мы хотим ввести данные в базу данных, созданную на прошлом уроке.
$link = mysql_connect('somehost.ru', 'artem', '123456') or die("Не могу
соединиться");
mysql_select_db('myphp', $link);
$query = "INSERT INTO lessons (title, body) VALUES (
'Выражаемся по-ПиЭйчПовски',
'Итак, на прошлом уроке я обещал начать урок 3 с изучения следующего,
четвертого типа данных...'
)";
mysql_query($query, $link);
А теперь давайте выберем данные, которые ввели на этом и прошлом уроке. Это
делается следующим образом:
// предположим, что уже соединились с MySQL
// и выбрали базу данных 'myphp'
$query = "SELECT title, body FROM lessons";
$result = mysql_query($query, $link);
while ($row = mysql_fetch_array($result)) {
echo "<h1>".$row['title']."</h1><p>";
echo $row['body']."</p>";
}
Заметьте, что mysql_query возвращает не сам результат, а идентификатор результата,
который может быть использован другими функциями. Таким образом, $result в
примере выше не содержим необходимую нам информацию в явном виде, его значение
- лишь ссылка на нее.
Извлечение информации.
Чтобы извлечь информацию в понятную нам форму, необходимо воспользоваться
функциями mysql_fetch_array(), mysql_fetch_row() или другими более
специфическими функциями.
Функция mysql_fetch_array(), использованная в примере выше, осуществляет
выборку записи в виде ассоциативного массива. Заметьте, эта функция, как и другие
функции извлечения информации, не работает непосредственно с MySQL, а лишь
обрабатывает полученный результат, поэтому ссылка на идентификатор соединения
для работы этой функцией не требуется. Ее обязательный аргумент - лишь
идентификатор результата, полученный функцией mysql_query().
За каждый раз своего выполения эта функция обрабатывает лишь одну строку
результата, тем самым, чтобы получить данные из всего результата выполнения
mysql_query, необходимо использовать функцию в цикле. Например:
$result = mysql_query("SELECT id, title FROM lessons", $link);
$first_row = mysql_fetch_array($result);
echo "Первая запись:<br>".$first_row['id']." - ".$first_row['title']."<br>";
echo "Остальные записи:<br>";
while ($row = mysql_fetch_array($result)) {
echo $row['id']." - ".$row['title'].'<br>';
}
Данный пример может вывести у вас непонятные символы - текст в неизвестной
кодировке. Это связано с тем, что MySQL использует по умолчанию кодировку latin,
тогда как отображение данных происходит на веб-странице с кодировкой, скорее
всего, windows-1251. Тем самым, данные введенные посредством одной кодировки и
показанные - другой, превращаются в абракадабру. Но если вы будете вводить данные
через веб-интерфейс и выводить через него же, то "смешения" кодировок не
произойдет, и текст будет выводиться в нормальном виде.
Функция mysql_fetch_row() аналогична вышерассмотренной функции, за исключением
того, что она возвращает пронумерованный массив, а не ассоциативный.
В случае, когда идентификатор результата содержит значения небольшого количества
полей, или, что еще лучше, одного, то тогда становится удобным размещать данные не
в массиве, а в отдельных переменных. Этим и занимается функция mysql_result().
Она имеет несколько более сложный синтаксис, чем mysql_fetch_array, поэтому
рассмотрим его подробнее:
mysql_result(идентификатор результата, номер записи, [поле]);
Номер записи - это номер строки, из которой требуется получить данные. Поле - это
название или номер поля указанной записи, из которой необходимо получить данные.
$result = mysql_query("SELECT title FROM lessons", $link);
$title1 = mysql_result($result, 0);
$title2 = mysql_result($result, 1);
Так как мы сделали выборку только по одному полю, то третий аргумент функции
mysql_result() необязателен.
Закрытие соединения с MySQL.
При завершении работы скрипта PHP самостоятельно закрывает соединение с сервером
MySQL, так что вы можете не заботиться об этом. Но все же правилом хорошего тона
считается принудительное закрытие соединения после выполнения всех необходимых
операций с базами данных.
Закрытие соединения осуществляется с помощью функции mysql_close().
В заключение.
Конечно, все функции PHP для работы с данными MySQL невозможно охватить в
пределах одного урока. Мы разобрали самые важные и наиболее используемые из них.
Настоятельно рекомендую ознакомиться с другими в документации PHP.
На этом и закончим наш урок. До встречи.
Урок 17. Шаблоны.
Разделение оформления и содержания - извечная проблема веб-разработчика. Для
держателя небольшого сайта в несколько страниц такой проблемы не возникает.
Изменение дизайна, либо другая правка внешнего вида страниц для него не
представляет трудностей. Однако для крупных веб-ресурсов, порталов смешение этих
двух важнейших компонентов сайта: оформления и содержания - просто недопустимо.
Иначе сайт становится настолько неповоротлив, что владелец ресурса теряет над ним
управление.
Столкнувшись однажды с такой проблемой, сайтостроители начали ломать голову над
тем, как сделать так, чтобы информация не привязывалась никоем образом к дизайну и
наоборот, чтобы внешний вид всего сайта менялся буквально одним движением руки.
Необходимо сказать, что способа идеального разделения оформления и содержания в
наши дни пока не найдено. Но все же созданы определенные наработки и технологии,
которые позволяют максимально приблизиться к этому идеалу. Прежде всего это
технология использования шаблонов.
Что есть шаблон?
Понятие "шаблон" довольно обширное. Но чаще всего под шаблоном понимается некий
документ (обычно в формате HTML), содержащий в себе все оформление ресурса, либо
только его части, а также специальные метки, которые в процессе генерации страницы
заменяются на информационные данные.
Для работы с шаблонами используются специальные веб-приложения (если говорить о
веб-разработках), которые могут обладать различными свойствами и функциями. Но
чаще всего их роль сводится к тому, чтобы заменять вышеуказанные метки на
информационные блоки и соединять полученные части страниц в единое целое.
Наверное, во всех языках вебпрограммирования есть подобные инструменты для
работы с шаблонами: в Perl это FastTemplate (который, кстати, существует и для PHP,
но его использование не рекомендуется, так как приемлемые для работы версии
относятся еще к третьей версии PHP), в отношении PHP - это крупный и
многофункциональный Smarty, а также ряд других более мелких "шаблонизаторов".
Smarty.
Smarty - одна из самых крупных разработок подобного рода. Реализован он, как и
практически все приложения подобного типа, в виде класса.
Этот "шаблонизатор" имеет огромное количество возможностей: он не только
осуществляет замену выделенных участков шаблона указанными данными, но и
предоставляет использование внутри шаблона исполняемых участков, функций,
конфигурационных файлов и прочее.
Описывать все его возможности и правила использвования не имеет смысла, так как на
это потребуется не один и не два урока, к тому же к нему написана довольно хорошая
документация, а примеры использования прилагаются к самому Smarty.
Smarty - это настоящий монстр по работе с шаблонами. Но, как понимаете, за столь
широкую функциональность приходится платить ресурсами сервера и временем
исполнения скрипта на его основе. В некоторых случаях из-за своей громоздкости
Smarty кажется неповоротливым. Поэтому для сравнительно небольших проектов
использование Smarty - обычно далеко не лучший выбор. В этом случае следует
обратить свой взор на более мелкие и менее функциональные, но тем не менее
достойные инструменты.
Другие инструменты для работы с шаблонами.
Здесь я хочу обратить ваше внимание на "шаблонизаторы" на основе функции eval().
Эта функция расценивает код, заданный в ее аргументе, как код PHP и,
соответственно, исполняет его. Например:
$text = 'Здравствуйте, $name $fam!';
$name = "Артем";
$fam = "Акатов";
eval('echo "' . $text . '";');
Этот примитивный пример работы с мини-шаблоном выведет "Здравствуйте, Артем
Акатов!".
Преимущество данного типа инструментов работы с шаблонами связано с тем, что от
разработчика заранее не требуется определять текст или другую информацию для
меток в шаблоне (в нашем примере таковыми метками служат слова $name и $fam).
Также такие "шаблонизаторы" значительно выигрывают по скорости.
Разовьем пример выше и напишем функцию, которая будет извлекать шаблон из
файла:
function getTemplate($template, $ext = "htm") {
$templatefolder = "templates";
// папка для хранения шаблонов
return str_replace("\"","\\\"", implode("",
file($templatefolder."/".$template.".".$ext)));
}
$name = "Артем";
$fam = "Акатов";
eval('echo "'.getTemplate('test').'";');
Если в файл test.htm мы поместим текст из переменной $text из примера выше, то
результат выполения данного примера будет аналогичен предыдущему.
Для удобства работы с данным типом шаблонов пишутся классы. Одним из самых
удачных классов такого рода является класс Евгения Кучерова.
Попробуем с его помощью создать один из вариантов примитивного "движка" для
данного раздела "Уроки". При этом будем использовать записи MySQL, которые мы
внесли на прошлых уроках.
Прежде всего заготовим шаблон. Для этого откроем окно с HTML-кодом данной
страницы и заменим номер урока на переменную $row[id], вместо названия "Шаблоны"
вставим $row[title], весь текст от первого слова "Разделение" до последнего
"встречи!" заменим на $row[body]. Вместо элементов навигации "Назад, на урок 16" и в
месте, где должно было бы располагаться "Дальше, на урок 18", соответственно ставим
$prev и $next. Сохраним его как page.tpl. Готовый шаблон можно скачать здесь.
Теперь работаем с PHP. Пишем:
require "class.Template.php";
// относительный путь до класса
$tpl = new Template;
$id = (int)getenv("QUERY_STRING"); // извлекаем номер урока из строки
запроса
mysql_connect("host", "artem", "12345");
mysql_select_db("myphp");
$query = "SELECT * FROM lessons WHERE id = '$id'";
$result = mysql_query($query);
$row = mysql_fetch_array($result);
if ($id > 1) $prev = "<a href='?".($id - 1)."'>Назад, на урок ".($id - 1)."</a>";
$query = "SELECT COUNT(*) FROM lessons";
$result = mysql_query($query);
$max = mysql_result($result, 0);
if ($id < $max) $next = "<a href='?".($id + 1)."'>Дальше, на урок ".($id +
1)."</a>";
eval('echo "'.$tpl->get("page").'";');
Это простой пример страницы, имеющей несложную структуру. Но даже на таком
примере видно, как шаблоны упрощают создание веб-приложений.
В заключение.
Конечно, здесь мы разобрали лишь крохотную часть этой обширной темы работы с
шаблонами и рассмотрели лишь малую долю инструментов работы с шаблонами. Но
хотелось бы сказать, что не так уж важно, каким образом вы работаете с шаблонами и
какие средства вы используете, намного важнее то, как вы с ними работаете.
Научиться грамотной работе с шаблонами не так-то просто, понимание этого приходит
только с практикой, и тут я не могу дать определенных советов.
На этом все. До следующей встречи!
Урок 18. Деревья.
С человеком происходит то же, что и с деревом.
Чем больше стремится он вверх, к свету, тем глубже
уходят корни его в землю, вниз, в мрак и глубину - ко злу.
Фридрих Ницше
Сегодняшний урок будет посвящен построению так называемых деревьев данных. Этот
элемент часто используется в вебпрограммировании и просто необходим для таких
приложений, как каталоги сайтов, многоуровневых форумов и других скриптов, в
которых данные хранятся таким образом, что каждый элемент вложен в другой.
Все, кто имел дело с компьютером, имеют представление о деревьях. Например, это
файловая система операционной системы.
Графически дерево можно представить следующим образом:
элемент 1
|
+ - элемент 2
|
|
|
+ - элемент 3
|
+ - элемент 4
|
+ - элемент 5
Уровень вложенности и длина дерева не ограничены.
Создание деревьев.
Существует несколько алгоритмов построения деревьев. Начнем с наиболее
распространенного метода, который эффективен при создании деревьев с небольшой
глубиной вложенности.
Метод заключается в том, что в каждом дочернем элементе сохраняются данные о его
непосредственном родителе, то есть, рассматривая схему выше, элементы 2 и 5
содержат ссылку на элемент 1, а 3 и 4 - на элемент 2.
Запрос на создание таблицы, в которой будут храниться данные по вышеуказанному
алгоритму, может выглядеть следующим образом:
CREATE TABLE catalogs (
cat_id int(11) NOT NULL auto_increment,
parent_id int(11) NOT NULL default '0',
cat_name varchar(200) NOT NULL default '',
PRIMARY KEY (cat_id)
)
Вставим в полученную таблицу несколько записей:
// функция для вставки записей в MySQL
function sql_insert($parent_id, $cat_name) {
$query = "INSERT INTO catalogs(parent_id, cat_name)
VALUES('".(int)$parent_id."', '$cat_name')";
mysql_query($query) or die(mysql_error());
return mysql_insert_id(); // возвращаем id внесенной записи
}
// соединение и выбор базы данных рассматривать не будем
$level[1][0] = sql_insert("0", "Программирование");
$level[2][0] = sql_insert($level[1][0], "Веб программирование");
$level[2][1] = sql_insert($level[1][0], "Системное программирование");
$level[3][0] = sql_insert($level[2][0], "PHP");
$level[3][1] = sql_insert($level[2][0], "Perl");
$level[3][2] = sql_insert($level[2][1], "C++");
$level[4][0] = sql_insert($level[3][2], "Visual C++");
$level[3][3] = sql_insert($level[2][1], "Delphi");
// Вторая ветвь
$level[1][1] = sql_insert("0", "Базы данных");
$level[2][2] = sql_insert($level[1][1], "MySQL");
$level[2][3] = sql_insert($level[1][1], "Oracle");
$level[2][4] = sql_insert($level[1][1], "MS Access");
Теперь выведем полученное дерево:
function get_tree($parent_id = 0, $prefix = "") {
global $out;
$query = "SELECT * FROM catalogs WHERE parent_id = '$parent_id'";
$result = mysql_query($query);
while ($row = mysql_fetch_array($result)) {
$out .= $prefix.$row['cat_name']."<br>";
get_tree($row['cat_id'], $prefix."  ");
}
return $out;
}
echo get_tree();
Так как мы имеем многоуровневое дерево, функцию get_tree() нам приходся
вызывать рекурсивно, для одноуровневых деревьев (не больше одного уровня
вложенности) этого не требуется.
Как видите, алгоритм построения такого рода деревьев очень прост.
К достоинствам данного метода построения деревьев можно отнести то, что каждый
элемент дерева обладает достаточной степенью обособленности, так как с родителем
его связывает значение только одного поля, которое мы без проблем можем изменить и
элементарно поменять тем самым родителя. К недостаткам же - крайне неудобную
работу при высоких уровнях вложенности. Так что этот алгоритм оптимально подходит
для одноуровневых деревьев и деревьев с невысоким уровем вложенности (2 - 3
уровня).
Алгоритм Nested Sets.
Алгоритм Nested Sets - более мощный инструмент работы с деревьями. Класс для
работы с ним вы можете скачать здесь. Архив состоит из двух файлов: dbtree.php собственно, класс для работы с деревьями, и database.php - класс для работы с базой
данных. Во второй файл вы можете добавить свои функции работы с MySQL и всячески
подстраивать для себя, только не изменяйте функции, используемые классом в файле
dbtree.php.
Алгоритм Nested Sets с первого взгляда может показаться сложным, но на самом деле
он предельно прост. Суть его заключается в том, что он не сохраняет id родительского
элемента, а использует три дополнительных поля: cat_left, cat_right и cat_level (имена
полей вы можете изменить). Использует он их следующим образом.
Представьте, что по нашему дереву данных шагает человечек с флажками в руках,
причем надписями на них служат подряд идущие цифры. Проходя элемент дерева,
человечек ставит на него флажок с номером, начиная с первого, доходит до самой
глубины ветки и возвращается обратно, чтобы перейти к другой ветке, но при этом
продолжает ставить флажки на уже пройденных элементах.
Посмотрим, как поставит флажки наш человечек в схеме дерева из начала урока
(синим отмечены флажки, поставленные при первом проходе элемента, зеленым - при
возвращении из ветки).
элемент 1 [1] [10]
|
+ - элемент 2 [2]
|
|
|
+ - элемент
|
+ - элемент
|
+ - элемент 5 [8]
[7]
3 [3] [4]
4 [5] [6]
[9]
Значения синих флажков в таблице MySQL запоминаются в поле cat_left, а зеленых - в
поле cat_right, уровень вложенности - в поле cat_level.
Создадим таблицу с деревом, подобному указанному выше.
include("dbtree.php");
include("database.php");
$db = new CDatabase("your_database_name", "localhost", "root", "12345");
$tree = new CDBTree ($db, "catalogs2", "cat_id");
// создаем таблицу
$query="CREATE TABLE catalogs2(
cat_id int not null auto_increment primary key,
cat_left int not null,
cat_right int not null,
cat_level int not null,
cat_name varchar(200) not null)";
$db->query($query);
// заполняем таблицу
// создаем корневой элемент
$level[0][0]=$tree->clear();
// записываем основную часть дерева
$level[1][0] = $tree->insert($level[0][0],
array("cat_name"=>"Программирование"));
$level[1][1] = $tree->insert($level[0][0], array("cat_name"=>"Базы данных"));
$level[2][0] = $tree->insert($level[1][0],
array("cat_name"=>"Веб программирование"));
$level[2][1] = $tree->insert($level[1][0],
array("cat_name"=>"Системное программирование"));
$level[2][2] = $tree->insert($level[1][1], array("cat_name"=>"MySQL"));
$level[2][3] = $tree->insert($level[1][1], array("cat_name"=>"Oracle"));
$level[2][4] = $tree->insert($level[1][1], array("cat_name"=>"MS Access"));
$level[3][0]
$level[3][1]
$level[3][2]
$level[3][3]
=
=
=
=
$tree->insert($level[2][0],
$tree->insert($level[2][0],
$tree->insert($level[2][1],
$tree->insert($level[2][1],
array("cat_name"=>"PHP"));
array("cat_name"=>"Perl"));
array("cat_name"=>"C++"));
array("cat_name"=>"Delphi"));
$level[4][0] = $tree->insert($level[3][2], array("cat_name"=>"Visual C++"));
// Выводим дерево
$query = "SELECT * FROM catalogs2 ORDER BY cat_left";
$result = $db->query($query);
while ($row = $db->fetch_array($result)){
echo str_repeat("   ", $row['cat_level']).$row['cat_name'].
" <font color='#0033FF'>[".$row['cat_left']."]</font>".
" <font color='#009900'>[".$row['cat_right']."]</font><br>";
}
Использование полей cat_left и cat_right позволяет манипулировать деревом
практически во всех направлениях. Например, заметьте, что у элементов, не имеющих
потомков, значения этих полей отличаются на единицу. А у потомков какого-либо
данного элемента значения cat_left больше значения у выбранного родителя, а
cat_right - меньше, причем, чем больше ветвь потомков, тем больше разность между
значениями cat_right и cat_left у данного родителя (сравните "Веб программирование" и
"Базы данных"). Основываясь на этих данных, сделаем выборку всех элементов, не
имеющих родителей, а также всех потомков элемента "Системное программирование":
$query = "SELECT cat_left, cat_right FROM catalogs2 ".
"WHERE cat_name = 'Системное программирование'";
$result = $db->query($query);
$val = $db->fetch_array($result);
$query = "SELECT * FROM catalogs2 WHERE cat_left > '".$val['cat_left'].
"' AND cat_right < '".$val['cat_right']."' ORDER BY cat_left";
$result = $db->query($query);
echo "<b>Ветка 'Системное программирование'</b><br>";
while ($row = $db->fetch_array($result)) {
echo str_repeat("   ", $row['cat_level']).
$row['cat_name']."<br>";
}
echo "<b>Все элементы, не имеющие потомков</b><br>";
$query = "SELECT * FROM catalogs2 WHERE cat_right - cat_left = 1";
$result = $db->query($query);
while ($row = $db->fetch_array($result)) {
echo $row['cat_name']." ( ".$row['cat_id']." )<br>";
}
Возможности этого класса вышеуказанным не ограничиваются. Можно логически
домыслить, как найти всех родителей данного элемента, заканчивая корневым (что
удобно при создании навигации по разделам), а также множество других
возможностей. Настоятельно рекомендую перед использованием данного класса
просмотреть в файле dbtree.php все комментарии перед методами класса, так как
вышеприведенными методами этот класс не ограничивается.
На этом и заканчиваем наш урок. До встречи на следующем!
Download