<form action="foo.php3" method="post">
Name: <input type="text" name="name">
<input type="submit">
</form>
Значит после того, как пользователь нажмет в форме кнопку submit в php-скрипт foo.php3 методом post передадутся данные из формы, а обратиться, например к текстовому полю, из этого скрипта можно будет через переменную $name. Кроме того, можно использовать и массивы в качестве названий полей.
<form action="array.php" method="post">
Name: <input type="text" name="personal[name]">
Email: <input type="text" name="personal[email]">
Beer:
<select multiple name="beer[]">
<option value="warthog">Warthog
<option value="guinness">Guinness
</select>
<input type="submit">
</form>
В результате этот скрипт array.php может вывести полученные результаты так:
echo "Имя: ".$personal["name"];
echo "Email: ".$personal["email"];
for ($i=0;$i<sizeof($beer);$i++)
echo $beer[$i]."<br>";
Кроме этого, как известно, в форме в качестве submit-кнопки можно применять картику <input type=image src="image.gif" name="sub">. В таком случае скрипту передаются еще две дополнительные переменные, содержащие координаты точки, на которой кликнули мышкой. Это sub_x и sub_y.
HTTP Cookies
В PHP реалиован также простой способ работы с куками. С помощью функции SetCookie() можно их устанавливать, получать значения, как и в случае с формами - по имени переменной. В случае, если вы ожидаете получить несколько значений от куки имеет резон использовать в качестве имени переменной - массив. Как в следующем примере:
SetCookie ("MyCookie[]", "Testing", time()+3600);
Существует только одно небольшое ограничение. Так как куки - это часть HTTP заголовка их следует посылать самыми первыми. То есть грубо говоря, ваш скрипт должен начинаться с установки куки, а потом уже заниматься выводом html кода. Еще один пример, уже полноценного счетчика:
$Count++;
SetCookie ("Count", $Count, time()+3600);
SetCookie ("Cart[$Count]", $item, time()+3600);
Переменные окружения
И тут все так же просто. Хотите узнать домашний каталог? В юникс-системах это обычно переменная окружения с именем HOME. Так узнавайте:
echo $HOME;
Вы также можете работать с переменными окружения через функции getenv() и putenv()
КОНСТАНТЫ
Вы можете использовать не только переменные, но и константы с помощью функции define(). Взгляните на следующий пример:
define("CONSTANT", "Hello world.");
echo CONSTANT;
обратите внимание на то, что перед именем константы не пишется символ переменной $ и это правильно.
Список предопределенных констант:
- __FILE__ - Имя файла выполняемого скрипта.
- __LINE__ - Количество линий, интерптетированных на данный момент в этом скрипте.
- PHP_VERSION - Тут хранится версия PHP. Например: \'3.0.8-dev\'.
- PHP_OS - Имя операционной системы, на которой выполняется PHP-скрипт.
- TRUE - Истина.
- FALSE - Ложь.
- E_ERROR - Описывает случившуюся ошибку, после которой продолжение работы невозможно.
- E_WARNING - Описывает ошибку, после которой продолжается выполнение скрипта.
- E_PARSE - Описывает синтаксическую ошибку, при разборе интерпретатором текста скрипта.
- E_NOTICE - Просто какое-то сообщение от интерпретатора. Возможно ошибка, а возможно и нет.
Вот и все о константах и переменных. Со следующего шага займемся операторами и выражениями.
Программируемый калькулятор.
Сегодня рассмотрим все возможные операции на всеми возможными типами переменных и их значениями.
АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
Арифметических операций в PHP всего пять, вот они:
$a + $b
$a - $b
$a * $b
$a / $b
$a % $b
Я думаю здесь все ясно. Ну, может только поясню последнюю операцию, результатом ее выполнения будет остаток от деления $a на $b.
СТРОКОВЫЕ ОПЕРАЦИИ
Ха! А здесь и того меньше! Чисто строковой операцией считается операция сложения двух строк. Причем выглядит она довольно необычно, но практично:
$c = $a . $b;
То есть символом этой операции является точка. А результатом ее выполнения будет обыкновенная строка, состоящая из $a и $b.
ОПЕРАЦИЯ ПРИСВАИВАНИЯ
Ну, тут вообще ничего сложного, она всегда одна, это знак \'=\' и естественно этот знак означает, что переменной слева от него будет присвоено значение, полученное в результате выполнения каких либо операций или переменной/константы с правой стороны. Причем тут возможны некоторые С++-ные варианты, как-то:
$a = ($b = 4) + 5; // $a будет равна 9, а $b 4.
$a += 5; // аналогично $a = $a + 5;
$b = "Привет ";
$b .= "всем!"; // аналогично $b="Привет всем!"
ДВОИЧНЫЕ ОПЕРАЦИИ
Аа... и тут вообщем-то ничего нового:
- $a & $b - Побитовое И (AND)
- $a | $b - Побитовое ИЛИ (OR)
- ~ $a - Исключающее ИЛИ (XOR)
- $a << $b - Сдвиг влево на $b битов
- $a >> $b - Сдвиг вправо на $b битов
ЛОГИЧЕСКИЕ ОПЕРАЦИИ
Все тоже...
- $a and $b - И (AND)
- $a && $b - Тоже самое, что и предыдущее
- $a or $b - Или (OR)
- $a || $b - Тоже, что и предыдущее
- $a xor $b - Исключающее ИЛИ(XOR)
- ! $a - Инверсия (NOT)
ОПЕРАЦИИ СРАВНЕНИЯ
Здесь будет что-нибудь новое или нет? Даже комментировать эти операции не буду...
- $a == $b
- $a != $b
- $a < $b
- $a > $b
- $a <= $b
- $a >= $b
В PHP существует также, как и в C++, тринарный оператор \'?:\'. Кто незнает - объясню на примере:
$res = (expr1) ? (expr2) : (expr3);
Результатом этой операции будет значение (expr2), в том случае, если expr1==1 и значение expr3 в противном случае. Где все эти expr1, expr2, expr3 - являются переменными/константами или математиескими выражениями. Такая запись по сути своей аналогична следующей:
if (expr1) $res=(expr2); else $res=(expr3);
но естественно более короткая.
А собственно об этом и все. Мы рассмотрели все операции, присутствующие в PHP. Следующим шагом рассмотрим управляющие структуры (или их еще можно назвать командами).
Поуправляем?
Мне кажется не стоит попусту лить воду и описывать тут и так всем известные из других языков программирования структуры управления. Вместо этого я приведу их полный список и все методы их использования.
Оператор IF
Структура:
if (выражение)
блок_выполнения
Пример использования:
if ($a > $b)
print "a больше, чем b";
if ($a > $b){
print "a больше, чем b";
$b = $a;
}
Оператор ELSE
Пример использования:
if ($a > $b) {
print "a больше, чем b";
} else {
print "a не больше, чем b";
}
Оператор ELSEIF
Интересный оператор. Он применяется в случае, когда вам необходимо использовать IF сразу после ELSE. Пример использования:
if ($a > $b) {
print "a is bigger than b";
} elseif ($a == $b) {
print "a is equal to b";
} else {
print "a is smaller than b";
}
Оператор if(): ... endif;
Тоже довольно необычная структура. Смысл ее в том, что если условие, записанное в круглых скобах оператора IF оказалось истинным, то будет выполняться весь код, начиная от двоеточия \':\' до команды ENDIF;
Пример использования:
<?php if ($a==5): ?>
A = 5
<?php endif; ?>
Или вот еще с использованием вышеописанного оператора ELSEIF
if ($a == 5):
print "a equals 5";
print "...";
elseif ($a == 6):
print "a equals 6";
print "!!!";
else:
print "a is neither 5 nor 6";
endif;
Оператор WHILE
Структура:
while (выражение): блок_выполнения ... endwhile;
И примерчик:
$i = 1;
while ($i <= 10) {
print $i++;
}
Оператор DO...WHILE
Вообщем-то ничего необычного - цикл, как цикл. Выполняется блок выполнения до тех пор, пока справедливо выражение. Структура:
do блок_выполнения while (выражение);
Оператор FOR
Точно такой-же цикл как и в C++. Структура:
for (выражение1; выражение2; выражение3)
блок_выполнения
Хотя - нет. Есть в PHP еще одно дополнение. Структура:
for (выражение1; выражение2; выражение3):
блок_выполнения; ...; endfor;
Обратите внимание, на двоеточие перед блоком выполнения.
Оператор break
Старо как мир. Этот оператор позволяет выскакивать из любого цикла (for, while, do...while) до окончания его выполнения. Пример:
$i = 0;
while ($i < 10) {
if ($arr[$i] == "stop") {
break;
}
$i++;
}
<
Оператор continue
Тоже ничего нового. Этот оператор позволяет пропустить дальнейшее действия блока_выполнения любого цикла и продолжить выполнение с нового круга. Пример:
while (выражение) {
if (выражение2)
continue;
действие;
};
Такая програма аналогична следующей, без использования continue:
while (выражение) {
if (!выражение2)
действие;
};
Оператор switch
Часто очень необходимый оператор выбора. Хотя все его и так знают, попробую представить ясный пример. Допустим у вас есть следующий участок в программе:
if ($i == 0) {
print "i равно 0";
} else
if ($i == 1) {
print "i равно 1";
} else
if ($i == 2) {
print "i равно 2";
} else
print "ни одно условие не прошло"
Сэкономить время выполнения данного участка, а также представить его более логичным способом поможет нам этот оператор. Следующий пример совершает действия, аналогичные предыдущему, но в более красивой форме:
switch ($i) {
case 0:
print "i равно 0";
break;
case 1:
print "i равно 1";
break;
case 2:
print "i равно 2";
break;
default:
print " ниодно условие не прошло"
}
В таком представлении есть и еще одно преимущество. Если вы не поставите оператор break, например, перед case 1:, то в случае, когда $I будет равен нулю после вывода на экран сообщения об этом программа пойдет дальше и выведет также сообщение о том, что $I равна еще и 1
и только после, встретив break;
продолжит свое выполнение за пределами switch
Оператор require
Этот оператор действует примерно так-же как и #include в C++. Файл, указанный в кавычках включается в скрипт и выполняется, но только однажды. В файле, включаемом оператором require резонно хранить какие-то данные, необходимые для многих скриптов и соответственно влкючать его в эти "многие" скрипты. Структура его такая:
require \'header.inc\';
Оператор include
Структура:
include \'func.inc\';
Этот оператор позволяет включать код, содержащийся в указанном файле (func.inc в нашем случае) и выполнять его столько раз, сколько программа встречает этот оператор. То есть. Например, в файле func.inc у нас хранится программа вывода на экран определенных параметров. Ну и каждый раз, когда нам нужно будет выводить эти параметры мы будем вставлять в текст нашей основной программы include \'func.inc\', то есть это в принципе тоже самое, как если-бы мы везде в таких случаях вставляли бы текст, содержащийся в файле func.inc
Обратите внимание. Разница между include и require довольно существенная, поэтому поэксперементируйте с ними сами, если вы ее так и не усмотрели.
Ну вот и все. Надеюсь это пригодилось.
И снова PHP - продолжим, или галопом по Европам!
Итак, описание основных конструкций языка (за исключением классов - о них позже) сделано! Теперь следует попрактиковаться на реальном проекте. Но прежде все же надлежит уделить внимание некоторой рутине - хотя бы бегло рассмотреть основной набор функций языка. Большая часть этих функций находится в подключаемых модулях, которые собираются во время линковки интерпретатора (под win32 наборы функций выполнены как dll-и). Исключения составляют некоторые особо популярные группы функций, встроенные в ядро интерпретатора (встраиваемость зависит от версии PHP). Например, в PHP версии 4 функции доступа к MySQL встроены в интерпретатор.
Под рассмотрение (причем весьма беглое) подпадет некоторое подмножество функций. Я исключу из рассмотрения функции, специфичные для SQL-серверов (кроме, MySQL - именно его мы будем использовать в наших проектах, а работа с остальными SQL-серверами выполняется аналогичным образом), и некоторые другие из числа редкоиспользуемых (мною не используемых? ;).
Кроме того, я еще раз подчеркну - обзор будет весьма беглым. Подробнее разбирательством с возможностями PHP мы займемся по ходу работы над проектами.
Однако, приступим.
- Функции, специфичные для Apache-сервера я пока опускаю, т.к. под win32 они не идут (если я правильно понял readme.txt из дистрибутива PHP, то и не должны идти - под win32 PHP живет в CGI-выполняемом режиме, а не как модуль apache). Под *nix-ом я пока проверить не имею возможности. Подождем до лучших времен.
- Функции работы с массивами. Эта группа одна из самых многочисленных (если не самая многочисленная). Причем под PHP версии 4 она раза в два больше, чем под 3-й версией. Базовыми для обеих версий можно считать функцию конструирования массива array(), функции сортировки (штук 9), функции навигации по массиву (перемещение внутреннего указателя - подробности в следующих шагах), информационные функции (количество элементов массива и некоторые другие), выборка данных. Как я уже сказал, в 4-й версии добавлена большая группа функций array_xxxxx(), где xxxxx - предназначение. Например, array_push() добавляет элемент в конец массива. Эта группа отвечает за сравнения массивов, множественные операции (объединение, пересечение, разность и пр.), добавление/удаление элемент и некоторые другие операции.
- 4 функции работы со словарем.
- Математические функции над числами произвольной точности.
- Функции работы с календарями.
- Поддержка COM-объектов на win32-платформе.
- В 4-й версии добавлены функции получения информации о классах в PHP
В следующем шаге мы продолжим беглый обзор функций PHP. А сейчас в качестве постскриптума
хотелось бы сказать вот о чем:
В фидошной конференции по PHP на днях пробегала такая ссылка: http://it.sinor.ru/?isnum=4. Автор в этой статье описывает метод установки PHP как модуль Apache под win32. К сожалению мой апач обиделся до глубины души от предложения загрузить в качестве модуля php4apache.dll. А жаль. Прийдется ставить линукс. В прочем, может, у меня просто руки ни к тому месту прикручены? ;)
Далее. Опять же на днях (вот везет!) попался мне один чудный сайт "Опыты" (http://exper.ural.ru). С огромным удовольствием прочитав его содержимое, я наткнулся на статью "Использование транслятора XML в PHP.", которая сильно повлияла на мои планы по дальнейшим шагам. Дело в том, что, используя функции
XML-парсинга можно разрабатывать сайт на XML, а на выходе получить обычный HTML-файл. Что это дает? Самодокументируемый код, возможность генерить браузерозависимый код на сервере (а не тащить кучу кода на клиентскую машину и делать JavaScript-овые проверки версии браузера - на этом сайте, кстати, много материала посвящено различиям в интерпретации HTML- и JavaScript-кода браузерами IE и NN) и другие приятности. Мы в дальнейшем еще вернемся к теме XML.
Еще одна новость - издательство "Символ" решило учинить и на нашей улице праздник! (Не ищите на их сайте информацию об этих новинках - ее там нет! Я ничего не понимаю в их маркетинге.) На днях (опять же!) к нам пришла по почте книга, к которой прилагался рекламный буклет с планами издательства "Символ". Вот что я там узрел из их планов на конец этого - начало будущего года. Во-первых, Хариш Рават и др. Профессиональное программирование на PHP (!). 1100 страниц (!!). Я плакал ! ;)
От O\'Reilly: Стивен Спейнауэр. Справочник вебмастера. 2-е издание. Брэт МакЛафлин и др. Java и XML.
Ну наконец-то! Вот ОНА: Ларри УОЛЛ. Программирование на Perl. 3-е издание. 1200 страниц (!!!)
Да-да. Та самая кэмэлбука! Далее. Аллигатор (классное имя ;) Декарт (и фамилия ;) и др. Программирование на Perl DBI. Дэвид Бланк-Эдельман и др. Perl для системного администрирования. Скотт Гуэлич. CGI-программирование на Perl.
Фух. Рука бойцов колоть устала.
Заканчиваем обзор.
Однако, продолжим рассмотрение (вернее, беглый обзор - что называется, голопом по европам) функций PHP.
- Функции доступа к dbm-базам.
- Функции даты и времени: получить, проверить правильность, вывести в формате.
- Dbase-функции. Ну тут стандартный dbase-, clipper-, и_иже_с_ними-набор: открыть/закрыть, читать/писать/заменять/добавлять/удалять записи, получить количества колонок и записей в таблице.
- Функции для работы с директориями: открыть/закрыть, читать, сменить каталог.
- Так называемые dl-функции (вернее, одна единственная функция) - динамическая загрузка PHP-библиотеки в рунтайме. Статическая загрузка предполагает наличие в ini-файле PHP указания на загружаемые модули (разделы "extension" - сами модули, - и "extension_dir" - где эти модули искать).
- Работа с DOM-объектами для XML-документов (для PHP 4-ой версии).
- Один из самых многочисленных разделов - группа функций работы с файловой системой. С этой группой мы еще не раз столкнемся. Я думаю, этой группе следует посвятить отдельный шаг.
- FTP-функции поддерживают весь стандартный набор команд ftp-протокола.
- Чрезвычайно ценные HTTP-функции (их всего 3) позволяют управлять HTTP-заголовком. Вот пример, в котором без функции header() вообще нельзя обойтись. Вы формируете картинку PHP-кодом (до image-функций мы еще доберемся). Если не сказать перед передачей картинки браузеру header("Content-type: image/gif"), то в заголовке HTTP-запроса будет утверждаться, что пришедший от сервера файл имеет тип text/html.
- Image-функции позволяют создавать PNG- и Jpeg-файлы "налету" (поддержка gif-формата с 4-ой версии PHP прекращена в связи с проблемой лицензирования; впрочем, существуют сборки php_gd.dll с поддержкой gif-ов), рисовать графические примитивы, выводить в картинке текст и читать существующие картинки.
- IMAP-функции позволяют работать с imap-ящиками (чтение, проверка, посылка письма, работа с заголовками писем, с почтовыми ящиками, ping-и, mail-адреса, всякие там en-/decode, base64 и прочее).
- Математические функции (а как же без них!)
- Функции шифрования и хеш-функции (список поддерживаемых алгоритмов занимает 2 строки).
- Что называется, минеслаус-функции: проверка коннекта, define-функции, eval (выполнение строки как скрипта), работа с аргументами функций (4-я версия), сериализация, sleep и пр.
- MySQL-функции. Им будут посвящены несколько шагов.
- Сетевые функции: NDS, ip-адреса, связь номера и имени сетевого протокола и пр.
- ODBC-функции
- Очень полезная группа функций, возвращающих информацию о состоянии и настройках PHP-интерпретатора, записывающих сообщения в log-файлы, читающих окружения интерпретатора. Самая эффектная - phpinfo() ;). Откройте в браузере файл http://localhost/info.php3, содержащий такой текст:
<html><body><?php phpinfo(); ?> </body></html>
Увидите чудо!
- Четыре функции для запуска внешних программ. Вернее три, четвертая (escapeshellcmd) предназначена для экранирования символов "<", ">", "|" и некоторых других. Дело в том, что вызов внешней программы из скрипта - потенциально опасное действие. Не проверив содержимое строки, передаваемой в качестве параметра таким функциям, как eval(), можно создать потенциальную брешь для злоумышленников. Посмотрите, что выведет на экран такая конструкция: echo escapeshellcmd("echo y| format c:").
- Само собой, что в PHP есть функции работы с регулярными выражениями.
- В 4-ой версии появилась возможность работать с сессиями (в 3-ей для этих целей нужно писать эмулирующий сессии код). Сессиями мы займемся, когда будем разрабатывать систему регистраций в наших будущих проектах.
- Огромный список функций работы со строками (включая функцию для конвертации кириллицы в разные кодировки! - наверное, изобилием кодировок мы уже весь мир достали ;).
- Семь функций работы с URL: base64, разбор URL-строки на части (хост, порт, путь и т.д.), а также кодировка строки с заменой неанглийских символов и пробелов 16-ричными цифрами с лидирующим "%" и обратно (очень помогает при работе с файлами, имеющими русские буквы в названии).
- Функции проверки PHP-переменных на инициализированность (ух какое словечко получилось! ;), принадлежность определенному типу (число, строка и пр.) и т.п.
- XML-функции (парсинг XML-тегов). В прошлом шаге я давал ссылку на сайт, где эта тема очень хорошо освещена.
Первый проект - начало. MySQL.
Очень надеюсь, что обзор возможностей был не слишком утомителен.
Я думаю, сейчас самое время перейти от слов к делу - сделать первый небольшой скрипт. Возьмем для примера следующую задачу. Нужно сделать базу с книгами, которые появились или скоро появятся в продаже, и которые вызывают интерес.
Для этого нам понадобятся: база (таблицы сделаем в MySQL), форма ввода, процедура загрузки из текстового файла (на тот случай, если ранее список книг лежал в файле - у меня дело именно так и обстоит), страница для вывода с сортировкой по критериям и, как дополнительный прибамбас (его сделаем в самую последнюю очередь), загрузка новостей с сайтов издательств с занесением выбранных позиций в базу.
Цель проекта проста. Не упустить интересующее чтиво.
Структура таблиц будет такой. Таблица описания книг BOOKS:
- id int(5) - уникальный id autoincrement primary key - сначала хотел сделать автора и название ключем, решил, что это будет неудобно, если решим добавлять ссылочные таблицы)
- author char(60) - автор
- namebook text(100) - название книги
- series int(2) - id серии (если книга принадлежит серии) - для серий отдельная таблица
- edition int(2) - издание
- year int(4) - год издания
- isbn char(20) - ISBN (интересно, 20 символов хватит?)
- pages int(4) - объем книги
- when_create int(2) - номер квартала (за точку отсчета возьмем I квартал 2000)
- how int(4) - предположительная цена
- status int(1) - id статуса: обязательно заказать, посмотреть подробнее и т.п.
Для начала хватит. Потом добавим серии книг, статусы, таблицу оглавлений и обложек (в blob-ах), комментарии к книгам и список интернет-магазинов, в которых книга была обнаружена.
Для создания таблицы нам понадобится создать базу данных в MySQL (назовем ее тоже BOOKS) и в ней уже саму таблицу. Но сначала об установке MySQL. Я опишу установку под WinNT (под *nix-ы его надо собирать - я пока это проделывать не пробовал).
Процедура эта довольно проста. Надо забрать дистрибутив с http://www.mysql.com, раскрыть zip, запустить setup.exe. После окончания установки в каталоге MySQL-я будет лежать файл my-example.cnf. Предполагается, что его надо будет скопировать в каталог c:\\ под именем my.cnf. Я этого не делал. Вместо этого, следуя Readme-файлу, я запустил winmysqladmin.exe, выбрал закладку "my.ini Setup", указал директорию, в которой живут поддиректории MySQL-я (у меня это C:/usr/local/mysql), выбрал радиобаттон в секции "mysqld file" mysqld-nt (запуск MySQL-сервера как службы в NT) и нажал на "Save Modification". После чего в WINNT-каталоге обнаружил файл my.ini. Все. Осталось убедиться, что в списке служб появилась служба "mysql", и выставить ей атрибут запуска (руками/автоматом).
Итак, заходим в каталог (если вы, конечно, не внесли эту директорию в path-окружение)
c:\\mysql\\bin (или туда, куды вы установили MySQL; далее будем полагать, что mysql установлен в c:\\mysql) и запускаем mysqladmin с параметром (краткую справку по параметрам mysqladmin-а я приведу в следующем шаге).
create <имя база>.
В нашем случае
create books.
mysqladmin никакой мессагой нас не порадовал,
но, зайдя в c:\\mysql\\data (там по умолчанию mysql хранит базы данных, если не
задано противное в конфигурации сервера), можно с чувством глубокого удовлетворения обнаружить
директорию books. Это и есть наша база. Правда, директория пуста - мы ведь не создали в базе еще
ни одной таблицы. (Кстати, если вы решите удалить базу пудем удаления директории -
лучше откажитесь от этой затеи. Mysql хранит у себя информацию о существующих базах.
Для корректного удаления базы есть параметр у mysqladmin-а:
drop <имя базы>.
Создаем таблицу. Можно зайти в mysql.exe и создать там, а можно скормить тектовый файл с командами для mysql-я на вход exe-шнику. (При заходе с локальной машины mysql считает, что пришел root со всеми полномочиями - о пользователях и полномочиях в mysql-е поговорим позднее).
Итак, либо, зайдя в mysql, введите нижеследующую последовательность команд,
либо сохраните их в файле (например, books.sql) и скажите
mysql < books.sql
А вот содержимое books.sql:
connect books;
create table books (
id int(5) not null primary key auto_increment,
author char(60),
namebook varchar(100),
series int(2),
edition int(2),
year int(4),
isbn char(20),
pages int(4),
when_create int(2),
how int(4),
status int(1)
);
Зайдя теперь в каталог c:\\mysql\\data\\books, вы обнаружите целых три файла: books.frm (структура таблицы), books.myd (данные; сейчас он нулевой длины - insert-ов ведь не было!) и books.myi (данные о ключах).
Напоследок небольшое пояснение по первой строке (полю) в insert-е. По идее, параметр "not null" не нужен, раз "primary key" говорим. Но есть мнение, что лучше все же его указать (я не готов это утверждение как-либо комментировать). Auto_increment, соответственно, присваивает одноименный атрибут полю, позволяя при insert-е автоматически генерить цифру на единицу большую, чем в предыдущем insert-е.
Заглянем в базу. Первый скрипт.
Два замечания в начале:
1) В предыдущем шаге я допустил досадную оплошность. Для установки MySQL как службы под Windows NT кроме создания my.ini файла winmysqladmin-ом надо, конечно же, выполнить саму установку сервера как службы вот так:
C:\\mysql\\bin\\mysqld-nt --install
Приношу свои извинения за допущенную ошибку.
2) После опубликования предыдущих трех шагов мне пришло письмо от Michael Vladimirov (спасибо, Michael !) - сайт "How IT works" http://it.sinor.ru/, - по поводу проблемы с установкой PHP как модуля Apache. Michael пишет, что PHP не становится как модуль при включенной библиотеке GDextension=php_gd.dll" в php.ini веб-сервер Apache действительно запустился. К этой теме я еще вернусь, когда выясню некоторые пока для меня неясные моменты.
Итак, база (и первая таблица в ней) созданы.
Напишем скрипт, который будет формировать html-страницу с содержимым таблицы. Но сначала таблицу надо заполнить содержимым. Так как форма ввода у нас еще не создана, то воспользуемся методом из предыдущего шага - создадим sql-запрос на добавление записи в таблицу и направим его (файл с запросом) на вход mysql.exe.
Вот этот скрипт:
connect books;
insert into books (
id, author, namebook, series, edition, year, isbn,
pages, when_create, how, status)
values (null, "Р. Яргер и др.",
"MySQL и mSQL. Базы данных для небольших предприятий и Интернета",
0, 0, 2000, "5-93286-010-3", 557, 3, 170, 0),
(null, "Ларри УОЛЛ", "Программирование на Perl",
0, 3, 2001, null, 1200, 5, null, 0);
Несколько замечаний по этому запросу. Во-первых, я не сказал "commit". В данном случае это не обязательно, т.к. commit произойдет по окончанию скрипта автоматически. Во-вторых, в качестве значения для ключевого поля id выбрано значение null, что заставляет mysql присвоить (в силу параметра для поля auto_increment) числовое значение, на единицу большее, чем максимальное из существующих значений для этого поля в таблице. В-третьих, некоторые поля мы "занулили" - потом заполним. И в-четвертых, этот запрос к sql-серверу отличается от стандарта! Дело в том, что (как я уже говорил) mysql имеет некоторые отличия от sql-стандарта. В частности, insert допускает такую сокращенную форму записи. По правилам я бы должен был бы вместо одного insert-а с перечисленными через запятую группами значений в values использовать два запроса. Но мы облегчили себе жизнь. Вообще-то это, на мой взгляд, не лучшая практика - использовать всякие "отклонения от нормы". По крайней мере страдает переносимость. Но нас сейчас эта тема вряд ли должна волновать.
Теперь напишем скрипт для нашей первой страницы. HTML-код пока использован по минимуму. Кстати, чуть не забыл. Если вы используете (готовитесь использовать ;) PHP3, то в php3.ini в виндючем каталоге раскомментируйте строчку
extension=php3_mysql.dll
Для PHP4 (его ini-файл кличут php.ini) ничего делать не надо, т.к. поддержка mysql в 4-й версии встроенная.
И еще замечание: PHP3 и PHP4 можно использовать на одном компьютере вместе. У меня в apache файлы с расширением php3 обрабатывает 3-я версия, а php4 и phtml - 4-я.
Вот фрагмент из httpd.conf:
<Directory "C:/usr/local/php4">
Options ExecCGI
</Directory>
ScriptAlias "/__php4_dir__/" "C:/usr/local/php4/"
Action application/x-httpd-php4 "/__php4_dir__/php.exe"
<Directory "C:/usr/local/php3">
Options ExecCGI
</Directory>
ScriptAlias "/__php3_dir__/" "C:/usr/local/php3/"
Action application/x-httpd-php3 "/__php3_dir__/php.exe"
А вот фрагмент файла mime.types:
application/x-httpd-php3 phtml php3
application/x-httpd-php4 php4
А теперь обещанный скрипт:
<html><body>
<table border=1>
<?php
$db_name="books"; //база данных
$table="books"; //таблица
$host="localhost"; //хост
$user=""; //логин
$pass=""; //password
//законнектимся - получаем link-идентификатор или вывод номера и текста ошибки
//с последующим прерыванием работы скрипта (die())
$link=mysql_connect($host,$user,$pass) or die(mysql_errno($link).mysql_error($link));
//выбираем базу данных BOOKS, созданную нами ранее
$db=mysql_select_db($db_name,$link) or die(mysql_errno($link).mysql_error($link));
//выберем данные
$result=mysql_query("SELECT * FROM ".$table, $link) or die(mysql_errno($link).mysql_error($link));
//сформируем заголовок таблицы результатов выборки
$th=explode("#","ID#Автор#Название книги#Серия#Ред.#Год#ISBN#Стр.#Когда#Цена#Статус");
echo "<tr><th>",implode("</th><th>",$th),"</th></tr>";
//выведем результаты в HTML-документ
while($data=mysql_fetch_row($result)) {
echo "<tr><td>", implode("</td><td>",$data), "</td></tr>";
}
//освободить выделенную под результат выборки память
mysql_free_result($result);
mysql_close($link);
?>
</table>
</body></html>
Некоторые комментарии (более подробно в следующих шагах).
Во-первых, даже в такой упрощенной форме код работы с mysql избыточен. Т.к. мы работает с одной базой, да еще и делаем единственный запрос, то вместо mysql_connect(), mysql_select_db() и mysql_query() достаточно было вызвать mysql_db_query(). Эта функция при необходимости выполнит коннекцию (в прочем, в этом она не одинока), выберет базу данных и выполнит запрос к базе.
Во-вторых,
mysql_close() тоже необязательна, т.к. соединение с сервером будет автоматически закрыто при окончании скрипта.
В-третьих, и уж конечно же в данном случае во mysql_free_result() никакой необходимости. Освобождать память при единственном запросе да еще такого незначительного объема... Здесь эта функция вызвана скорее для демонстрации возможных операций с mysql на php, ну или, если хотите, для порядка. ;)
В-четвертых, функции implode() и explode() относятся к стринговым. Этой теме будет посвящен отдельный шаг (шаги). А пока: explode() создает массив из фрагментов строки, разбирая ее как строку с разделителем, указанном в первом параметре. Implode() делает обратную операцию. Эту пару я применил для удобства - по-моему это проще, чем набирать строку в виде "...текст</th><th>текст..." и уж тем более проще, чем делать кучу echo с текстами, обрамленными тегами <th>/</th>.
И в-пятых, сие чудо программистской мысли ;) выполняется как под 4-й версией php4, так и под 3-ей.
Краткий справочник по MySQL. Типы данных.
Прежде чем продолжать разрабатывать наше творение я приведу краткую справку по MySQL, а точнее, коротко опишу типы данных mysql, функции и особенности sql-запросов. Сам sql, я думаю, Вы знаете (если это не так - то к вашим услугам соответствующий раздел про SQL на "Первых шагах"! ;)
Я не буду описывать подробно все изобилие типов MySQL. Я уверен, что это лишнее. Дело в том, что в mysql-е числовые и текстовые группы типов очень многочисленны. Отличаются типы в этих группах размером в байтах и возможными модификаторами. Подробности можно посмотреть в мануале в директории Docs каталога с mysql-ем в разделе "7.3 Column types". Там и английский знать особо не нужно - смотрите на название и цифры длины! ;)
Здесь я сделаю обзор самих групп типов данных - для быстрого введение.
MySQL поддерживает следующие типы/группы типов данных:
- Числовые. Возможные модификаторы: UNSIGNED для объявления беззнаковости, ZEROFILL для заполнения лидирующих пробелов нулями (имеется в виду внешний вид при выводе). Могут быть созданы как целые (TINYINT, SMALLINT, INT, BIGINT и пр.), так и числа с плавающей точкой (FLOAT, DOUBLE, REAL и пр.).
- Строковые. Возможные модификаторы: BINARY для объявления поля как бинарного (любые коды хранимых символов), NATIONAL - модификатор по умолчанию, - использование набора символов для сортировки, сравнения и пр. Занятный модификатор. Отвечает за конструкцию SET-группы: SET CHARACTER SET character_set_name | DEFAULT, где character_set_name может принимать значение cp1251_koi8. Однако эти установки выставлены по умолчанию. Выходит, без модификатора результат тот же, что и с ним. Я так понял, что эти фишки для будущего использования. Модификатор для типа CHAR VARYING создает строковое поле переменной длины.
- BLOB-поля - поля для хранения двоичных данных.
Типы данных, не попавшие в предыдущие три группы:
- TIMESTAMP - поле хранит дату и время последнего изменения записи. Это значит, что, добавив в таблицу поле типа TIMESTAMP (например, воспользовавшись конструкцией ALTER TABLE table_name ADD COLUMN column_name TIMESTAMP), Вы, не производя никаких изменений поля типа TIMESTAMP, будете в нем иметь время последней операции с записью, влияющей на содержимое строки таблицы.
- DATE, TIME, DATETIME - поля хранения даты, времени, и того, и другого. Тут, я думаю все ясно.
- YEAR - поле, добавленное в версии 3.22, - для хранения года в интервале с 1901 по 2155.
- ENUM - поле, хранящее одно из значений, указанных в списке при создании (модификации структуры)
таблицы, например, ALTER TABLE tab_name ADD COLUMN col_enum ENUM(\'Ага\', \'Угу\', \'Ну его нафиг\').
Теперь поместить в поле col_enum одно из перечисленных значений можно так:
INSERT INTO tab_name SET col_enum=\'Ага\'
или
UPDATE tab_name SET col_enum=3.
В последнем случае в col_enum будет значение \'Ну его нафиг\'. Присвоение полю значения не из списка (например, col_enum=7 или col_enum=\'format c:\') запишет в поле пустую строку (даже не \'NULL\').
- тип SET в отличии от типа ENUM предназначен для хранения списка значений, например,
ALTER TABLE tab_name ADD COLUMN col_set SET (\'один\',\'два\',\'три\',\'четыре\')
Теперь изменим значение поля:
UPDATE tab_name SET col_set=\'один\' WHERE поле=значение
После такого запроса col_set будет содержать значение \'один\'.
UPDATE tab_name SET col_set=15
Здесь col_set содержит значение \'один,два,три,четыре\'. Да, именно строку с set-значениями, разделенными запятыми. Цифра 15 - это двоичное 1111. Каждый бит отвечает за свое значение в списке. Например: 1 - \'del\', 10 - \'file1.txt\', 11 - \'del,file1.txt\' и т.д. Как и в случае с ENUM установка недопустимого значения записывает в поле пустую строку.
Функции работы с MySQL.
В предыдущем шаге я обещал дать краткий справочник по функциям MySQL.
Однако сейчас я пришел к выводу, что лучше все же поступить немного по-другому. По SQL на "Первых шагах" есть специальный раздел. Так что я сосредоточусь на функциях PHP.
В "Подробностях" приведен список некоторых операторов и функций MySQL в основном из числа отличных от SQL-стандарта.
Вернемся к скрипту 10-го шага и рассмотрим его подробнее.
Во-первых, в скрипте постоянно используется функция DIE() с параметром
"mysql_errno($link).mysql_error($link)". Функция DIE(текст) выводит текст в выходной поток (в HTML-код) и прерывает дальнейшее выполнение скрипта. В данном случае текст для DIE() формируется двумя функциями из группы mysql-функций: MYSQL_ERRNO() - код ошибки и MYSQL_ERROR() - текст ошибки.
Оператор PHP "точка" конкатенирует строки. Необязательный параметр (здесь переменная $link) - идентификатор, возвращаемый функцией MYSQL_CONNECT() после соединения с сервером. (Об этом чуть ниже). Текст же ошибки, возвращаемый функцией MYSQL_ERROR(), выбирается из файлов, расположенных в подкаталоге "share" директории, в которой установлен MySQL.
Управлять выбором языка сообщений можно с помощью параметра language в my.ini, расположенном (напомню) в каталоге winnt (или в том, где у Вас живет виндюк). Вот строка из моего ini-файла:
language=d:/usr/local/mysql/share/russian
В этих файлах все сообщения на русском языке в кодировке KOI-8, поэтому учитывайте это.
Разобравшись с DIE() и функциями-формирователями строки для нее, пройдем весь скрипт 10-го шага по порядку. Секция PHP в файле открыта, как всегда, тегом "<?php". Далее объявляются переменные для работы с функциями MySQL (забегая вперед, замечу, что лучше такие переменные выносить в отдельные файлы и включать в скрипты, скажем, функцией REQUIRE() или INCLUDE()).
Перед началом работы с MySQL выполняем соединение с сервером:
$link=MYSQL_CONNECT($host,$user,$pass).
Здесь одно замечание. К $host может быть приписан через двоеточие номер tcp-порта, который слушает сервер в надежде, что кто-нибудь его позовет. По умолчанию порт 3306. Изменяется это значение опять же в my.ini. Кстати, напомню, что для актуализации изменений ini-файла надо перестартовать сервер MySQL (т.е. службу, если речь идет об NT).
Есть еще одна функция, предназначенная для установки соединения с сервером - MYSQL_PCONNECT(). Эта функция в отличии от предыдущей обеспечивает так называемый устойчивый коннект к базе, т.е. соединение, которое не разрывается после завершения выполнения скрипта. Речь идет о следующем. После окончания работы текущего скрипта все открытые соединения с базами закрываются автоматически (если Вы явно не вызвали функцию MYSQL_CLOSE($[link])). Соединения, полученные с помощью функции устойчивого коннекта, не только сохраняются (повторные вызовы MYSQL_PCONNECT() просто возвращают $link уже установленного соединения), но и прямой вызов MYSQL_CLOSE() соединения не разрывает. Здесь надо отметить следующее. Во-первых, по сообщениям в конференциях по PHP время, затрачиваемое на соединение с сервером MySQL, весьма незначительно (в отличие, скажем, от Oracle, у которого процесс коннекта достаточно ресурсоемок), чтобы к его экономии относиться столь серьезно. Я это утверждение не проверял, но склонен с ним согласиться. Наверное, так и есть, учитывая объем работы, проводимой сервером для установления соединения (обслуживание запросов с tcp-порта, проверка полномочий, выделение памяти под данные соединения и пр.). Во-вторых, я так и не понял, как же разрывать pconnect-соединение. Узнаю - опишу.
Следующая (и последняя из рассматриваемых в этом шаге) функция - MYSQL_SELECT_DB(). Она имеет два параметра: первый - имя базы, и второй (необязательный) - id соединения ($link). Под необязательностью идентификатора соединения как параметра понимается следующее. Если параметр не указан, то используется последнее открытое соединение. А если ни одно соединение не открыто, то неявно выполняется connect (т.е. при использовании одного соединения MYSQL_CONNECT() явно вызывать не обязательно).
Следующий шаг будет полностью посвящен выборке данных из базы. До следующего шага!
P.S. В фидошной конференции по php я увидел чудную ссылку: http://php.weblogs.com/adodb. Речь идет о классе-оболочке для работы с разными sql-серверами на PHP (некий аналог DBI в Perl). Правда, я сей класс еще не тестировал. Попробую - опишу. А так выглядит очень интересно - набор методов, одинаковый для множества sql-серверов. Только строку инициализации библиотеки меняй под конкретный sql-сервер - и вперед!
Функции работы с MySQL. Часть 2.
Продолжим рассмотрение скрипта 10-го шага. Все внимание функциям выборки данных.
В предыдущем шаге мы законнектились к mysql-серверу и выбрали базу. Теперь формируем запрос к базе. Для этой цели используется функция MYSQL_QUERY() или MYSQL_DB_QUERY(). Разница между ними, как Вы, наверное, уже догадываетесь, заключается в том, что первая из них предполагает, что база данных уже выбрана а вторая наряду с выполнением запроса осуществляет выбор базы.
Параметры: MYSQL_QUERY(строка запроса [, $link]), где $link - идентификатор коннекта, возвращаемого функцией MYSQL_CONNECT(); MYSQL_DB_QUERY(база данных, строка запроса [, $link])). Как обычно, если не указан $link, то используется последнее выполненное соединение. Обе функции выполняют соединение с mysql-сервером (MYSQL_CONNECT()), если ранее не выполнено такое соединение. При этом неявный вызов MYSQL_CONNECT() осуществляется без параметров, используя в качестве имени хоста "localhost", а в качестве имени пользователя и пароля - пустые строки. И обе функции возвращают переменную (идентификатор ресурса), которую потом нужно использовать при обработке запроса (см. ниже). Например,
mysql_select_db("books");
$result = mysql_query("select * from books");
или
$result = mysql_db_query("books","select * from books");
Теперь самое интересное - обработка запроса. Эта операция осуществляется с помощью fetch-группы функций PHP: MYSQL_FETCH_ARRAY(), MYSQL_FETCH_FIELD(), MYSQL_FETCH_LENGTHS(), MYSQL_FETCH_OBJECT() и MYSQL_FETCH_ROW(), а также некоторыми дополнительными функциями (функций управления fetch-курсором, информационными функциями и пр.). Эти функции возвращают очередную строку выборки в виде массива или объекта, перемещая внутренний указатель на следующую строку. Поэтому их следует использовать в циклах.
Сразу небольшое замечание. Среди mysql-функций в PHP есть MYSQL_RESULT(). Она возвращает значение одного столбца в указанной строке. В мануале рекомендуют вместо нее использовать более быстрые функции fetch-группы. Кроме того, по моему мнению, эта функция самая неудобная для работы с выбранными данными. Посему эту функцию я рассматривать не буду.
В 10-м шаге мы использовали функцию MYSQL_FETCH_ROW(). С нее и начнем. Итак, в результате выполнения одной из двух query-функций мы имеем указатель на ресурс выборки. Пусть сей указатель хранится в переменной $result. Тогда в таком цикле
while ($data=mysql_fetch_row($result)) {
echo implode(" | ",$data), "<br>";
}
можно вывести результаты запроса. Эта функция возвращает массив (здесь $data), в котором каждый элемент - поле строки выборки, возвращаемое select-ом в query-функции. Функция из string-группы IMPLODE() вернет строку, состоящую из элементов массива, указанного во втором параметре, разделенных разделителем - первым параметром.
Теперь попробуем повторить вывод результатов запроса дважды (пример, конечно, надуманный, но на практике иногда требуется пройтись по результатам запроса более одного раза):
echo "Первый раз";
while ($data=mysql_fetch_row($result)) {
echo implode(" | ",$data), "<br>";
}
echo "Второй раз";
while ($data=mysql_fetch_row($result)) {
echo implode(" | ",$data), "<br>";
}
После строки "Второй раз" мы ничего не увидим. Причина в том, что при каждом вызове fetch-функции внутренний указатель (курсор) автоматически переходит к следующей записи. Как только этот указатель доберется до конца выборки, fetch-функция вернет FALSE, и произойдет выход из цикла. Управлять указателем можно с помощью функции MYSQL_DATA_SEEK(). Параметры: id результата и номер строки, на которую надо установить указатель. Отсчет строк начинается с нуля. В последнем примере перед вторым циклом нужно было бы сказать
mysql_data_seek($result, 0);
Функции работы с MySQL. Часть 3.
Другой подход к обработке результатов запроса предлагает функция MYSQL_FETCH_ARRAY. Эта функция возвращает ассоциативный массив, сформированный из очередной обрабатываемой строки. Первый параметр, как обычно, указатель на ресурс результатов выборки, второй - флаг (константа), отвечающий за содержимое возвращаемого функцией ассоциативного массива. Рассмотрим пример. Надо вывести список книг из нашей таблицы books вместе с именами авторов.
//для формирования SELECT-а
$sel_fields=array(\'author\' => \'Автор\', \'namebook\' => \'Название книги\');
$link=mysql_connect($host,$user,$pass) or die(mysql_errno($link).mysql_error($link));
//выбрали БД
$db=mysql_select_db($db_name,$link) or die(mysql_errno($link).mysql_error($link));
//переменная $type принимает значение одной из следующих
//констант: MYSQL_ASSOC, MYSQL_NUM, and MYSQL_BOTH
$type=MYSQL_ASSOC;
reset($sel_fields); //внутренний указатель массива на первый элемент
//работает в версиях PHP3 и PHP4
while(list($k,$v)=each($sel_fields)) {//сформируем строку для SELECT-а
$sel_str[]=" $k as \'$v\'";
}
//закомментированная часть работает только в PHP4
//это альтернативное решение для цикла по массиву
//установка указателя на первый элемент для foreach-цикла не требуется
//foreach($sel_fields as $k=>$v) {
// $sel_str[]=" $k as \'$v\'";
//}
$sel_str=implode(\',\',$sel_str); //сделать строку из массива, используя разделитель
//на выходе имеем строку $sel_str = "author as \'Автор\', namebook as \'Название книги\'"
$result=mysql_query("SELECT ".$sel_str." FROM ".$table, $link) or die(mysql_errno($link).mysql_error($link));
//пройдемся по результату выборки
while($data=mysql_fetch_array($result,$type)) {
while(list($k,$v)=each($data)) { //версия для PHP 3,4
echo "$k => $v<br>";
}
//в комментариях альтернатива while-циклу (только для PHP4)
//foreach($data as $k=>$v) {
//echo "$k => $v<br>";
//}
}
В зависимости от значения второго параметра функции MYSQL_FETCH_ARRAY мы получим (для нашего примера) либо ассоциативный массив со значениями вида ($type = MYSQL_ASSOC)
Автор => Р. Яргер и др.
Название книги => MySQL и mSQL. Базы данных для небольших предприятий и Интернета
Автор => Ларри УОЛЛ
Название книги => Программирование на Perl
либо ($type = MYSQL_NUM)
0 => Р. Яргер и др.
1 => MySQL и mSQL. Базы данных для небольших предприятий и Интернета
0 => Ларри УОЛЛ
1 => Программирование на Perl
(здесь имеем ассоциативный массив, в котором ключ - номер поля в затребованной последовательности полей таблицы в select-е, значение - выбранное значение для поля; нумерация полей начинается с нуля) либо для значения MYSQL_BOTH получаем "винегрет" из первых двух вариантов:
0 => Р. Яргер и др.
Автор => Р. Яргер и др.
1 => MySQL и mSQL. Базы данных для небольших предприятий и Интернета
Название книги => MySQL и mSQL. Базы данных для небольших предприятий и Интернета.
0 => Ларри УОЛЛ
Автор => Ларри УОЛЛ
1 => Программирование на Perl
Название книги => Программирование на Perl
(Еще раз хочу обратить внимание. Имена полей здесь получились по-русски потому, что в select-е использована стандартная для SQL декларация алиасов: select field as \'некий текст\'.)
Еще один метод обработки выбранных данных - функция MYSQL_FETCH_OBJECT. Она по своей сути аналогична MYSQL_FETCH_ARRAY (как по параметрам, так и по значениям второго параметра - константам), с той лишь разницей, что возвращаемое значение - объект:
while($row=mysql_fetch_object($result,$type)) {
$data=get_object_vars($row);
//сформировали ассоциативный массив из переменных объекта
while(list($k,$v)=each($data)) {
echo "$k => $v<br>";
}
}
Здесь любопытна вторая строка. Дело в том, что, если бы мы не использовали строки с пробелами в алиасах select-а, то синтаксически все бы выглядело так:
//где-то выше было сказано $result=MYSQL_QUERY("select author, namebook from books");
while($row=mysql_fetch_object($result,$type)){ //остальные функции работы с классами в PHP
//мы рассмотрим в шаге, посвященном классам
echo "$row->author => $row->namebook<br>";
}
Все. Но как обратиться к переменной объекта с именем \'Название книги\', я так и не придумал.
Еще две fetch-функции (MYSQL_FETCH_FIELD() и MYSQL_FETCH_LENGTHS()) возвращают служебную информацию. Их мы рассмотрим в следующем шаге.
P.S. Остальные функции MySQL будут рассмотрены после того, как мы разберем минимальный набор строковых функций, функций работы с массивами и прочие полезности, т.к. без этого далее двигаться крайне затруднительно. Иными словами, двигаться в глубь PHP мы будем сразу по всему "функциональному фронту", постепенно расширяя и наращивая наши возможности.
Функции работы с MySQL. Часть 4.
Функции (MYSQL_FETCH_FIELD() и MYSQL_FETCH_LENGTHS) возвращают служебную информацию. MYSQL_FETCH_FIELD возвращает объект, содержащий характеристики полей, участвовавших в запросе. Например,
//где-то выше выполнен селект:
$res=mysql_db_query("books","select * from books");
//теперь, имея идентификатор QUERY-ресурса, получаем служебную информацию
$i=0;
while($i<mysql_num_fields($res)) {//получим количество полей в запросе
$field=mysql_fetch_field($res,$i)//и выводим по каждому полю информацию
foreach(($vars=get_object_vars($field)) as $k => $v) {
echo "$k => $v<br>";
}
$i++;
}
В результате получим что-то вроде этого:
name => id
table => books
def =>
max_length => 1
not_null => 1
primary_key => 1
multiple_key => 0
unique_key => 0
numeric => 1
blob => 0
type => int
unsigned => 0
zerofill => 0
и т.д. для каждого поля запроса (мы организовали цикл по всем полям - см. код). Что мы тут имеем (в данном случае, для поля \'ID\' таблицы \'BOOKS\')?
Функции MYSQL_FETCH_FIELDS были переданы 2 параметра: идентификатор результата SQL-запроса и номер поля в запросе. Функция вернула объект со следующими полями:
Name - имя поля в запросе (или alias, если он использован), table - сама таблица, def - НЛО какое-то! я так и не выяснил, что это за значение; это не DEFAULT-атрибут поля таблицы (документация и php.net вместе с zend.com-ом молчат как рыба об лед! :-( ), max_length - максимальная длина среди результатов (имеется в виду не объявленная при создании таблицы длина поля, а количество символов самого длинного значения для данного поля таблицы, полученное при данном запросе. Остальные атрибуты, я думаю, очевидны. Напомню лишь по последнему значению: zerofill означает, что для цифрового поля при выводе (select-е) вместо лидирующих пробелов будут нули.
Теперь о функции MYSQL_FETCH_LENGTHS. Здесь все совсем просто. Единственный параметр - все тот же идентификатор ресурсов, возвращаемый query-функцией. На выходе - обычный массив, заполненный длинами для всех полей текущей строки запроса к базе.
Если взять i-е поле запроса, то
mysql_fetch_fields($res,$i) == MAX( mysql_fetch_length($res) [$i] )
где MAX - обозначение процедуры поиска максимального значения среди i-ых элементов всех массивов, возвращаемых MYSQL_FETCH_LENGTH при переборе всех строк, полученных в запросе к базе.
На этом дальнейшее рассмотрение mysql-функций PHP пока закончим. Остальное рассмотрим позднее - когда разберемся с стринговыми функциями, массивами и прочими необходимыми для дальнейшего развития нашего книжного проекта возможностями PHP.
Функции работы с массивами.
Рассмотрев минимальное подмножество функций работы с sql-сервером, перейдем к другим частям "джентльментского набора".
Пока верстался номер...;) На сайте "How IT works", в серии статей по PHP опубликовано описание работы с массивами в PHP. Как нельзя кстати!
"Массивных" функций в PHP целых 46 штук. Причем штук 20 из них появились в 4-й версии PHP. Это в основном группа функций вида ARRAY_xxxxx, где xxxxx - функциональность.
Функция ARRAY() - самая основная в этой группе. Она предназначена для создания массива из списка.
$fruits = array (
"fruits" => array ("a"=>"orange", "b"=>"banana", "c"=>"apple"),
"numbers" => array (1, 2, 3, 4, 5, 6),
"holes" => array ("first", 5 => "second", "third")
);
Это пример взят из мануала. Вполне удачный. Что мы тут видим? Создается ассоциативный массив, первый элемент которого представляет ассоциативный массив из трех элементов, второй - обычный массив из 6-ти элементов (отсчет индексов ведется с нуля) и третий - обычный массив из трех элементов, индекс первого из которых - "0", второго - "5" и третьего - "6" (следующий после индекса предыдущего элемента).
Как достучаться до элементов массива? Ну, например, так:
echo $fruits["fruits"]["a"];
Результат:
orange
Или так:
foreach($fruits["fruits"] as $k => $v) {
echo "$k => $v<br>";
}
Видим
a => orange
b => banana
c => apple
И, наконец, весь массив:
foreach($fruits as $k => $v) {
foreach($v as $k2 => $v2) {
echo "$k2 => $v2<br>";
}
}
Получаем:
a => orange
b => banana
c => apple
0 => 1
1 => 2
2 => 3
3 => 4
4 => 5
5 => 6
0 => first
5 => second
6 => third
В примерах задействован оператор языка FOREACH. Этот оператор появился в 4-й версии PHP. Для 3-й версии для получения того же результата потребуется задействовать сразу три важных функции: RESET(), EACH() и LIST().
RESET(массив) устанавливает внутренний указатель массива (указатель на текущий обрабатываемый элемент) на начало массива. EACH(массив) возвращает ассоциативный массив из 4-х элементов:
reset($fruits["fruits"]);
$arr=each($fruits["fruits"]);
1 => orange
value => orange
0 => a
key => a
Или в более общем виде:
$arr["0"] => {$key|$index}, $arr["1"] => $value, $arr["key"] => {$key|$index}, $arr["value"] => $value, где $key - ключ в ассоциативном массиве, $index - индекс для обычного массива,$value - значение ключа или индекса. Внутренний указатель перемещается на следующий элемент. Если достигнут конец массива, то вернется false. И LIST(список переменных) присваивает последовательно элементы массива переменным, указанным в параметрах функции. Например, конструкция
list(,,$a,$b)=array(1,2,3,4,5,6);
присвоит переменным $a и $b значения третьего и четвертого элементов массива(первый, второй и все остальные после четвертого мы "прокинули"). Кстати, в примере с while-list-each мы так и поступили - использовали в LIST() только первые два элемента возвращаемого EACH() массива.
Теперь пример, аналогичный FOREACH-конструкции, приведенной в начале шага:
echo "<table border=0>";
reset($fruits);
while((list($k,$v)=each($fruits))) {
echo "<tr><td>$k:</td><td></td></tr>";
reset($v);
while((list($k2,$v2)=each($v))) {
echo "<tr><td></td><td>$k2 => $v2</td></tr>";
}
}
echo "</table>";
Еще три функции навигации по массивам: CURRENT(массив) возвращает значение текущего элемента массива; если достигнут конец массива - вернется false (при этом для пустого массива также вернется false). NEXT(массив) и PREV(массив) - перемещение внутреннего указателя вперед/назад. Как обычно, достижение начала/конца массива сопровождается возвратом false.END(массив) переместит указатель на конец массива - антоним RESET-а!
Функции работы с массивами. Часть 2.
В предыдущем шаге я в числе прочего описал функцию CURRENT(), возвращающую
значение текущего элемента маасива, и совсем забыл отметить, что функция есть
POS() - синоним для CURRENT(). Просто алиас.
Теперь продолжим по массивам. Функция EXTRACT() создает переменные на основе
ассоциативного массива. Функция имеет три параметра: исходный массив, тип операции (необязателен),
строка-префикс (необязательный; для типов операций EXTR_PREFIX_SAME и EXTR_PREFIX_ALL) -
для создания переменных с строкой-префиксом в названии, не попадающих под правило, определяемое
типом операции.
Теперь подробнее. Но сначала для большей наглядности демонстрационный пример, взятый из мануала
и несколько переделанный:
//массив типов операций
$types=array(EXTR_OVERWRITE, EXTR_SKIP, EXTR_PREFIX_SAME, EXTR_PREFIX_ALL);
//массив, над которым будем измываться ;)
$var_array = array ("color" => "blue",
"size" => "medium",
"shape" => "sphere");
foreach($types as $type) {
//инициализация перед очередным вызовом extract-а
unset($color,$shape,$wddx_color,$wddx_size,$wddx_shape);
$size = "large";
echo "<p>";
//позвали
extract ($var_array, $type, "wddx");
print "$color, $size, $shape; $wddx_color, $wddx_size, $wddx_shape<br>\\n";
}
Результат выполнения:
blue, medium, sphere; , ,
blue, large, sphere; , ,
blue, large, sphere; , medium,
, large, ; blue, medium, sphere
Как Вы, наверное, заметили, тип операции задается константой (допустимые значения от 0 до 3).
Перед выполнением EXTRACT()-а проинициализирована одна из создаваемых функцией
переменных - $size. Значение 2-го параметра EXTR_OVERVIEW (значение по умолчанию)
заставляет функцию перезаписывать значение этой переменной новым значением (из массива).
EXTR_SKIP не "портит" значение $size.
EXTR_PREFIX_SAME также не изменяет $size,
но создает одноименную переменную с префиксом, передаваемым в третьем параметре (в этом случае
третий параметр обязателен - умолчания нет!), т.е. в данном примере переменную wddx_size.
И наконец, EXTR_PREFIX_ALL, не трогая $size, создает на каждый ключ массива
переменную с префиксом.
Остается добавить, что функция предназначена только для обработки ассоциативных массивов.
Если встретившийся ей элемент массива не является ассоциативным, то функция посчитает
его некорректным и проигнорирует, т.к. не сможет создать переменную, начинающуюся с цифры.
То же касается и случая
$var_array=array("Кто назвал"=>"даму сэром?");
т.к. в имени переменной не может быть пробелов - ключ для функции неверен.
Мануал предлагает использовать эту функцию для обработки массивов с результатами работы функции группы WDDX_xxxx (от сюда и строка префикса в примере).
К WDDX-группе мы перейдем ближе к финалу рассмотрения возможностей PHP, когда вплотную займемся работой с XML. По описанию на сайте разработчиков WDDX-концепция основана на XML-технологии (DTD) и предназначена для сериализации данных, под которой авторы понимают сохранение и передачу данных между разными скриптовыми технологиями на стороне сервера (например, PHP ;). Необходимоть такого решения обуславливается различиями внутреннего формата данных в разных скриптовых решениях (ColdFusion, Perl, ASP, Java, JavaScript, PHP и пр.).
(Надеюсь, я правильно понял описание...)
Функция KEY() возвращает текущее значение ключа массива (напомню, когда речь идет о текущем значении, имеется в виду внутренний указатель на элемент массива). В качестве параметра передается массив. Если массив неассоциативный, то ключем является индекс.
Функция RANGE() создает массив из диапазона чисел:
foreach(range(10,20) as $k=>$v) {
echo "$k => $v<br>";
}
Функция SHUFFLE() случайным образом "перемешивает" содержимое массива (правда, для полноценной "случайности" надо проинициализировать псевдослучайную последовательность функцией SRAND(), дав ей на вход уникальное число, например, от функции TIME()).
Вот пример (взят из мануала).
srand(time());
$q=range(10,20); //Создали массив вида q[0]=10, q[1]=11, q[2]=12 и т.д.
shuffle($q); //"перемешали" содержимое
foreach($q as $k=>$v) {
echo "$k => $v<br>";
}
Массив может быть как с числовыми индексами, так и ассоциативный. Индексы остаются по-прежнему в порядке возрастания - перетасовывается только содержимое.
Лирическое отступление - функции для Oracle.
Прошу прощения за непоследовательность изложения, но пропустить такую тему в данном случае я бы очень не хотел. Касаться темы "PHP и Oracle" я не планировал (по крайней мере в обозримом будущем). Но на днях пришлось-таки сделать выборку с базы на Oracle. Т.к. тема эта, вообще говоря, популярна, то я посчитал нужным сделать отступление от "генерального плана" и вкратце описать структуру скрипта, осуществляющего работу с базой на Oracle-сервере.
Сразу оговорюсь. В PHP есть две группы функций для работы с БД на Oracle: "Oracle function" и "Oracle 8 function". Последняя, как ясно из названия, предназначена для работы с ораклом 8-й версии. Я же делал выборку из базы на 7-м оракле. Поэтому продемонстрирую работу с первой группой.
Общая схема такова. Есть сервер oracle и есть клиент (в моем случае Windows NT). На клиентской машине установлен клиент Oracle (нужен SQL-NET). Пусть в TNSNAMES.ora прописан коннект к oracle-базе с именем TEST. Допустим, есть таблица такой структуры:
TEST_TAB: field1, field2, field3, field4. Выберем поля field1, field2 и field4.
Теперь сам скрипт:
$user=\'user\';
$tns=\'test\'; //имя из TNSNAMES.ora
$pass=\'password\'; //крутой пароль ;)
//выбираемые из таблицы поля
$fields=split(\',\',"field1,field2,field4");
//формируем строку запроса
$sql="select ".implode(\',\',$fields)." from test_tab where field1=\'значение\'";
//коннект
$conn=ora_logon("$user@$tns","$pass") or die(\'Error ORA_LOGON\');
//создали курсор
$cur=ora_open($conn) or die(ora_errorcode($conn).\': \'.ora_error($conn));
$defer=0; //а что это такое - я пока, увы, не знаю :-(
//парсинг строки sql-запроса и связывание ее с курсором
ora_parse($cur,$sql,$defer) or die(ora_errorcode($cur).\': \'.ora_error($cur));
//выполнить запрос к базе
ora_exec($cur) or die(ora_errorcode($cur).\': \'.ora_error($cur));
//формируем вывод результатов выборки
echo \'<table border=1><tr>\';
for($col=0;$col < count($fields);$col++) { //заголовок
$col_nam=Ora_ColumnName($cur,$col); //имена колонок (или алиасы)
$col_typ=Ora_ColumnType($cur,$col); //типы полей в выборки - для иллюстрации
echo "<th>$col_nam<br>($col_typ)</th>";
}
while(ora_fetch($cur)) { //выбираем значения по строкам
echo \'</tr><tr>\';
for($col=0;$col < count($fields);$col++) {
$result=ora_getcolumn($cur,$col);
echo "<td>$result</td>";
}
echo \'</tr>\';
}
echo \'</table>\';
ora_close($cur); //удалить курсор
ora_logoff($conn); //разорвать соединение
Вы, наверное, заметили, что в целом логика работы с oracle-базой похожа на работу с MySQL. Разве, что парсинг запроса вынесен в отдельную функцию.
P.S. Вот и вышел PHP 4.0.4 ! Говорят, там gd-функции работают и в режиме "PHP как модуль Apache". (Это я в PHP-конференсии прочел ;)
Лирическое отступление-2 - Работа с графикой.
Что-то меня на лирику потянуло...;) Поставил себе PHP версии 4.0.4 и понял, что прийдется еще раз отойти от "генеральной линии". Дело в том, что версия 4.0.4 теперь ставится как модуль к Apache и при этом image-функции прекрасно работают!
Но сначала несколько замечаний. Во-первых, при установке PHP как модуля к Apache следует в точности руководствоваться инструкциями по установке PHP, приведенным на сайте How IT work за 18.10.2000, включая требование переноса файла php.ini в windows-каталог (для установки как cgi это было не обязательно - можно хранить ini-файл вместе с php.exe). Во-вторых, после внесения изменений в php.ini надо перезапустить Apache, иначе изменения видны не будут (ну правильно, ставим ведь как модуль!).
Для работы с графическими функциями нужно подключить GD-библиотеку. Для этого в php.ini надо раскомментировать строку "extension=php_gd.dll" (и перезапустить Apache!).
Проверьте также правильность пути расположения dll-файлов библиотек для PHP (строка "extension_dir =" в php.ini).
Графические функции в PHP работают с тремя форматами: GIF, JPEG и PNG. На самом деле поддерживаются (по крайней мере официально) только два формата. GIF из-за проблем с лицензированием в версии 1.6 библиотеки GD в PHP не поддерживается. Т.е. при выполнении скрипта PHP, встретив функцию GIF-группы, не выругается, но и ничего делать не будет.
Теперь о функциях работы с графикой.
Чрезвычайно полезная для организации структуры html-страницы функция GETIMAGESIZE() получает на вход имя файла-картинки (кстати, в порядке исключения, эта функция может работать еще и с форматом SWF ), а возвращает массив из четырех элементов: ширина и высота картинки в пикселях, тип формата (1 = GIF, 2 = JPG, 3 = PNG, 4 = SWF) и четвертый элемент массива (индекс = 3) содержит самую полезную строку для html-кодировщика - атрибут тега IMG: height=xxx width=xxx, где \'xxx\' - значения размеров. Полезнейший атрибут для тех, кто хочет избежать неприятного эффекта "прыгающего" текста, когда по мере подгружения браузером вставленных в текст картинок читающий пользователь видит сдвиги текста вниз по экрану (это браузер наконец вычислил размер области под изображение). Второй параметр (необязательный) для функции GETIMAGESIZE() - адрес массива для получения дополнительной информации из заголовка JPG-файлов (к сожалению, я не знаю подробностей на эту тему). Кстати, для этой функции подключать GD-библиотеку в PHP не нужно.
В image-разделе есть группа фукнций, работающих с текстом (формирование изображений из текста) на основе шрифтов в GD-формате. Сформировать такой шрифт можно с помощью утилиты bdf2gdfont, исходный код которой входит, например, в поставку Linux-а. Эта утилита делает GD-фонт из т.н. BDF-шрифта. Насколько я понял, это шрифт предназначен для X-windows (я неправ?).
В любом случае мне с этими шрифтами иметь дело не приходилось, поэтому я опущу рассмотрение функций работы с такими шрифтами (речь идет о таких функциях как ImageLoadFont(), ImageString(), ImageChar(), ImageFontHeight(), ImageFontWidth() и др. Отличить их можно по наличию параметра - идентификатора ресурса шрифта (в мануале "int font"), который возвращает функция ImageLoadFont().
Кстати, есть еще группа функций для работы с postscript-шрифтами: IMAGEPSxxxxx, где \'xxxxx\' - функциональность в названии функции. С PS-шрифтами я тоже не работал (пока) и посему рассматривать не буду.
А еще есть функции для работы с truetype-шрифтами. Вот их-то мы и будем использовать!
Работа с графикой-2.
Логика работы с графикой проста. Есть html-страница, в которой надо вывести картинку. Пусть саму картинку генерирует скрипт, находящийся в файле image.php. В основном html-файле тогда будет написано что-то типа этого:
<img src="image.php?r=150&g=150&b=150">
Здесь мы передаем в скрипт, генерирующий картинку (в примере будет серый прямоугольник), RGB-палитру цвета формируемого прямоугольника (напомню, что эти переменные, передаваемые в скрипт как параметры, будут в нем видны так, как если бы они были в нем объявлены и проинициализированы передаваемыми в вызывающем скрипте параметрами, т.е. для их использования ничего делать не надо!).
В image.php будет такой код:
<?php
Header ("Content-type: image/jpeg"); //HTTP-заголовок для картинки
$im = ImageCreate (500, 30); //создаем image
$background = ImageColorAllocate ($im, $r, $g, $b); //создаем цвет фона
ImageJpeg ($im); //выводим image
ImageDestroy ($im); //освободим память, выделенную под image
?>
Комментарии здесь следующие. Первое - в файле image.php перед тегом, открывающим php-секцию, не должно быть ни одного символа (даже пробелов!). До функции HEADER() также не должно быть ни одного оператора ECHO или PRINT. Файл не должен начинаться с декларации html (<HTML>).
Это важно потому, что при появлении символов на вывод web-сервер прекращает формирование HTTP-заголовка и переходит к формированию секции данных (например, собственно html-страницы). А функция HEADER() как раз и добавляет/заменяет секцию заголовка (в данном случае, отвечающую за тип данных).
Кстати, "обойти" это ограничение можно буферизацией вывода. Т.е. это не обход, а организация выполнения скрипта - до окончания формирования HTTP-заголовка выводимая информация помещается в буфер, а потом буфер сбрасывается в выходной поток. В PHP есть для этого соответствующие функции.
Функция IMAGECREATE() создает image размера, указанного в параметрах функции: ширина и высота. Возвращаемое значение - целое, являющееся идентификатором ресурса image.
IMAGECOLORALLOCATE() возвращает идентификатор цвета, формируемого на основе RGB-значений (2..4 параметры; первый параметр - идентификатор image-ресурса).
IMAGEJPEG() формирует картинку (в данном случае, jpeg-формата) для передачи сервером клиентскому браузеру. Всего такого рода функций три. Кроме указанной для jpeg-формата есть еще IMAGEPNG() и IMAGEGIF() для png- и gif-форматов соответственно (формально, а на самом деле gif-ы, как я уже говорил, не поддерживаются, так что всего функций, формирующих картинки, две).
IMAGEDESTROY() освобождает память, выделенную функцией IMAGECREATE() под image. Единственный параметр - идентификатор image-ресурса.
Фоном картинки становится первый созданный после создания image цвет. А как сделать фон картинки прозрачным? Очень просто. Для этого есть функция IMAGECOLORTRANSPARENT(), ставящая для указанного цвета в указанной картинке атрибут transparent. Первый параметр этой функции - идентификатор image-ресурса, а второй (необязательный) - идентификатор созданного цвета, например:
imagecolortransparent ($im, $color);
Вообще говоря, идентификаторы создаваемых цветов представляют из себя, как я понял, целые числа, начинающиеся с нуля (первый вызов IMAGECOLORALLOCATE()) и увеличивающиеся на единицу при каждом последующем вызове функции создания цвета. Явного указания на этот факт в мануале я, правда, не нашел (не туда смотрел?).
P.S. Кстати, если у Вас картинка все же не формируется, проверьте синтаксис. В случае ошибок PHP выводит в выходной поток (сервер отдаст этот вывод браузеру) сообщение об ошибке (если в php.ini параметр "error_reporting" выставлен в "on"). Причем выводит до выполнения скрипта, т.е. на этапе компиляции, а не выполнения, а значит, до выполнения функции HEADER() - см. выше.
Если же в php.ini параметр "display_errors" вместо умолчательного значения "E_ALL & ~E_NOTICE" будет равен, скажем, "E_ALL" (т.е. выводить все сообщения, включая предупреждения), то остаться без картинки можно, например, использовав необъявленную переменую.
Подробности к шагу 12.
Для экспериментов с операторами и функциями mysql удобно создать таблицу-пустышку по аналогии с oracle-иным решением (я думаю, Вы уже создали какую-нибудь базу для экспериментов, ну, например, mysqladmin create test):
connect test;
create table dual (id char(1));
insert into dual values(\'X\');
Итак, сокращенный список операторов и функций MySQL. (Здесь описаны лишь некоторые функции, отобранные в основном по критерию отличности от SQL-стандарта.)
Условия WHERE:
- [NOT] BETWEEN .. AND
- диапазон (как в стандарте - просто напоминаю).
- [NOT] IN (...)
- список значений (то же замечание).
- Стандартный оператор [NOT] LIKE строка_с_подстановками,
- где подстановки могут быть символом \'%\' (любое число символов) и/или \'_\' (одиночный символ), например,
поле field в таблице test некоторой записи содержит такой текст: "PHP - рулез форева!";
тогда добраться до него можно так:
select * from test where field like \'%рул_з%\'
(предполагается, что мы забыли, что именно является рулезом и как вообще пишется рулез: "рулЕз" или "рУлиз" ;)
- [NOT] REGEXP/RLIKE (а вот это интересно!)
- использование регулярных выражений (!), например, если таблица из предыдущего примера хранит записи с утверждениями для всех версий PHP, а мы согласны с рулезностью только 3-й и 4-й версий, то условие WHERE можно записать так:
field regexp \'^PHP[3|4]\'
- LIMIT [начало,] конец_выборки
- ограничение диапазона выборки.
По моему скромному разумению, без этой фразы mysql в интернете не прижился бы.
LIMIT позволяет ограничить объем выборки указанным диапазоном. На этой штуке базируется логика выборки данных для всяких форумов, конференций, новостных страниц и пр. Почему? Ну представьте, что посетитель Вашей страницы хочет просмотреть список новостей, начиная с текущей даты. Вы можете вывалить ему в ответ список сообщений (допустим, их накопилось штук 300 или 1000), превратив бегунок вертикального скроллбара броузера в узкую полоску или выдать сообщения порциями по нескольку штук. Но где хранить всю выборку между выдачами очередных порций? Ведь после ответа на запрос клиентского броузера web-сервер разорвет соединение, освободив выделенные ранее ресурсы. Решение может быть таким:
$result=mysql_query("SELECT * FROM ".$table." LIMIT ".$start.", ".$rows)
где $start=$rows * $page_number, а $page_number - параметр, на 1 больший номера текущей страницы для перехода к следующей "пачке", и на 1 меньший - для предыдущей порции Ваших сообщений. Этот прием будет продемонстрирован в следующих шагах, когда мы разработаем полноценный отчет по книжным спискам.
- PROCEDURE имя_процедуры
- с версии 3.22 можно задать процедуру обработки запроса.
Эту возможность я сам еще не использовал. Привожу ее для "кучи". Позднее разберусь - опишу.
Некоторые функции, которые могут быть использованы в WHERE и в SELECT (скажем, в конструкции SELECT функция(...) FROM dual, где dual - созданная нами таблица с одной (все равно какой) записью - см. замечание в начале "Подробностей").
- DATABASE()
-
- вернет имя текуще базы (скажем,
SELECT DATASET() FROM какая_нибудь_таблица_из_открытой_базы LIMIT 1
(LIMIT - чтобы на выводе иметь только одну строку.).
- Обращаю внимание на изобилие форматов вывода для дат в функции DATE_FORMAT().
- ENCRYPT и PASSWORD
- предназначены для шифрования строк (по-моему, по методу DES от UNIX-операционок).