Книга
Книга
ЧАСТЬ I
Основы РАЗРАБОТКИПРИЛОЖЕНИЙ НА PHP 5
Глава 1
Работа
с Web-сервером Apache
Установка Web-сервера Apache 2.0.0
В этом разделе мы рассмотрим установку Web-сервера Apache 2.0.0 на операционные системы Windows н Linux. В любом случае, первое, что необходимо сделать, это загрузить исходные коды дистрибутивов с официального сайта Apache http://www.apache.org.
Установка под Windows
Для успешной установки Web-сервера Apache под Windows необходимо, чтобы используемая версия Windows корректно работала со стеком протокола TCP/IP. Пользователям Windows 2000 и Windows ХР об этом беспокоить» ся не следует. В случае более ранних версий этой операционной системы необходимо убедиться в том, что для них произведены необходимые обновления.
工 Примечание ;
Несмотря на то, что Web-сервер Apache уже давно перенесен и успешно функционирует под Windows, только с версии Apache 200 произведена оптимизация исходных кодов с целью использования системных вызовов Windows. До этого использовался уровень POSIX, что приводило к недостаточной производительности при работе с Apache под Windows.
Apache под Windows доступен как в виде исходных кодов, так и в виде откомпилированного пакета. Исходные коды могут понадобиться в том случае, если вы решили при установке перекомпилировать Apache. Перекомпн- лирование исходных кодов необходимо тогда, когда нужно исключить из исполняемой версии неиспользуемые функции или включить поддержку функций, не входящих в стандартную поставку. В этом случае необходимо наличие установленной Microsoft Visual Studio. Если стандартная компиляция сервера вас устраивает, можно приступить к установке.
:Замечание )
Бинарные коды дистрибутивов Apache распространяются в нескольких вариантах, как с расширением ехе, так и с расширением msi и имеют название вида httpd_BepcMR_win32_*_.msi.
После двойного щелчка на файле httpd_BepcnH_wiп32_*_,rnsi запустится Microsoft Installer (рис. 1.1).
Рис. 1.1. Окно программы Windows Installer
; Замечание )
Такого рода фзйлы часто не запускаются из некоторых программ: Far, Norton Commander и т. д., их следует запускать из Проводника.
Для начала установки нажмите кнопку Next, после чего появится окно с лицензионным соглашением, показанное на рис. 1.2. После принятия лицензионного соглашения следует перейти к следующему окну с краткой информацией о нововведениях во второй версии Apache (рис. 1.3). Следующее окно, показанное на рис. L4, позволяет ввести информацию о сервере: доменное имя сервера, имя сервера и адрес электронной почты администратора. Если установка происходит на локальную машину, то в поля лля до-
менного нменн н имени сервера следует ввести localhost (см. рис. 1.4). В нижней части окна предлагается выбрать номер порта, по которому сервер будет принимать запросы (80 или 8080),
Рис. 1.2. Лицензионное соглашение
( Замечание )
Обычно используется стандартный для протокола HTTP порт 80; если для запуска сервера Apache по порту 80 не достаточно прав, следует выбрать порт 8080.
После этого будет предложен способ установки (рис. I.5): типичный (Typical) или выборочный (Custom), позволяющий выбрать компоненты сервера вручную. Следующее окно позволяет выбрать каталог установки сервера (рис. L6), по умолчанию это C:\Program Files\Apache Group. После этого мастер установки сообщит о готовности к процессу установки (рис L7), и после нажатия кнопки Install будет произведено копирование файлов сервера (рис. 1.8). Если установка прошла успешно, Windows автоматически запустит Apache. По умолчанию вместе с сервером запускается утилита мониторинга работы сервера ApacheMoniior, значок которой помещается в системном триере (рис. 1.9).
滑 Apaclie HTTP Saver 2Л) - Installatioi) Wizard
Redd This First
Read this Before Running Apache on Windows.
Apache HTTP Server二
What is it?
■ The Apache HTTP Server is a powerful and flexible HTTP/1,1 compliant web server Originally designed as a replacement for the NCSA HTTP Server, it has grown to be the — most popular web server on the Internet. As a project of the Apache Software Foundation, the developers aim to collaboratively develop and maintain a robust, commercial-grade, standards-based server with freely available source code.
Th®賞Version
Details of the latest version can be found on the Apache HTTP server project page
'under:
http;//hlipd.apai:he 口 W
Documentamn
'The documentation available as of the date of this refease is included in HTML format in 色
< BackNext>Cancel
Рис. 1.3. Краткое описание нововведений в версии Apache 2.0
Рис. 1.5. Выбор способа установки
Рис. 1.6. Выбор каталога установки Apache
Рис. 1.7. Мастер установки готов к копированию файлов
Рис. 1.8. Завершение установки
歌痴<*啬■esr 17:27
Рис. 1.9. Значок утилиты ApacheMonitorв системном триере
I Замензнме )
Работа утилиты ApacheMonitor не влияет на работу сервера, и ее можно отключить. Если далее она потребуется, ее можно найти в подкаталоге bin каталоге установки Apache.
Проверить работоспособность сервера можно, набрав в браузере http:// localhost (если установка проводилась для локальной машиньг); в случае успешной установки вы увидите страницу, изображенную на рис, 1.10.
If you cau кее Ihis. if menu?; that the iiistnllahoik of the Annch+l'Mb。芻止目'sofhvme on Uii^ /-ystem \vn^ 淵иссек^Ьй Yun шлу not* add content to thiu dircGory njid icpkice tliiw page
Seeing this instead of the website you expected?
This page ts bei c becmise the site ndinhibtrnt0rlm^ ^bruiged the caiiEgiu aliou of this web sen'er 卩len昵 contort the регион i^Kpoiuible for imiiiif;Utdng tlib sen ei* widi <iur«tiou?r. Hie Apnche SoSware Foitu^lion. which m ote the web $епчн Boflvvart Hhs mite ;uhumi爾tMs is u»iugt b^ notliing to d& with mniufaiiuxiu thh site mi4 cmmot lielp rettolvc couilgivtilioi) isnieN.
The Apnche docume耐ahon lw been included 2山 this distribution
You nre fi ee to u*e the mim発 below on an Ajjaclw-paivered web *ei*ver, Thad淸 Rr wiuy Apache!
Рис. 1 ЛО. Web-сервер Apache успешно установлен
Управление Apache
В Windows управлять работой сервера Apache можно несколькими способами:
口 при помощи утилиты ApacheMonitor;
口 используя консоль управления сервисов Windows;
口 используя пункты меню Пуск;
□ из командной строки.
Управление Apache
при помощи ApacheMonitor
Для запуска Web-сервера Apache при помощи утилиты ApacheMonitor дважды щелкните на значке программы в системном триере. В появившемся окне, изображенном на рис 1.11, кнопками Start, Stop и Restart можно производить пуск, остановку и перезапуск сервера.
Рис. 1.11. Запуск Apache из триера программ
Управление Apache
из консоли управления служб Windows
Если при установке сервера в качестве порта, по которому Apache принимает запросы, был выбран порт 80 (см. рис. 1.4), допускается запуск Apache в качестве сервиса Windows. Для запуска консоли управления выполните команду Пуск | Настройка | Панель управления | Администрирование | Службы или нажмите кнопку Services в утилите ApacheMonitor В появившемся окне консоли, показанном на рис. 1.12, следует выбрать сервис Apache2. Контекстное меню позволяет осуществлять запуск, остановку и перезапуск сервиса. Службы Windows позволяют осуществлять запуск фоновых приложений при старте системы. Для этого необходимо выбрать в контекстном меню сервиса пункт Свойства (рис. 1Л2) и в появившемся окне (рнс. 1.13), в выпадающем списке Тип запуска выбрать пункт Авто.
Рис. 1.12. Службы Windows
Рис. 1.13. Окно свойства сервиса Apache2
Управление Apache из меню Пуск
Осуществлять пуск, остановку и перезапуск сервера Apache можно из меню Пуск. Для этого следует перейти в меню: Пуск | Программы | Apache HTTP Server | Control Apache Server. Здесь можно осуществить пуск, остановку и перезапуск сервера путем выбора пунктов Start, Stop и Restart.
Управление Apache из командной строки
Запускать, останавливать и перезапускать сервер Apache из командной строки можно при помощи следующих команд:
口 Apache -k start (старт);
口 Apache -к restart (перезапуск);
口 Apache -к stop ИЛИ Apache -к shutdown(СТОП).
Все команды следует выполнять из каталога bin сервера Apache (C:\Program Files\Apache G roup\Apache2\bin\).
I З.менание )
Команды с ключом -к являются управляющими командами сервера Apache. Так, команды Apache -k install и Apache -к uninstall позволяют установить и удалить сервис Apache2.
Замечание )
Получить полный список команд управления с их кратким описанием можно, воспользовавшись командой Apache -help или обратившись к документации сервера Apache.
Команда Apache -t позволяет проверить конфигурационные файлы Apache на предмет наличия синтаксических ошибок. В случае их отсугствия выдается строка Syntax ок. Если же в конфигурациомных файлах имеются ошибки, то в результате тестирования программа выдаст сообщение об ошибке, к примеру:
Syntax error on line 57 of C:/Program Files/Apache Group/Apache 2/ conf/ httpd.conf:
ServerRoot takes one argument. Common directory of server-related files
Установка под Linux
Web-сервер Apache присутствует практически в каждом дистрибутиве Linux, поэтому скорее всего установка его не потребуется. Если при установке дистрибутива пакет, содержащий Apache, не был установлен или требуется ус-
тановка более новой версии сервера, разархивируйте исходные коды Apache в любую папку:
tar -xzf apache_x.х.хх,хх.tar.gz
:Замечание )
Установку сервера Apache следует производить, обладая правами root.
При помощи сценария configure, входящего в исходные коды, необходимо выполнить конфигурирование кодов перед установкой. Данный сценарий прнннмает множество параметров, среди которых обязательным для использования является параметр --prefix, позволяющий указать путь установки сервера.
В листинге 1.1 приведен пример команды конфигурирования, задающей в качестве каталога установки: /usr/local/apache/, и исключающей поддержку сервером SSI и исполнение CGI-модулей.
出塞竿斗炉:把乎■,产型0%弊fav然工般关装車学豈雪;呼иуау逑飞岭绕?:?у片门*产,, FtH*,#鞏皆嘗聲产中…¥小豈.** ±七”” **.”附哼学学中廿产.У咨*力:少3整 j Листинг 1.1. Сценарий configure
# cd apache__x * x. xx. xx
若 */configure --prefix=/usr/local/араch.e/\ --disable*include\ ―disable-cgi
Возможные параметры сценария configure приведены в табл. L1. Обычно большинство из этих параметров не используется, н, как правило, необходимо указать лишь значение параметра prefix и параметры для разреше- ния/запрещения установки некоторых модулей, к примеру, как это показано в листинге L1.
Таблица ТЛ. Основные параметры сценария configure
Параметр
Описание параметра
—prefics=pr
Устанавливает архитектурно-независи мые файлы в pr=/usr/local/apache
—bindir=dir
Устанавливает исполняемые файлы пользователей в каталог dir
—sbindir==dir
Устанавливает исполняемые файлы администратора в каталог dir
--Libexecdi r=d i г
Устанавливает программные файлы в каталог dir
―datadir=dlr
Устанавливает архитектурно-независимые файлы, доступные только для чтения в каталог dir
Таблица 7Л (окончание)
ПараметрОписание параметра
—infodir=dir
Устанавливает документацию info в каталог dir
—withport=port
Устанавливает порт port, к которому производится обращение
—enable-debug
Включает сообщения отладки и компиляции
—enable-file- cache
Разрешает кэширование файлов
―enable-echo
Включает ЕСНО-сервер
—enable-cache
Включает динамическое кэширование
—disable-include
Выключает поддержку SSI-расширений
—disable-http
Выключает поддержку НТТР-протокола
—disable-mime
Выключает поддержку преобразований расширений файлов в MIME
—disable-dir
Выключает обработку запросов на доступ к каталогам
——enable-head®rs
Включает управление НТТР-заголовками
—enable-tls
Включает TLS/SSL
--enable-cgid
Включает поддержку CGI-сценариев
—disable-cgi
Выключает поддержку CGI-сценариев
После настройки исходных кодов следует выполнить команды:
make
make install
Команда make произведет компилирования кодов, а команда make install — установку скомпилированных файлов в каталог, указанный в параметре prefix (см. ЛИСТИНГ 1.1).
Управление сервером Apache осуществляется при помощи утилиты apachectl. Так, передача в качестве параметра start позволяет запустить сервер:
/usг/local/apache/bin/apachectl start
Передача в качестве параметров stop н restart позволяет осуществить остановку и перезапуск сервера.
Проверить работоспособность сервера можно, запустив графический (если работа осуществляете* в Window) или текстовый браузер (при работе в консоли): $ lynx http://localhost/
Проверить, работает ли в данный момент сервер Apache, можно командой: $ ps aux I grep httpd
Если установка прошла успешно, эта команда покажет пять рабочих процессов с одинаковым именем httpd.
Для автоматического запуска Apache при старте системы необходимо настроить стартовые сценарии. В RedHat Linux (Fedora Core) следует помес* тить в каталог /etc/rc.d/init.d/ следующий сценарий с именем apache: # ср /usr/local/apache/bin/apachectl /etc/rc.d/init.d/apache # ahmod 755 /etc/rc.d/init.d/apache
Эти команды создают исполняемый сценарий оболочки, доступный на всех уровнях загрузки. Затем в /etc/rc.d/rc3.d/ нужно выполнить команду
in -з ../init.d/apache S99apache # In -s * */init.d/apache KOlapache
Эти две команды создадут символические ссылки в каталоге rc3.d, сообщая системе о необходимости запуска сервера при старте и остановке при завершении работы.
:Замечание )
Предыдущие команды позволят автоматически запускать сервер Apache при загрузке на третьем уровне (многопользовательский доступ, текстовый режим). Если необходимо запускать Apache и на других уровнях, следует создать символические ссылки в других каталогах, например. rc5.d для уровня 5 (мно- гопольэовательский доступ, графическая система Windows).
〔 Знменанме )
В RedHat (Fedora Core) автоматически установить запуск сервера при старте системы можно, воспользовавшись утилитой ntsysv.
12 Конфигурирование Web-сервера Apache 2.0.0
121. Базовая настройка Apache
Для того чтобы Apache помимо HTML-файлов обрабатывал также и РНР- файлы, в строку text/html в файле mime.types следует добавить расширение для файлов, содержащих PHP-скрипты (php, phtml): text/html htal htm shtml shtm php phtml
Основные директивы настройки Apache хранятся в смайле hllpd.conf, который расположен в каталоге C:\Program Files\Apache Group\Apache2\con f\. Для успешной работы этот файл необходимо настроить:
В поле Ser ver Admin следует указать адрес электронной почты администратора сервера. К примеру:
Serve г Admin mymail@yand.ex . г и
Этот адрес будет в дальнейшем показываться в окне браузера в сообщениях об ошибке сервера.
В поле serve匸Name (имя сервера) следует ввести имя сервера, по которому к нему будут обращаться машины из сетевого окружения:
ServerName Maks
В поле корневой директории документов DocumentRoot следует указать директорию, где будут храниться собственно файлы, предоставляемые сервером по запросам (HTML- и РНР-файлы):
DocumentRoot d:\main\html
:)
Хорошим тоном считается выделение для данной директории раздела диска, для которого регулярно производится резервное копирование данных.
Директиву <Directory />, в которой хранятся установки для всех директорий по умолчанию, следует исправить следующим образом:
<Directory />
Options Indexes FollowSymLinks
Al LowOve r r i de None
</Directory>
Директивы Options н AllowOverride применяются к корневому каталогу основного Web-сайта, т. е. к DocwnentRoot. Теперь откроем доступ к каталогу DocumentRoot, поместив следующую директиву:
<Directory "d: \main\htirtlM>
Options Indexes FollowSynfLinks
Al lowOverride None
Order allow,deny
Allow from all
</Directory>
Директива Options установлена в значение FoilowsymLinks, что разрешает Apache следовать символическим связям внутри DocumentRoot. Так как разрешен переход только по символическим связям, остальные опции для каждого из используемых подкаталогов необходимо указывать явно.
Значение indexes этой директивы говорит о том, что указанный каталог и его подкаталоги позволяют индексировать содержимое. Значение none директивы AiiowOverride говорит о том, что ни одна из указанных опций не может быть изменена локальным файлом управления доступом.
:Замечание )
Этот файл указывается директивой AccessFileName и по умолчанию мм является htaccess.
Затем проверяются директивы allow и deny. Клиент, не соответствую- ший директиве allow или соответствующий директиве deny, не получает доступ к серверу. Строка Allow from all означает, что разрешен доступ для всех.
Если необходимо, чтобы пользователи могли хранить файлы своих сайтов в своем домашнем каталоге, следует убрать комментарии напротив директивы oserDir и указать путь к домашнему каталогу пользователей, например:
UserDir Hd: \main\htnLL\users"
Тогда обращаться к домашним страницам пользователей можно будет по адресу http:〃www.uji.nl/~user, где www.url.ru — адрес сервера, a user — имя пользователя.
Если необходимо назначить имена так называемых индексных файлов, которые автоматически будут выданы сервером в случае передачи в адресе только имени папки (например, http://localhost/dort/), необходимо их перечислить после директивы prectoryindex: Directoryindex index.html index.phtml index.php
( Замечание )
Поиск файлов будет производиться в порядке, указанном в директиве Directoryindex. То есть если в папке dort присутствуют файлы index.php и index.html, то при передаче серверу запроса http://localhost/dort/ будет запущен файл index.htmL
〔Замечание )
Имена файлов не обязательно могут называться index.*, но это имя является стандартом де-факто,
Если предполагается хранение CGI-скриптов в директории, отличной от установленной по умолчанию, то следует изменить директиву ScriptAiias на ScriptAlias /cgi-Ып/ "cgidii:"*
Например:
ScriptAlias /cgi-bin/ 11 d: \main\cgi-bin^^
Теперь следует настроить блок параметров, начинающийся с <Directory r,d : \main\cgi-binri>. Здесь хранятся установки CGI-директории (если установлено другое имя» необходимо модифицировать путь). Этот блок может выглядеть следующим образом:
<Directory ” d:XmainXcgi-bin1^
AllowOverride None
Options ExecCGI
</Directory>
Затем необходимо настроить параметр AddHandier:
AddHandler cgi-script *bat .exe
Это позволит Apache рассматривать файлы с расширением ехе и bat как CGI-скрипты.
9* Параметр AddHandler server-parsed следует установить следующим образом:
AddHandler server-parsed .shtml
На этом основной этап установки и настройки Apache завершен.
Необходимо перезапустить сервер Apache и проверить работоспособность сервера. Для этого в директории D:/main/html следует создать файл index.html, поместив в него текст HTML-страницы. Если при наборе в строке запроса браузера адреса http://localhost/index.html отображается содержимое тестовой страницы ——настройка конфигурационного файла прошла успешно.
После настройки основных параметров Apache следует настроить виртуальные хосты, которые обычно расположены в конце файла httpd.conE Если настройка происходит для локальной машины, следует исправить директиву NameVirtualHost следующим образом:
NameVirtualHost 127.0.0.1:80
Далее следует заменить код, который находится между тегами <virtualHost 127.0.0.1:*> на тот, который приведен ниже (в комментариях над параметрами приведены их описания):
Электронный адрес администратора
Serveг Admin webmasterQhost.some_domain.com
Каталог, в котором расположены файлы сервера
DocumentRoot d: /main/first/
Имя сервера (пишите любое имя)
ServerName maks
Алиаоы
ServerAlias localhost
Каталог, в который записываются log-файлы; logs — это подкаталог в
основном каталоге Apache
Ее го г Log logs/inaks-error. log
CustomLog logs/maks-ассезз.Log common
При необходимости вы можете добавить в описание виртуального хоста и другие параметры. Все параметры, которые не описаны, наследуются виртуальным хостом от основного. Таким образом, создан один виртуальный хост. Точно так же можно создать второй, третий и т.工
[ Замечание )
После любых изменений конфигурационного файла http,conf для того, чтобы изменения вступили в силу, необходимо перезапустить сервер Apache.
1.2.2. Установка и настройка PHP 5
Windows
Загрузите с официального сайта PHP (http:〃www.php.net) дистрибутив PHP 5.0.0. На момент написания книги дистрибутив PHP 5.0.0 не содержал мастера установки, позтсму здесь будет описана ручная установка дистрибутива.
Для установки PHP следует создать каталог C:\php м разместить в нем файлы из архива дистрибутива. После этого следует переименовать конфигурационный файл php.ini-dist в php.ini и скопировать его в директорию с системными файлами Windows.
В файле php.ini необходимо установить параметр extension_dir равным той директории, в которой находится дистрибутив PHP:
extens ion^dir = "c:\php”
Параметру doc_root надо передать значение директивы Document Root Web- сервера. Например:
doc_root = "d:\main\httpH
Если необходимо использование расширений PHP, уберите комментарий у строк extension ^ php* *dll
Следующим этапом является настройка уровня обработки ошибок PHP, определяемого параметром error_reporting конфигурационного файла php.ini. Этот параметр определяет типы ошибок, о которых PHP информирует выводом текстового сообщения в окно браузера. Возможные значения этого параметра приведены в табл.】2
24
Часть 厶 Основы разработки приложений на PHP 5
Таблица 7.2 Уровни обработки ошибок PHP
Уровень сшибки
Тип диагностируемой ошибки
E_ERROR
Ошибки обычных функций (критичные ошибки)
E_WARN工NG
Обычные предупреждения (не критичные ошибки)
E_PARSE
Ошибки синтаксического анализатора
E_NOTICE
Замечания (аномалии в коде, возможные источники ошибок — следует отключить при наличии русского текста в коде, так как для интернациональных кодировок не обеспечивается корректная работа)
E_8RE」ERROR
E_CORE_WARN工NG
Ошибки обработчика
Предупреждения обработчика
E_COMPILE_ERROR
Ошибки компилятора
E_COMP 工 LE—VJARN 工 NG
Предупреждения компилятора
E_USER_ERROR
Ошибки пользователей
E_USER_WARNING
Предупреждения пользователей
E_USER_NOTICE
Уведомления пользователей
E_ALL
Все ошибки
Параметр error_reporting позволяет устанавливать несколько уровней, используя побитовые флаги, так, уровень error_reporting E_ALL & ~E_NOT工СЕ
позволяет выводить все ошибки, за исключением замечаний.
В отличие от предыдущих версий, в связи с требованиями безопасности, PHP 5 не выводит ряд ошибок в окно браузера, для того чтобы лишняя информация не попала к потенциальному взломщику. Вся информации об ошибках размещается в log-файлах. Для того чтобы осуществлять отладку Web-приложений с выводом сообщений об ошибках в окно браузера, следует установить параметр display^ errors: display_errors = On
Вывод сообщений об ошибках в log-файлы осуществляется установкой параметра log_errors: log_errors = On
Передача параметрам display_errors и iag_errors значения Off позволяет запретить вывод сообщений об ошибках в окно браузера и log-файл соответственно.
Директива error_log = syslog позволяет регистрировать ошибки в системном журнале Windows, доступном по пути Пуск | Настройка | Панель управления I Адми1шстрированис | Просмотр событий.
В конфигурационном файле Web-сервера hnpd.conf перед блоком описания виртуальных хостов необходимо добавить следующие строки:
MdType appl icat ion./x-htitpd-'php phtml php
<Directory "c:\php">
Options ExecCGI
</Directory>
Action application/x-httpd-php "c:\php\php-cgi.exe"
Первая строка из приведенного выше фрагмента конфигурационного файла сообщает серверу Apache, что со скриптами PHP ассоциированы файлы с расширениями phtml и php. Кроме того, в директории, где установлен PHP (C:/php), разрешается запуск CGI-скриптов. При помощи директивы Action в качестве обработчика PHP-скриптов назначается файл php-cgi.php, расположенный в папке C:\php.
[ Замечание )
Начиная с PHP 5 внешняя программа для обработки PHP-скриптов называется не php. ехет a php-cgi. ехе.
На этом установку PHP можно считать завершенной, изменения вступают в силу после перезапуска сервера Apache.
Для тестирования работы PHP можно создать в корневом каталоге, определяемом директивой DocumentRoot, файл lest.php, в котором следует написать любой небольшой скрипт, например:
<?
echo(“Hell。, PHP!”);
?>
Если установка прошла успешно, то при вводе в браузере запроса http:// localhost/test.php в ответ появится строка Hello, рнр!
〔Замбчан!ле )
Начиная с пятой версии PHP все расширения по умолчанию отключены. Для того чтобы подключить нужное расширение, необходимо убрать комментарий напротив соотвествующей библиотеки расширения в php.ini и перегрузить Web- сервер Apache, Кроме этого, необходимо прописать путь к библиотекам расширений в директиве extensiondir: ex tens ion_di г = "C: /php/ext'1.
Linux
Для установки PHP 5 следует разархивировать файлы дистрибутива:
tar -xzf php-5*x-x.tar.gz
После этого необходимо создать сценарий для настройки PHP, для чего следует создать файл с именем config.sh и поместить в него следующие строки: ,/configures
—with-apxs=/usr/local/араche/bin/apxs\
—wi th-mys ql=/ var/lib/mys ql /
В данном файле следует перечислить все расширения, которые должен поддерживать PHP. В приведенном выше примере вместо параметра вместо —with-apache используется -with-apxs, необходим, ес”н PHP устанавливается как динамически разделяемый объект, а не статический модуль. В этом случае обновление PHP можно проводить, не компилируя Apache заново. Сохраните файл config.sh и выставите права доступа при помощи команды:
cbmod 755 config.sh
Затем следует запустить сценарий: ./config.sh
После этого производим компиляцию и установку PHP, для чего последовательно необходимо выполнить следующие команды:
make
make install
На этом установку PHP можно считать завершенной. Настройка PHP в Linux аналогична настройке в Windows, файл php.ini обычно расположен в /etc.
PHP.INI
В этом разделе рассмотрены основные параметры файла php.ini, содержащего установки PHP. Необходимо учитывать, что директивы файла чувствительны к регистру.
Настройки языка
engine ^ On
Директива разрешает (on) или запрещает (off) работу скриптов на PHP под управлением сервера Apache.
short_open_tag = On
Директива разрешает (on) или запрещает (off) использование коротких тегов <?. В противном случае могут использоваться только теги <?php.
asptags = Off
Директива запрещает (off) или разрешает (on) использование тегов в стиле ASP (<% %>).
Precision = 12
Директива залает число значащих цифр после запятой для чисел с плавающей точкой.
output_buffering = 4096
Директива включает буферизацию вывода с одновременным указанием размера буфера вывода в байтах. Включенная директива (output_buffering = on) позволяет посылать заголовки, в том числе и cookie после вывода текста. В случае, если размер буфера не указан, используется максимально возможный размер буфера. implicit_flush = Off
Действие этой директивы аналогично вызову функции flush (функция, очищающая буфер вывода PHP) после каждого применения функций print или echo и после каждого HTML-блока. Использование указанной директивы серьезно замедляет выполнение скриптов, поэтому по умолчанию она отключена, и использовать ее рекомендуется только в целях отладки.
а 1 low_cal l_t ime_pas s_ref е rence = Off
Эта директива определяет, возможна ли передача аргументов функциям по ссылке при выполнении скрипта. В PHP 5 ло умолчанию отключена. Поскольку в более ранних версиях языка указанная директива по умолчанию находилась во включенном состоянии, при переходе на PHP 5 возможны проблемы с выполнением этих скриптов (будут появляться предупреждения всякий раз, когда аргумент передается ненадлежащим образом).
safe^mode = Off
Директива отключает (off) или включает (on) безопасный режим. safe_mode_allowed_env_vars 二 РНР_
Директива, позволяющая пользователю устанавливать только те переменные окружения, которые начинаются с рнр_. Если эта директива не установлена, то пользователи могут модифицировать любые переменные окружения, что может создать серьезные проблемы в защите сценариев.
safe_mode_protected_env_vars = LD_LIBRARY__PATH
Эта директива содержит список имен переменных окружения, которые пользователь не может изменить, вызвав функцию putanv. Перечисленные переменные будут защиихены даже в том случае, если будет разрешено их использование при помощи соответствующих настроек директивы safe_mode_allowed_env_vars* disable_functions =
Директива позволяет запрещать вызовы некоторых функций и действует независимо от того, установлен безопасный режим или нет. Список запрещаемых функций создается в виде имен функций, которые разделены запятыми.
disable_classes =
Директива позволяет запрещать вызовы некоторых классов и действует независимо от того, установлен безопасный режим или нет. Список запрещаемых классов создается в виде имен классов, которые разделены запятыми, highlight.string = #DD0000 highlight.comment = #FF8000 highlight.keyword = #007700 highlight.bg = #FFFFFF highlight,default = #0000BB highlight.html = #000000
Набор данных директив определяет цвета, используемые для раскраски синтаксиса PHP при вызове функций highlight_string () И highlight_f ile (h
Ограничение по ресурсам
raax__execu t i on_t ime = 30
Директива определяет, что максимальное возможное время выполнения скрипта составляет 30 секунд. max_input__t ime = 60
Директива задает максимальное время, которое скрипт может потратить на обработку запрошенных данных.
memory_limit = 8М
Директива определяет, что максимальный объем памяти, выделяемый одному сценарию, составляет 8 Мбайт.
Обработка ошибок и журнализация
error_reporting =
Директива error_reporting задается в виде битового поля, т. е. ее значение устанавливается с помощью нижеперечисленных констант, объединенных оператором | (or): □ e_all (все предупреждения и ошибки);、
口 E—ERROR (фатальные ошибки во время выполнения);
口 E—Warntng (некритические ошибки — предупреждения — во время выполнения);
E PARSE (ошибки трансляции);
口 e_notice (замечания во время выполнения, которые сообщают о возможных логических ошибках в скрипте; к этому типу ошибок относятся, к примеру, предупреждения об использовании неинициализированных переменных);
口 e_core_error (фатальные ошибки при старте PHP);
口 e_core_warni:ng (предупреждения при старте PHP);
e_compile_error (фатальные ошибки при компиляции);
е_сомр I le__warn I ng (предупреждения при компиляции);
口 e_user_error (ошибки, вызванные действиями пользователя);
e_dser_wa№ing (предупреждения, вызванные действиями пользователя); 口 E—USER—NOT工се (замечания, вызванные действиями пользователя).
Таким образом, для того, чтобы отображались все ошибки, за исключением замечаний о возможных логических ошибках в сценарии, нужно установить днректмву error^reporting следующим образом: error_reporting = Е_ALL & ~E_NOTICE
Для того чтобы показывать только ошибкиt директива должна быть на* строена так, как показано ниже:
error_reporting = E_COMPILE_ERROR I E_ERROR | E_CORE_ERROR display_errors = Off
Директива запрещает вывод ошибок и предупреждений в браузер. При отладке приложений следует включить эту директиву, в реальной же работе лучше ее отключить (в PHP 5 по умолчанию она отключена), поскольку при включенной dispiay_errors посетитель может получить доступ к секретной информации. Вместо этой директивы можно использовать журнализацию. display_startup_errors ^ Off
Директива отключает (off) или разрешает (on) отображение ошибок, вознн” кающих при старте PHP. loggerгorз = Off
Директива отключает (off) млн разрешает (on) сохранение сообщений об ошибках в файле журнала. log_errors_max_len = 1024
Директива задает максимальную длину файла журнала.
track_errors = Off
Директива отключает (off) или включает (on) сохранение сообщения о последней ошибке в переменной $php_errormsg html_errors = Off
Директива отключает (off) или включает (on) вывод возможных сообщений об ошибках в HTML.
error__log = filename
Данная директива позволяет вести журнал в файле filename.
Обработка данных
Замечание
Директива track_vars всегда включена начиная с версии PHP 4,0.3.
variables_order = "EGPCS”
Эта директива определяет порядок регистрации переменных get, post, cookie, Environment и серверных переменных (соответственно g, р, с, е и s). Регистрация осуществляется путем чтения этой строки слева направо. По умолчанию в PHP 5 директиве присвоено значение gpcs.
( Замечание )
Если установить директиве variables_order значение GP, то регистрация переменных будет проводиться только для переменных, передаваемых методами POST и get. При этом все переменные, передаваемые через cookie, а так же серверные переменные и переменные окружения будут /「нормроватьсн.
register_globals = Off
Данная директива отключает (off) или включает (on) возможность регистрации ЕСРСЗ^переменных как глобальных переменных. Использование глобальных переменных может создать кдырын в защите сценария, поэтому по умолчанию эта директива отключена.
I Замечание )
Директива register_globals является одним из самых дискуссионных моментов в языке PHP. С одной стороны, ее использование действительно может породить реальные проблемы с защитой РНР-сценариее при ненадлежащем учете возможных ошибочных ситуаций, и многие разработчики справедливо отмечают, что написание скриптов без использования глобальных переменных на 90% уменьшает уязвимость скриптов к различного рода атакам. С другой стороны, на заре возникновения PHP не одна тысяча пользователей доверилась разработчикам языка (до версии PHP 4.3 эта директива была по умолчанию включена), в силу чего в настоящее время имеются миллионы функционирующих скриптов, написанных с использованием глобальных переменных. (Стоит отметить, что в обучающих целях иногда совершенно нелишне писать скрипты с использованием глобальных переменных, поскольку их замена на суперглобальные массивы сильно ухудшает читабельность кода.)
Замечание )
В настоящий момент у всех хост-провайдеров эта директива включена и. по- видимому, будет оставаться включенной еще долгое время, поскольку в противном случае может нарушиться преемственность кода. Большинство примеров в этой книге написано с учетом, что указанная директива отключена.
register_long_arrays = Off
Директива, разрешающая (on) или запрещающая (off) использование для передачи переменных из форм длинных массивов вида $нттp_get_vars, $http_post_vars и т. д. Если данная директива отключена, следует пользоваться новыми суперглобальными массивами вида §_get, $_post и т. д.
С Замечание )
Выставленное по умолчанию значение (Off) для этой директивы, является весьма спорным, поскольку реальных опасений с точки зрения защиты при включенной директиве register_long_arrays возникать не должно. Многие же разработчики предпочитают в силу привычки использовать суперглобальные массивы вида $http_get_vars вместо 帛一get. Если же указанная директива выключена, естественно, такой код перестает быть рабочим.
pos t^nax_s i ze = 8M
Директива определяет максимальный размер данных, преданных методом POST.
mag i c_quo t es__gpc = Off
Данная директива позволяет отключить (Off) или включить (on) автоматическая обработку магических кавычек и апострофов, в данных get, post, и Cookie.
magi c_quo t e s_r unt ime = Off
Директива позволяет отключить (off) или включить (on) возможность выполнения заключенных в магические кавычки команд ('something'). auto_prepend_f ile = auto__append_f i le =
Содержимое файлов, указанных в этих директивах» PHP должен обрабатывать соответственно до и после выполнения сценария.
default^jnimetype = “text/html”
Эта директива указывает, какую информацию PHP сообщает браузеру об используемой кодировке в заголовке Content-type. По умолчанию используется text/hbni без указания кодировки.
Пути и директории
;UNIX: "/pathl:/path2"
;include_j>ath = ".: /php/includes"
;Windows: 11 \pathl; \path2 "
;include^path = ".;c:\php\includesM
Пути, в которых находятся PHP-сценарии.
doc_root =
Директива задает корневой каталог для РНР^сценариев. extension_dir =
Директива задает каталог, в котором размещаются динамически загружаемые расширения.
enable_dl = On
Директива, разрешающая (on) или запрещающая (off) использование функции dl.
( Замечание )
Функция dl предназначена для загрузки расширений PHP, к примеру, таких как SQLLite. Указанная функция неправильно работает в многопоточных Web- серверах (IIS, Zeus) и для них автоматически отключается.
Загрузка файлов
file_uploads = On
Директива, разрешающая (on) или запрещающая (off) загрузку файлов на сервер.
up1oad_tmp_di г =
Директива устанавливает временную директорию для загружаемых файлов. upload_max_filesize = 2M
Директива, задающая максимальный размер загруженных файлов.
Работа с сокетами
а 1 low_ur l__f open = On
Директива, разрешающая (on) или запрещающая (off) использование URL (http://, ftp://) как файлов.
!Замеч.нме )
Указанная директива обязательно должна быть включена при работе с сокетами.
from=j ohnQdoe * com
Директива, задающая анонимный адрес электронной почты. user_agent«"PHP"
Директива, задающая значение переменной user_agent, предоставляемой сервером при подключении с использованием сокета.
de f ault_socket_t imeout = 60
Директива, задающая максимальное время прослушивания сокета, в секундах.
Динамические расширения
Для автоматической загрузки динамических расширений директиву extension необходимо задать следующим образом:
ex t en s ion^modul ename • extension
К примеру, для Windows можно написать так: extension=msql.dll
А Ш1Я UN IX так: extens ion^^nsql. so
Замечание )
Здесь задается только имя, без указания пути. Если необходимо указать путь, следует использовать вышеописанную директиву extension_dir.
Установки для модулей
Модуль Syslog
define_syslog_variables = Off
Директива, определяющая, следует (on) или нет (off) использовать различные переменные демона syslog, такие как $log р工d, $log_cron и т. д. Для ускорения работы данную директиву рекомендуется отключать, а при необходимости использовать функцию define syslog_variables.
Модуль mail function
SMTP = localhost
Эта директива предназначена только для использования в среде Windows и задает используемый SMTP-сервер..
sendmai l_f rom = admin@ localhost
Эта директива предназначена только для использования в среде Windows и задает поле From по умолчанию.
sendmai1_раth = \usr\sbin\sendmail -t -i
Данная директива используется только в среде UNIX it задает путь и аргументы демона sendmail.
Модуль Debugger
debugger.enabled = On
debugger.profiler_enabled = On
Директивы, отвечающие за включение (on) или отключение (off) отладчика и профилировщика.
Модуль Java
java,class.path = .\php_java.jar j ava.home = c:\jdk
java.library = c:\jdk\j re\bin\hotspot\jvm.dll
java.library.path = .\ Пути и каталоги для Java, uodbc.allow^persistent - On Директива разрешает (on) или запрещает (off) постоянные ODBC- соединений.
uodbc.check_persistent = On
Директива включает (on) или отключает (off) проверку доступности ODBC- соединения перед его использованием, uodbc.max_persistent = ~1
Директива, определяющая максимальное число постоянных ODBC-соединений. Значение -1 означает, что ограничений нет.
uodbc .шах__1 inks = -1
Директива, определяющая максимальное число как постоянных, так и непостоянных ODBC-соединений. Значение -1 означает, что ограничений нет. uodbc.defaultlrl = 4096
Длина для LONG-полей.
uodbc.defaultbinmode = 1
Директива, устанавливающая параметры для работы с бинарными данными. Значение о означает режим passthru, 1 — режим as is, 2 — режим преобразования в символы.
Модуль MySQL
mysql. allowjersistent = On
Директива разрешает (on) или запрещает (off) постоянные MySQL- соединений.
mysql < ma x__per sis tent = -1
Директива, определяющая максимальное число постоянных MySQL- соединений. Значение -1 означает, что ограничений нет* mysql.max_links = -1
Директива, определяющая максимальное число как постоянных, так и непостоянных MySQL-соединений. Значение -1 означает, что ограничений нет. mysql.default^port =
Директива, определяющая порт, заданный по умолчанию для функции mysqi_connect. При незаданном значении функция mysql_connect исполь-
зует переменную $mysql_tcp_pqrt или запись mysqi-tcp в каталоге /etc/services. а затем заданную во время компиляции константу mysql_port (именно в таком порядке).
:Замечание )
При работе в среде Windows используется только константа mysql_port.
mysql* default_socket =
Директива, определяющая имя сокета для локальных соединений MySQL. При незаданном значении используется значение по умолчанию, mysql.defauljhost =
Директива, задающая имя хоста по умолчанию для функции mysql_connect().
mymql. default_user =
Директива задающая имя пользователя базы данных по умолчанию. Это значение используется при установлении соединения с сервером базы данных, если имя пользователя не передано в качестве аргумента функции mysq工一8rmect ().
mysql. default_password =
Директива, задающая пароль пользователя базы данных по умолчанию. Это значение используется при установлении соединения с сервером базы данных, если имя пользователя не передано в качестве аргумента функции mysql_connect().
( Замечание )
Хранить пароль в файле настроек PHP вряд ли стоит, поскольку любой пользователь, имеющий права на запуск PHP, может узнать пароль путем выполнения сценария echo cfg_get__var ("mysql. default_passwordH).
[3 aMeманне)
В безопасном режиме три последние директивы не работают.
Далее перечислены директивы файла php.ini для других СУБД, полностью аналогичные рассмотренным.
Модуль bcmath
bcmath.scale = О
Директива, определяющая число десятичных цифр для всех bcmath-функций (математические функции произвольной точности).
Модуль Session
session.save_h^ndler = files
Директива, определяющая, каким образом хранить данные сессий (по умолчанию —в файлах), session.save_path = /trap
Если в предыдущей директиве указан режим хранения данных в файлах, то в этой директиве указывается каталог, в который помещаются файлы сессий.
session.use_cookies = 1
Директива, определяющая, можно ли использовать Cookies (по умолчанию использование Cookies разрешено).
session.name = PHPSESS工D
Директива, задающая имя сессии по умолчанию.
session. autorestart = О
Директива, разрешающая или запрещающая инициализацию сессии при старте (по умолчанию отключена).
session. cookie_lifetime = О
Определяет время жизни Cookie по умолчанию. Если директива принимает значение 0, то срок жизни Cookie длится до закрытия браузера.
session.cookie_path = //
Директива, задающая путь, по которому сохраняются идентификаторы Cookie.
session * cookie_domain =
Определяет домен для Cookie (параметр по умолчанию для функции setcookie).
session.serial!ze^handler = php
Функция, используемая для сериализации данных. Значение php задает стандартную функцию.
session.gc^probability = 1
Директива, определяющая вероятность того, что при очередном запуске сценария, работающего с сессиями» будет вызвана функция "сборки мусора1' для очистки сессий, уже не применяемых пользователем.
session.gc_maxlifetime - 1440
Директива задает время в секундах, после которого сохраненные данные будут автоматически удалены сборщиком мусора.
session,referer_check =
Директива, определяющая, нужно ли проверять нттр-Referer на предмет того, не является ли идентификатор сессии фальшивым.
session.cache_limiter = nocache
Директива, устанааливающая одно из значений кэширования HTTR Может принимать следующие значения: nocache, private, public.
i Замечание )
Более подробно значение этих директив рассмотрено в главе 3.
session.cache_expire = 180
Директива, задающая количество мннут, по истечении которого документ будет считаться устаревшим. По истечении данного времени документ не может использоваться при кэшировании, и требуется его повторная загрузка из источника.
session * use_t rans_sid = 1
Директива, определяющая, нужно ли использовать поддержку так называе- мьтх переходящих SID (идентификаторов сессии). Иначе говоря, необходимо ли поддерживать сессии, если пользователь отключил Cookies в браузере.
:Замечание )
Указанная директива работает только в том случае, если PHP был скомпилирован с включенной опцией ^-enable-trans-sid.
session.hash_f unction = 0
Директива, определяющая выбор хеш-функций для сессий. Значение 0 соответствует функции MD5, значение 1 — функции sha-i.
Модуль Assertion
assert.active = On
Директива, определяющая, работает ли функция проверки assert О, выясняющая, не являются ли некоторые утверждения ложными.
assert.warning = On
Директива, определяющая, нужно ли генерировать предупреждения для каждой неудавшейся проверки выражений функцией assert().
assert .bail = Off
Директива, определяющая, следует ли завершать программу (on) или нет (off) в случае неудачного выполнения функции assert (). assert«callback = 0
Директива, определяющая пользовательскую функцию, которая будет вызвана при неудавшейся проверке функцией assert (). По умолчанию ——значение равно нулю, т. е. пользовательские функции не применяются.
Модуль Verisign Payflow Pro (параметры прокси-сервера) pfpro.defaulthost = "test * signio.com"
Директива, задающая URL-адрес прокси-сервера, pfpro,defaultport =443
Директива задает порт по умолчанию для прокси-сервера, pfpro.defaulttimeout = 30
Директива задает тайм-аут для прокси-сервера, pfpro.proxyaddress 二 IP-адрес прокси-сервера по умолчанию.
pfpro.proxyport =
Порт прокси-сервера по умолчанию.
pfpro.proxylogon 工
Логин для прокси-сервера, pfpro, proxypassword = Пароль для прокси-сервера.
Модуль Sockets
sockets. use_syston_read = On
Директива, разрешающая использование функции mad при работе с сокетами.
Глава 2
Работа с MySQL
В настоящее время ни одно серьезное Web-приложение не может обойтись без работы с базой данных, обеспечивающей почти безграничное манипулирование данными: сортировку, поиск, преобразование, редактирование и многое другое. При этом все низкоуровневые операции с файловой системой скрыты от программиста за несложными SQL-запросами. Работа с базой данных имеет как преимущества, так и недостатки. К достоинствам можно отнести значительное сокращение кода (иногда в 2—3 раза) по сравнению с файловыми вариантами Web-приложений, что сокращает время разработки и упрощает процесс отладки. К недостаткам можно отнести зависимость приложения не только от работоспособности Web-сервера, но и от работоспособности сервера базы данных. Разумеется, вероятность того, что один из двух серверов может выйти из строя выше по сравнению с тем, если бы работа Web-приложения зависела только от одного сервера. Кроме того, непосредственная работа с файловой системой осуществляется быстрее по сравнению со случаем, когда в качестве посредника для работы с ней выбирается СУБД. Так, поисковая система Google, известная высокой скоростью обработки запросов, основана на модели плоских файлов и не использует базы данных. С другой стороны, PHP как интерпретируемый язык не может достичь производительности баз данных, реализованных на С.
В последнее время СУБД MySQL стала стандартом де-факто благодаря своей производительности, низкой стоимости, простоте установки, настройки и обслуживания.
Поскольку MySQL будет использоваться в качестве СУБД на протяжении всей книги, эта глава целиком посвящена приемам работы с MySQL.
Установка и настройка MySQL
Дистрибутив MySQL включает в свой состав серверную и клиентскую части, а также ряд вспомогательных утилит и документацию на английском языке.
I Замечание )
Полное справочное руководство на русском языке можно найти на официаль' ном сайте MySQL по адресу http://dev.mysql.corn/doc/niysql/ru/tndex.htiTil.
В данном разделе расематривается установка MySQL для платформ Windows и Linux. При возникновении проблем с установкой необходимо обратиться к официальной документации.
Установка MySQL на платформу Windows
Для установки MySQL имеются два типа дисфибутивов:
бинарный дистрибутив, содержащий в себе программу установки;
Q дистрибутив исходного кода.
Мы будем устанавливать MySQL из бинарного дистрибутива, загрузить который вы можете с сайта http://www*mysql.com/downloads/.
При работе в Windows NT/2000/XP войдите в систему с привилегиями администратора. Разархивируйте файл дистрибутива во временный каталог, после чего запустите файл setup.exe. Далее необходимо следовать всем пунктам мастера установки. По умолчанию MySQL будет установлен в каталог C:/mysql.
Теперь, после завершения установки, переходим к настройке конфигурации. Начиная с версии MySQL 3.23;38 дистрибугив для Windows включает в себя, кроме обычного бинарного кода, бинарный код сервера MySQL-Max. Ниже приводится список различных серверов MySQL, которые можно использовать:
口 mysqid (сервер скомпилирован с полным набором возможностей отладки н автоматической проверки выделения памяти, включена поддержка транзакционных таблиц);
口 mysqld-opt (оптимизированный бинарный код без поддержки транзакционных таблиц);
mysqld-nt (оптимизированный бинарный код для Windows NT/2000/XP с поддержкой именованных каналов);
口 mysgid-max (оптимизированный бинарный код с поддержкой символических ссылок и транзакционных таблиц);
口 mysqld-max-nt (ТО же самое, ЧТО И mysqld-max, ТОЛЬКО С поддержкой именованных каналов).
Редактирование файла настроек my.ini необходимо в следующих случаях:
口 если каталоги установки или данных отличаются от заданных по умолчанию местоположений (C:/mysql и C:/mysql/data);
口 если вы хотте использовать серверы mysqld, mysqlci-max или mysq丄d- max-nt;
если требуется выполнить настройку параметров сервера.
Дл月 настройки конфигурации сервера MySQL можно пользоваться как графической утилитой WinMySQLAdmin, которая входит в состав дистрибутива (\mysqi\bin\ winmysqladmin. ехе), так и ручной правкой конфигурационных файлов.
WinMySQLAdmin позволяет задавать пароль пользователя, просматривать статистическую информацию о сервере и размещенных на нем баз данных. Кроме этого, данная утилита позволяет запускать и приостанавливать сервер mysqld.
После запуска WinMySQLAdmin в системном триере появляется значок в виде светофора, контекстное меню которого позволяет запускать и останав* ливать сервер MySQL, а сигнал ^светофора" отображает состояние сервера: зеленый цвет, если сервер запушен, и красный — если приостановлен (рис. 2.1).
Рис. 2.1. Контекстное меню утилиты WinMySQLAdmin
При работе в среде Windows NT/2000/XP можно установить MySQL в качестве сервиса, что обеспечит запуск сервера mysqid при старте системы. Для этого в контекстном меню WinMySQLAdmin необходимо выбрать пункт Install the Service,
Без использования WinMySQLAdmin настроить СУБД MySQL можно, редактируя конфигурационный файл my.cnf или my.ini.
( Замечание )
Во избежание недоразумений используйте только один из файлов: my.cnf или myini, так как MySQL сначала ищет файл my.ini, а затем my.cnf,
〔 Замечание )
Утилита WinMySQLAdmin использует только файл my.ini.
Отредактируйте раздел [mysqid], указав значения параметров basedir и datadirl
[mysqld]
set basedir to installation path, e.g*, c:/mysql basedir = the_ins tall_j»ath
set datadir to location of data directory, # e.g., c:/mysql/data or d:/mydata/data datadir = the_dat a_path
Запуск сервера следует проводить из командной строки:
С:\mysql\bin> mysqld-raax --standalone
Остановить сервер можно при помощи команды:
С:\mysql\bin> mysqladmin -u root shutdown
Установить сервер mysqld в качестве сервиса можно при помощи команды:
С: \mysql\bin> mysqId-max-nt —install
Для работы с сервером необходимо в командной строке запустить клиента MySQL —mysql.
[ Замечание )
Графический Web-интерфейс для работы с MySQL (phpMyAdmin) будет описан в разд. 24 данной главы.
При подключении к серверу с помощью клиентской программы mysql нужно ввести имя пользователя MySQL и пароль. Если сервер запущен на удаленном компьютере, необходимо также указать имя хоста. Для соединения с сервером введите команду mysql -h host -u user -p
После этого введите пароль, когда на экране появится комавда "Enter password".
Если установка MySQL прошла успешно, то на экране должна появиться метка командной строки mysql:
mysql>
Появление этой метки означает, что программа mysql готова к вводу команд.
При некоторых вариантах установки MySQL возможно подсоединение к серверу без ввода имени пользователя. В зтом случае подсоединиться к серверу можно, просто набрав команду mysql.
Выйти из программа mysql можно, набрав в командной строке mysql> команду quit:
mysql> QUIT
:Замечание )
Начиная с пятой версии PHP все расширения по умолчанию отключены, в том числе и расширение для работы с MySQL. Для того чтобы его подключить, необходимо убрать комментарий напротив строки extension=php__mysql. dll в php.ini и перезагрузить Web-сервер Apache. Кроме этого, необходимо прописать путь к библиотекам расширений в директиве extension_dir: extension_dir = МС: /php/ext**,
Установка MySQL на платформу Linux
Большинство дистрибутивов Linux содержат в своем составе СУБД MySQL В данном разделе будет рассмотрена установка сервера и клиента применительно к Red Hat Linux (Fedora Core).
Для установки СУБД MySQL на сайте MySQL http://www.mysql.com/ downloads/ доступны следующие RPM-файлы:
MySQL-server-VERSiON .1386. rpm (сервер MySQL);
MySQL-ciient-vERSiON. 1386.rpm (стандартньге клиентские программы MySQL; установка этого пакета требуется всегда);
口 MySQL-bench-version . 1386. rpm (тесты и контрольные задачи; требуется наличие модулей Perl И msql-mysql-modules);
MySQL-devei-VERSiON. i386. rpm (библиотеки и включаемые файлы, необходимые для компилирования других клиентов MySQL);
MySQL-shared-VERSioN,138 6, rpm (этот пакет содержит динамические библиотеки libmysqlclient. so*, которые нужны для некоторых языков программнровання, чтобы работать с MySQL);
口 MySQL-vers ion . s г с. rpm (этот пакет содержит исходные коды для всех предыдущих пакетов).
Чтобы просмотреть все файлы в пакете RPM, выполните команду
rpm -qpl MySQL-VERSION.i386.rpm
Для выполнения стандартной установки выполните команду, предварительно войдя в систему с правами root:
rpm -ihv MySQL-serv曰r-VERSI6J.1386.rpm MySQL-client-VERSION.1386. rpm
RPM помещает данные в каталог /var/lib/mysql/ и создает соответствующие вхождения в каталоге /etc/init.d/ для а втомати чес ко го запуска сервера при загрузке.
При обновлении уже существующей версии MySQL можно воспользоваться командой:
rpm -Uhv MySQL-server-VERSION.1386. rpm MySQL-client-VERSION. 1386. rpm
Замечание )
Перед обновлением существующей версии MySQL как на платформ© Windows, так и Linux необходимо остановить работу сервера MySQL mysqld.
После установки RPM-пакетов вы можете сразу приступать к использованию MySQL.
Если в системе уже установлена СУБД MySQL, но демон mysqld не запущен, необходимо выставить его автоматический запуск при старте системы, например, при помощи программы ntsysv, позволяющей задать набор демонов, запуск которых необходимо обеспечить.
Оптимизация SQL-запросов
В данном разделе будут рассмотрены способы оптимизации SQL-занросов и таблиц базы данных MySQL. Для создания эффективных SQL-запросов, а следовательно, в дальнейшем и эффективного кода с использованием PHP, необходимо хорошо понимать тонкости работы с СУБД MySQL, типы данных и характеристики таблиц, с которыми она позволяет работать. Несмотря на то, что для работы с СУБД используется унифицированный язык запросов SQL, все СУБД поддерживают его с определенными оговорками. Говоря об SQL применительно к какой-либо базе данных, следует иметь в виду, что речь идет о диалекте SQL, поддерживаемом данной СУБД.
Замечанье )
Такая ситуация связана с тем, что стандарт языка SQL был принят достаточно поздно, когда большинство современных баз данных уже широко использовались. Для того чтобы обеспечить преемственность старых версий баз данных, поставщикам баз данных приходится проектировать их. сохраняя поддержку оригинальных языков запросов, применявшихся до введения стандарта SQL. Кроме того, СУБД постоянно развиваются, приобретая с каждой новой версией новые особенности и операторы для работы с данными.
Как любая реляционная СУБД, MySQL оперирует таблицами, состоящими из одного или более столбцов и некоторого значения строк (записей), которое может быть и нулевым. Тип данных определяется столбцами таблицы.
Типы данных, применяемые в таблицах MySQL, условно можно поделить на три группы: числовые данные, строковые данные и календарные данные. Особняком среди них стоот так называемое "пустое знаненне"—— null, не являющееся типом и обозначающее отсутствие каких бы то ни было данных.
Замечание
Далее в главе необязательные элементы синтаксиса будут указываться в квадратных скобках ([)). Например, синтаксис 工NT[ (М) ] означает, что максимальная ширина отображения столбца+ обозначенная как [ (М) ], может не указываться.
Числовые данные
Числовые данные делятся на целочисленные и вещественные. Основные типы числовых данных, занимаемый ими объем памяти и характеристики приведены в табл.
Таблица 2.1. Числовые типы
Тип
ОбъемДиапазон
памяти
TINYINTt(М)]
1 байтот-128 до 127 (от 一2了 до 2『-1)
от 0 до 255 (от 0 до 2в-1)
SMALLINT[(М)]
2 байтаот -32 768 до 32767 (от -21S до215-1)
от 0 до 65 535 (от 0 до 21в-1)
MEDIUMINT[(М)]
3 байтаот -8 388 608 до 8 388 608 (от -2гз до 223-1)
от 0 до 16 777 215 (от 0 ДО 2г4-1)
INT[(М)]
4 байтаот -2 147 683 648 до 2 147 683 648 (ст -231 до 23,-1)
от 0 до 4 294 967 295 (от 0 до 2зг-1)
BlGINTf(М)]
8 байтот -2вз до 2М^1
от 0 до 2网
FLOAT[(M,D)]
4 байтаМинимальное значение +/-1.175494351Е—39
Максимальное значение +/-3.402823466Е+38
DOUBLE[(M,D)】
8 байтМинимальное значение
+/-2.2250738585072014Е-308
Максимальное значение +/-1.797693134862315Е+30 8
DECIMAL[fM,D)]
М + 2 байта Переменное; зависит от параметров М и D
Как видно из табл. 2J, СУБД MySQL имеет пять целых типов: т工ny工nt, small工nt, mediumint,工nt, в工gint. Различие между ними заключается в диапазоне величин, которые можно хранить в столбцах такого типа. Чем больше диапазон значений у типа данных, тем больше памяти для него требуется.
Тип П5Т имеет синоним integer.
Целые типы данных могут быть объявлены как положительные* Для этого после объявления типа следует разместить ключевое слово unsigned. В этом случае элементам данного столбца нельзя будет присвоить отрицательные значения.
При объявлении целого типа задается длина отображения М (от 1 до 255). Данный параметр определяет количество символов, которые будут выводиться при отображении значений столбца.
[ Замечание )
Параметр М определяет только количество цифр, которое будет выводиться при запросе в клиентах типа mysql, и не влияет на размер памяти, отводимый для хранения значения.
Для представления вещественных типов в СУБД MySQL имеются три типа: FLOAT, DOUBLE И DECIMAL.
I Замечание
Тип double имеет два синонима: precision и real. Тип decimal имеет см- НОНИМ NUMERIC.
Диапазон вещественных чисел, помимо максимального значения, имеет минимальное значение, которое характеризует точность данного типа. Параметр М в табл. 2.1 задает число символов для отображения всего числа, а D —— для его дробной части.
При выборе столбцов для формирования структуры таблицы необходимо обращать внимание на размер, занимаемый тем или иным типом данных: если значения, размещаемые в базе данных, никогда не будут выходить за пределы 100, не следует выбирать тип больше tinyint. Если же в полях столбца предполагается хранить только целочисленные данные, то применение атрибута unsigned позволит увеличить диапазон в два раза.
Строковые данные
Строковые типы данных, максимальный размер и требования к памяти приведены в табл, 2.2. В таблице L — это длина хранимой в ячейке строки, а байты, приплюсованные к L,——накладные расходы для хранения длины строки.
Таблица 22 Строковые типы.
Тип
Объем памяти
Максимальный размер
CHAR (И)
M байт
М байт
VARCHAR(М)
L+1 байт
М байт
TINYBLOB, Т工NYTEXT
L+1 байт
2а-1 байт
BLOB, TEXT
L+2 байта
2,а-1 байт
MEDIUMBLOB, MEDIUMTEXT
L+3 байта
2"-1 байт
LONGBLOB, LONGTEXT
L+4 байт
2堂_1 байт
ENUM(^valuel1,'valueZ*, .* .)
1 или 2 байта
65 535 элементов
SET(『valuel','value2‘,・..)
1, 2, 3( 4 или 8 байт
64 элемента
Тип char позволяет хранить строку фиксированной длины М, его дополняет тип varchar, позволяющий хранить переменные строки длиной L, Значение М может принимать значения от I до 255.
При выборе строкового типа данных для столбца следует принимать во внимание, что для переменных строк varchar требуется только число байт, равное длине строки плюс один байт, а тип char(m)независимо от длины строки использует для ее хранения все М байт. В то же время тип char обрабатывается эффективнее переменных типов.
Прн создании таблицы нельзя смешивать столбцы типа char и varchar. Если такое происходит, СУБД MySQL изменяет тип столбцов согласно правилу: если в таблице присутствует хоть один столбец переменной длины, все столбцы типа char приводятся к типу VARCHAR.
Типы blob и text в СУБД MySQL во всем аналогичны и отличаются только в деталях. Например, при выполнении операций над столбцами типа text учитывается кодировка, а над столбцами типа blob — нет.
Тип text обычно используется для хранения больших объемов текста, в то время как blob — для больших двоичных объектов, таких как электронные документы, изображения, звуки и т. д.
К особым типам данных относятся enum и set. Строки этих типов принимают значения из заранее заданного списка допустимых значений. Основное различие между ними заключается в том, что значение типа enum должно содержать точно одно значение из указанного множества, столбцы set могут содержать любой (или все) элемент заранее заданного множества одновременно. Так, значения для столбца, объявленного как enum('у', 'п‘},
могут принимать только два значения: либо ‘у’, либо 『п‘・ Данный тип удобно использовать для хранения значений переключателей из HTML- формы. Для типа set, как н для типа enum, при объявлении задается список возможных значений, но ячейка может принимать любое значение из списка, пустая строка означает, что ни один из элементов списка не выбран. Этот тип данных удобно использовать при сохранении выбранных посетителем в HTML-форме флажков.
Типы enum и set можно назвать строковыми лишь отчасти, так как при объявлении они задаются списком строк. Но во внутреннем представлении базы данных элементы множества сохраняются в виде чисел. Элементы типа enum нумеруются последовательно начиная с 1. В зависимости от числа элементов в списке под столбец может отводиться I байт (до 256 элементов в списке) или 2 байта (от 257 до 65 536 элементов в списке).
Элементы множества set обрабатываются как биты, размер типа при этом также определяется числом элементов в списке: 1 байт (от 1 до 8 элементов), 2 байта (от 9 до 16 элементов), 3 байта (от 17 до 24 элементов), 4 байта (от 25 до 32 элементов) и 8 байт (от 33 до 64 элементов).
Календарные данные
СУБД MySQL имеет 5 столбцов для хранения календарных типов данных: DATE, DATETIME, TIME, TIMESTAMP И YEAR(Т&6л. 4.3).
Таблица 23. Календарные типы
Тип
Объем памяти
Диапазон
DATE
3 байта
от *1000-01-ОГ до *9999-12-31'
TIME
3 байта
от '-828:59:59' до '828:59:59'
DATAT 工 ME
8 байт
от '1000-01-01 00:00:00’ до '9999-12-31
00:00:00*
TIMESTAMP [ (M)]
4 байта
от 19700101000000 до неопределенной даты в 2037 году
YEARl (M)]
1 байт
от 1901 ДО 2155 ДЛЯ YEAH (4 )
от 1970 до 2069 для YEAH (2)
Выбор типа данных
При выборе типа данных следует помнить, что обработка числовых данных происходит быстрее строковых. Так как типы данных enum и set имеют
внутреннее числовое представление, им следует отдавать предпочтение перед другими видами строковых данных, если предоставляется такая возможность.
Производительность можно увеличить за счет представления строк в виде чисел. Примером может служить преобразование IP-адреса из строковой нотации в 工nt(4). Это позволит уменьшить размер таблицы и значительно увеличит скорость при сортировке н выборке данных, но потребует дополнительных преобразований.
Не следует забывать, что базы данных хранятся на жестком диске, и чем меньше места они занимают, тем быстрее происходят поиск и извлечение. Поэтому там, где есть возможность, предпочтение следует отдавать типам данных, занимающим меньше места.
Типы фиксированной длины обрабатываются быстрее типов переменной длины, так как в последнем случае при частых удалениях и модификациях таблицы происходит фрагментация таблицы.
:Замечание )
Если применение столбцов с данными переменной длины неизбежно, для дефрагментации таблицы следует применять команду optimize table.
Таблицы MySQL
СУБД MySQL в настоящее время поддерживает несколько видов таблиц: ISAM, MylSAM, MERGE, HEAP, BDB И InnoDB.
Изначально СУБД MySQL планировалась как одна из быстрых баз данных и поддерживала единственный тип таблицы — isam, в котором применяется индексно-последовательный метод доступа. Для ускорения выполнения запросов дескриптор ISAM не поддерживает транзакции и ряд операторов SQL Тип таблицы ISAM сейчас является устаревшим. Он применялся в ранних версиях MySQL, использование его нежелательно, так как в будущем его поддержка может будет удалена. Этот вид таблиц был заменен на новый —— Муisam, являющийся стандартным типом по умолчанию. Основные пре^ имущества по сравнению с таблицами типа isam заключаются в следующем: □ поддерживается больший размер таблиц;
содержимое таблиц хранится в формате, не зависящем от платформы;
более эффективно работает с индексами и атрибутом auto_increment;
более эффективно организована проверка целостности таблицы;
поддерживается полнотекстовый поиск с использованием индекса FULLTEXT.
Таблицы merge предназначены для объединения нескольких таблиц MyiSAM в одну* Причем с помощью одного запроса можно обращаться ко всем таблицам, входящим в состав единой таблицы. Главное преимущество такого объединения в том, что итоговая таблица может превышать предельный размер, установленный для таблиц MyiSAM.
Таблицы heap —- это временные таблицы» предназначенные для хранения в оперативной памяти. Для повышения эффективности в них применяются только строки фиксированной длины.
Таблицы типа bdb поддерживаются дескриптором Berkeley db, разработанным компанией Sleepycat. Дескриптор bdb обеспечивает:
口 обработку таблиц с использованием транзакций;
автоматическое восстановление после сбоев;
口 блокирование на уровне страниц, обеспечивающее хорошую производительность при обработке параллельных запросов.
Таблицы типа innoDB являются самыми новыми таблицами, недавно добавленными в СУБД MySQL Этот тнп таблиц поддерживается дескриптором innoDB, который был разработан компанией Innobase Оу. Он обеспечивает следующие возможности:
обработка таблиц с использованием транзакций;
автоматическое восстановление после сбоев;
口 поддержка ключей, в том числе каскадного удаления;
блокирование на уровне строк, обеспечивающее хорошую производительность при обработке параллельных запросов.
Таблицы могут быть распределены по нескольким файлам или даже разделам, что позволяет выходить за пределы, установленные файловой системой.
.26 Структурированный язык запросов (SQL)
Структурированный язык запросов SQL позволяет производить различные операции с базами данных: создавать таблицы, помещать, обновлять н удалять из них данные, производить запросы из таблиц и т. д* Далее мы последовательно рассмотрим все эти операторы.
:Замечание )
Несмотря на то, что последний стандарт SQL принят в 1992 гм на сегодняшний день нет ни одной базы данных, где бы он полностью выполнялся. Более того, в различных базах данных часть операций осуществляется по-разному. Мы бу-
дем придерживаться диалекта SQLt характерного для СУБД MySQL, поэтому не все запросы могут выполняться для других баз данных.
Далее в главе будут рассмотрены основные операторы SQL
CREATE DATABASE
Эта команда создает новую базу данных:
CREATE DATABASE [IF NOT EXISTS] db_name;
Здесь db__name является именем создаваемой базы данных. Для того чтобы создать новую базу данных forum, наберите в строке-приглашении клиента mysqi эту команду и укажите название базы данных (листинг 2.1).
Листинг 2.1 Создание базы данных
mysql> CREATE DATABASE forum;
( Замечание )
Каждый запрос MySQL в клиенте mysqi завершается точкой с запятой, при выполнении запросов из PHP-скрипта отсутствие завершающей точки с запятой не приводит к ошибке.
Не обязательное ключевое слово if not exists сообщит, что базу данных следует создавать, только если база данных с таким именем отсутствует, это позволяет предотвратить завершение запроса ошибкой. Особенно это актуально при использовании SQL в РНР-скриптах.
Для того чтобы убедиться, что база данных forum успешно создана, можно выполнить команду show databases (листинг 2.2), которая покажет, какие базы данных существуют на вашем компьютере.
mysql> SHOW DATABASES;mysql> SHOW DATABASES;
mysql> SHOW DATABASES;
mysql> SHOW DATABASES;
Как видим, среди различных баз данных на компьютере автора присутствует и только что созданная база данных forum (рис. 2.2).
[ Замечанье )
Команда SHOW является внутренней командой MySQL, отсутствующей в стандарте SQL и не поддерживаемой другими базами данных. Далее, по мере опи
сания других операторов SQL, будут рассмотрены другие варианты использования команды SHOW.
+4
I Database I
I auth
1 count
I forum
I guest I local I叫sq】
I test
7 rows in set (G.00 sec)
Рис. 2.2 Созданные базы данных
Замечание
Изначально в MySQL присутствуют только две базы данных: test и mysql. В последней хранится системный каталог, описывающий внутреннюю структуру СУБД MySQL
USE
Для того чтобы начать работу с таблицами, необходимо сообщить MySQL, с какой базой данных вы намерены работать. Это осуществляется при помощи команды use:
OSE db_name;
Здесь db_name — название выбираемой базы данных. Выберем созданную базу forum (листинг 23).
•дилуймиуд* ・:Ч'^ .皆 J,'■ it'V^-' ^:t^J*♦*•■^TJ*<*^v-• • «<ц.*у ;;,■'! •« v* r'3t^ •±*,,t**y^y”*^yy,*学超 ^^^£|у^ду£*±,*.т,*¥г?門戶*厘 т*у,* *л*»*'Л^л v^-* *im^ »v. • ? л t? • • *•* jy," ^
”•”,♦.»,》C£”;u*;*i**W^^V$At*r; ;.#;;门・ 上.『;*;■*““““、•*„*△*“;♦«“;;,k,ir4*. .*;*¥.:♦”;*&”*S*:.;.l..”•”,♦.»,》C£”;u*;*i**W^^V$At*r; ;.#;;门・ 上.『;*;■*““““、•*„*△*“;♦«“;;,k,ir4*. .*;*¥.:♦”;*&”*S*:.;.l..时丽ZX盾时丽ZX盾;Листинг 2・3・ Выбор базы данных;
”•”,♦.»,》C£”;u*;*i**W^^V$At*r; ;.#;;门・ 上.『;*;■*““““、•*„*△*“;♦«“;;,k,ir4*. .*;*¥.:♦”;*&”*S*:.;.l..
”•”,♦.»,》C£”;u*;*i**W^^V$At*r; ;.#;;门・ 上.『;*;■*““““、•*„*△*“;♦«“;;,k,ir4*. .*;*¥.:♦”;*&”*S*:.;.l..
时丽ZX盾
时丽ZX盾
mysql> CREATE DATABASE forum; Database changed;
Замечание
При работе с клиентом mysql базу данных можно выбрать непосредственно при его загрузке, передав имя базы данных в качестве параметра, к примеру: mysql forum.
CREATE TABLE
Команда create table создает новую таблицу в выбранной базе данных. В простейшем случае команда имеет следующий синтаксис:
CREATE TABLE table__name [ (create_definition, , *») ] [table__options]
Здесь table name —— имя создаваемой таблицы; create^definition ——объявление столбца, его типа и атрибутов. В конце оператора может следовать необязательное указание типа таблицы tabie_options, например, TYPE=MyISAM.
Создадим первую таблицу базы данных forum, которая называется authors и содержит различные данные о зарегистрированных посетителях форума: ник (паше), пароль (passw), e-mail (email), Web-aapec сайта посетителя (url), номер ICQ (icq), сведения о посетителе (about), строку, содержащую путь к файлу фотографии посетителя (photo), время добавления запроса (time), последнее время посещения форума (last^time), счетчик сообщений, оставленных посетителем на форуме (themes), статус посетителя ——является ли он модератором, администратором или обычным посетителем (statususer). Кроме перечисленных полей в таблице имеется поле id_author, являющееся первичным ключом таблицы.
SQL-запрос, создающий эту таблицу, приведен в листинге 2.4.
mysql> CREATE TABLE authors (
mysql> id_author INT NOT NULL AUTO_INCREMENT,
mysql> name TINYTEXT,
rriysql〉passw TINYTEXT,
mysql> email TINYTEXT,
mysql> url TINYTEXT,
mysql> icq TINYTEXT,
mysql> about TINYTEXT,
mysql> photo TINYTEXT,
mysql> time DATETIME DEFAULT NULL,
mysql> last_time DATETIME DEFAULT NULL,
mysql> themes INT DEFAULT NULL,
mysql> statususer INT DEFAULT NULL, mysql> PRIMARY KEY (id^author) mysql> ) TYPE==MyISAM;
Выполнив SQL-команду show tables, можно убедиться, что таблица authors успешно создана (рис. 2.3).
Теперь аналогичным образом создадим другие таблицы.
В следующей таблице forums содержатся данные о разделах форума.
ЗЗак. П81
I Tabl®s_in_forun |
authors
гон in set (0.05 sec)
Рис. 2.3. Таблица authors успешно создана
工 Примечание ;
Для удобства на форуме может быть создано несколько различных разделов. К примеру, на форуме по языкам программирования для того, чтобы не смешивать темы, относящиеся к различным языкам, имеет смысл создать следующие разделы: C++, PHP, Java и т. д.
В таблице forums присутствуют следующие поля: первичный ключ (id_forum), название раздела (name), правила форума (rule), краткое описание форума (logo), порядковый номер (роз), флаг, принимающий значение 1, если форум скрытый, и 0, если общедоступный (hide).
SQL-запрос, создающий таблицу forums, приведен в листинге 25
Листинг 2.5* Создание таблицы forums
mysql> CREATE TABLE forums (
mysql> id^forum 工NT ⑹ NOT NULL AUTO__INCREMENT,
mysql> name TINYTEXT,
mysql> rule TEXT,
mysql> logo TEXT,
mysql> роз 工NT DEFAQLT NULL,
mysql> hide TINYINT(l) DEFAULT NULL,
mysql> PRIMARY KEY (id_forum) mysql> ) TYPE=MyISAM;
H^wf 繕.[NwyMMA^byi^llwAbUAayayfMVW^nmipgytAlj^M^^/^&^^^yjA* •■H^wf 繕.[NwyMMA^byi^llwAbUAayayfMVW^nmipgytAlj^M^^/^&^^^yjA* •■Структура форума может быть следующей: имеется список разделов, пере* ход по которым приводит посетителя к списку тем раздела. При переходе по теме посетитель приходит к обсуждению этой темы, состоящему из сообщений других посетителей. Теперь создадим таблицу themes, содержащую темы форума (листинг 2.6),
H^wf 繕.[NwyMMA^byi^llwAbUAayayfMVW^nmipgytAlj^M^^/^&^^^yjA* •■
H^wf 繕.[NwyMMA^byi^llwAbUAayayfMVW^nmipgytAlj^M^^/^&^^^yjA* •■
нг 26 Создание таблицы themes
mysql> CREATE TABLE themes (
mysql> id_theme INT(ll) NOT NULL AUTO_INCREMENT,
mysql> name TEXT,
mysql> author TINYTEXT,
mysql> id^author INT(6) DEFAULT NULL, mysql> hide TINYINT(l) DEFAULT NULL, mysql> time DATETIME DEFAULT NULL, mysql> id_forum TINYINT(2) DEFAULT NULL, mysql> PRIMARY KEY (id__therae) mysql> ) TYPE=MyISAM;
В таблице themes присутствуют следующие поля.
Первичный КЛЮЧ (id__theme).
口 Название темы (name).
口 Автор темы (author).
Внешний ключ к таблице авторов (id_author),
Флаг, принимающий значение 1, если тема скрытая, и 0 — если отображается (hide) (это поле необходимо для модерирования).
口 Время добавления темы (time).
Внешний ключ к таблице форумов (id_forum), для того чтобы определить, к какому разделу форума относится данная тема.
В таблице themes нормализация проведена частично, она содержит два внешних ключа: id_author н id_forum для таблиц посетителей и списка форумов, в то же время в ней дублируется имя автора author, присутствующее также в таблице посетителей authors под именем паше. Этот случай является примером денормализации, предназначенной для того, чтобы не запрашивать каждый раз таблицу авторов при выводе списка тем и их авторов н обеспечить приемлемую скорость работы форума.
Создадим последнюю таблицу posts, в которой хранятся сообщения посети^ телей (листинг 2.7).
mysql> CREATE TABLE posts (
mysql> id^post INT(ll) NOT NULL AUTO^INCREMENT,
mysql> name TEXT,
mysql> url TINYTEXTZ
mysql> file TINYTEXT,
mysql> author TINYTEXT,
mysql> id_ author INT(6) DEFAULT NULL, mysql> hide TINYINT(l) DEFAULT NULL, mysql> time DATETIME DEFAULT NULL, mysql> parent_post INT(11) DEFAULT NOLL, mysql> id_theme INT(ll) DEFAULT NULL, mysql> PRIMARY KEY (id_post) mysql> ) TYPEH^ylSAM;
В таблице posts присутствуют следующие поля.
口 Первичный ключ (id_post)>
口 Тело сообщения (name).
口 Необязательная ссылка на ресурс, которую автор сообщения может вве* сти при добавлении сообщения (uri).
口 Путь к файлу, прикрепляемому к сообщению (file).
Имя автора (author)*
О Внешний ключ к таблице авторов (id_author).
口 Флаг, принимающий значение 1, если сообщение отмечено как скрытое, и 0, если оно отображается (hide)—— это поле необходимо для модерирования.
口 Время добавления сообщения (time).
口 Сообщение, ответом на которое является данное сообщение (parent. post), если сообщение первое в теме —— это поле равно 0.
Внешний ключ к темам (id_theme), для того чтобы определить, к какой теме относится сообщение.
Убедимся, что все таблицы успешно созданы, выполнив команду show tables. Результат показан на рис. 2.4.
I Tables.in^forum I
I authors I forums I posts I themes
4 rows in set (Э.B1 qoc)
Рис. 24 Все таблицыбазы данных forum успешно созданы
DESCRIBE
Команда describe показывает структуру созданных таблиц и имеет следующий синтаксис:
DESCRIBE table_name
Здесь tabie_name —— нмя таблицы, структура которой запрашивается.
I Замечание )
Команда describe не входит в стандарт SQL и является внутренней командой СУБД MySQL.
Давайте посмотрим, к примеру, структуру таблицы forums, выполнив следующий SQL-запрос (листинг 2.8)*
:*:t:.^^*罡*:』辈^^q题W:**:1^W睪^^] '*''T‘?^^^уК1 第:";'£«瞽'№:半*.三:廿丁.七:斗经'^^闇】?"''三寸;"'"T二":工"?V^?:":岁W**":扌::翠£;'"'7*",7"' i Листинг 2<8. Оператор describe
mysql> DESCRIBE forums;
После выполнения этой команды интерпретатор MySQL выведет следующую таблицу (рис. 2.5).
1
Field
1 Type 1
Null 1
Key 1
Default 1
ExtraI
1
1 ]
1
1
id—forum name rule
】090 pos hide
1 int(6) 1
1 text 1
1 text 1
i text 1
1 int(G) 1
1 int(l) 1
VES 1
VES 1
VES 1
VES 1
VES 1
PRI 1 J 1
NULL1
NULL1
NULL1
NULL1
NULL1
NULL1
auto increment 1
1
6
rows in
set (0.05 sec)
Рис. 2.5. Структура таблицы forums
(Замечание )
Можно получить более полное описание структуры таблицы forums, включающее права доступа и комментарии, если воспользоваться оператором show FULL COLUMNS FROM forums;.
ALTER TABLE
Команда alter table позволяет изменить структуру таблицы. Эта команда позволяет добавлять и удалять столбцы, создавать и уничтожать индексы.
переименовывать столбцы и саму таблицу. Команда имеет следующий снн- таксис:
ALTER TABLE table_name alter_spec
Параметр aiter_spec имеет следующие основные значения (табл. 4*4).
Таблица 4.4. Основные преобразования, выполняемые оператором alter table
Синтаксис
Описание команды
ADD creata_de£inition
[FIRST 1 AFTER column_name]
Добавление нового столбца create_definition. create_def inition представляет собой название нового столбца и его тип. Конструкция FIRST добавляет новый столбец перед столбцом column_name. Конструкция AFTER добавляет новый столбец после столбца column_name. Если место добавления не указано, по умолчанию столбец добавляется в конец таблицы
ADD IWDEX [index_name]
Добавление индекса index__name для столбца
(index_col_name f...)
index_col_name. Если имя индекса index_name не указывается, ему присваивается змя^ совпадающее с именем столбца index_col__name
ADD PRIMARY KEY
Делает столбец index_col_name или группу столб-
(index^co^name, . *.) CHANGE old_col_name new_col_name type DROP col_name
цов первичным ключом таблицы
Изменение столбца с именем old_col_name на столбец с именем new_col_name и типом type
Удаление столбца с именем со1_пашв
DROP PRIMARY KEY
Удаление первичного ключа таблицы
DROP INDEX index_name
Удаление индекса index_name
Добавление в таблицу forums нового столбца test с размещением его после столбца name можно выполнить SQL-запросом, показанным в листинге 2.9.
:Листинг 2,9. Добавление столбца в таблицу
mysql> ALTER TABLE fortuns ADD test INT(10) AFTER name;
Выполнив команду describe forums, можно увидеть, что столбец test успешно добавлен после столбца паше (рис. 2.6).
f-—
1 Field
1 Type
Null 1
Key 1
Default
1 Extra
I id.forum
1 int(6)
1
PRI 1
NULL
1 auto increment
1 none
1 text
1
YES 1
NULL
1
1
1 teet
1 int(ie)
YES 1
]
NULL
1
1 rule
1 text
1
VES 1
1
NULL
1
1
1 logo
1 text
1
YES 1
1
NULL
1
1
1 pos
1 int(6)
1
VES 1
j
NULL
1
1 hide
1 int(l)
I
VE$ 1
1
NULL
1
1
Рис. 2.6. Новый столбец успешно добавлен 8 таблицу
Так, переименование столбца test в текстовый столбец new_test можно осуществить следующим образом (листинг 2.10),
Листинг 2.10. Переименование столбца
Я Л ** 抬,▲; о I • mT^raKwXrw!?^» йт > Л Лилг! Www«T«ииЯпТЯ
mysql> ALTER TABLE forums CHANGE test new_test text;
Как видно из рис. 2.7, столбец успешно переименован*
1 Field
1 Type
1 Null
1 Key
1 Default
1 Extra
1 id_forum
1 int(6)
1 PRI
1 NULL
1 auto increment
1 name
1 text
1 VES
1
1 NULL
1
I n©w_test
1 text
1 VES
1
1 NULL
1
1 rule
1 text
1 YES
1
1 NULL
1
J
1 logo
1 text
1 VES
1
1 NULL
1
1 pos
1 int(6)
1 VES
1
1 NULL
1
1
1 hide
1 int(l)
1 VES
1
1 NULL
1
1
7 rows in
set (6.6Э sec)
Рис. 2.7. Столбец переименован
При изменении только типа столбца, а не его имени, указание имени все равно необходимо, хотя в этом случае оно будет фактически повторяться (листинг 2.11).
Изменение типа столбца
?[暮*irf. ;*;:?;『..;寸吉 М ■■■■^■й'й'а “Г;;...” .W箔器;::;;';*<Й;3 ,;;、:,**,,;*.
mysql> ALTER TABLE forums CHANGE new_test new_test INT(5) NOT NULL;
Результат выполнения этого запроса приведен на рис. 2.8.
1
Field 1
Type 1 Null 1
Key
Default 1
Extra
id_forum 1
int(6) 11
PRI 1
NULL1
auto^incrcment 1
name1
text 1 YES 1
NULL1
new_test 1
int(5) 11
@1
rule1
text 1 VES 1
NULL 1
1
logo1
text 1 YES 1
NULL 1
1
pos1
int(6) 1 VES 1
NULL1
1 +
hide1
+
int(1) I YES (
NULL1
+.
1
7 rows in set (®.@® sec)
Рис. 2.8. Изменение типа столбца
Удаление столбца new__test, можно осуществить следующим запросом (листинг 2.12).
£[|^^||^ц^^^^^^^^^^^^^^^^|^^^^^^^^^^^; ■ tyiy^y^j^tf^^lR*弊^jj^i^V1 * ^5*Ш УЛ* * ^' ,'V* ” ,・ ±^^1%环¥ ,*^уту| ¥* 代空” МЬУЛУ,ALW " *, l\JAtl 屮 f$W^U
i Листинг 2.12. Удаление столбца иэ таблицы
:**#V.:".^^?^"*;,;當'.、-/*..,**%*.*,箝|i專^^^^^日*». Гf&; ...;當,¥,,1相;哈翻^*融1工入m信 ^jtГ.;;.;「.,“.. g :. "?rt>! Wgg".$,"#V*; ;;***一#::* j,3;,* ;;;.;;,.、$;;七.訖:м也"善,;q*4;一.g*T*
mysql> ALTER TABLE forums DROP new^test;
Как видно из рис. 2.9, после удаления этого столбца таблица forxims приобрела исходную структуру.
1
Field
1 Type 1
Null I
Key I
Default 1
Extra1
1
1
1
1
1
1
id_forum папе rule logo pos hide
1 xnt(6) I
1 tGXt 1
1 text 1
1 text 1
1 int(6) 1
1 int(1) I
I
VES I
YES I
YES I
VES I
VES I
PRI I
I
1
1
NULLI
NULL【
NULL1
NULL1
NULL1
NULLJ
auto increment I
1
6
rows in
set (0.08 sec)
Рис. 2.9. Удаление столбца прошло успешно
DROP TABLE
Команда drop table предназначена для удаления одной или нескольких таблиц:
DROP TABLE tableqame [ f table_name,...]
К примеру, для удаления таблицы forums нужно выполнить следующий
SQL-запрос (листинг 2.13).
:"*二,:" ":閃 h...:*,將缁 33;患£:£:学1£"::m^*^_整У"¥:鼻飞瞿丁:‘:ЦНf:х;; \~*',^r^ 訂 %吃;.,¥*#*”***,卄*:**飞± 仁二产:里2¥* 工*?亨卡吗,5>еда","」:*!*:."、"产'mwnw ,:*:
Листинг 2.13. Удаление таблиц:
'* •■■^■■•**^* w**'r»1 ■■■■■'••'•«**""**i■**•"■■•■■■■■■■••****"**rliiii'**«**"«»*»«'"",«*«""«""""-^»«*""'"''"""W"iA »f/i*i««-*eii!"»'Лч'Л■■i't.«#4'**V»ii'ii*«*il**'*»ib,*?e.,*eem*£^wViii*»«*»*«V^eew'f r»| mysql> DROP TABLE foirums;
DROP DATABASE
Команда drop database удаляет базу данных co всеми таблицами, входящими в ее состав:
DROP DATABASE database_name
Н&прммер, удалить базу данных forum можно следующим SQL-запросом (листинг 2Л4).
Листинг 2.14. Удаление базы данных
^rRWif^^nWra^KiirJlitalwWvn A>aW(^a«(7 *•?■»> 7о><>> »'>■■>■*»>■> Л>Л{А>|Г<Л,!^>7аа>«'аЛ71?кТл&»««<>/«4Т<а>,>.*>...<>>'«.а1Гаа1'««^---'-•.■..>'Л>'>..
mysql> DROP DATABASE forum;
INSERT INTO... VALUES
Команда insert. . .values вставляет новые записи в существующую таблицу. Синтаксис команды:
INSERT INTO table__name VALUES (values,...)
После оператора values в скобках через запятую перечисляются все значения соответствующих полей таблицы в соответствии с их типами*
Для того чтобы вставить в базу данных authors несколько записей, в которых расположена информация о зарегистрированных посетителях форума, можно воспользоваться нескольким^! операторами insert (листинг 2」5).
j Листинг 2.15. Оператор insertj Листинг 2.15. Оператор insertfe'ba *«*.♦♦..■♦♦*・■ ■'■ »*■«»»»■ *'*;♦ **;'***.'**♦***.***«*♦*・• • • * ***********•・;** *; J*・ * *'匸*;*24;,『** ■■ ■.fe'ba *«*.♦♦..■♦♦*・■ ■'■ »*■«»»»■ *'*;♦ **;'***.'**♦***.***«*♦*・• • • * ***********•・;** *; J*・ * *'匸*;*24;,『** ■■ ■.mysql> mysql> mysql> mysql> mysql> mysql> mysql> mysql> mysql>mysql> mysql> mysql> mysql> mysql> mysql> mysql> mysql> mysql>INSERTINSERTINSERTINSERTINSERTINSERTINTOINTOINTOINTOINTOINTOauthorsauthorsauthorsauthorsauthorsauthorsVALUESVALUESVALUESVALUESVALUESVALUES(0, 1 Maks ',,123\ 1maks@mail. ru1, T wv; Ф softtin®. ru I ‘I 'протравив? ‘I 'I L 0f 0);(0, 1Igor *, '123'# 1igor@mail.ru1, 1www.softtime.ru*, ,', * Программист1, 'I 1 \407, 0);(0, 1 Sergey1# ‘2工2‘, 1sergey@mail* ru1, ‘www.softtime.rul * 1, * Дизайнер', L г, г, 408, 0);(0, 1 Maks ',,123\ 1maks@mail. ru1, T wv; Ф softtin®. ru I ‘I 'протравив? ‘I 'I L 0f 0);(0, 1Igor *, '123'# 1igor@mail.ru1, 1www.softtime.ru*, ,', * Программист1, 'I 1 \407, 0);(0, 1 Sergey1# ‘2工2‘, 1sergey@mail* ru1, ‘www.softtime.rul * 1, * Дизайнер', L г, г, 408, 0);
j Листинг 2.15. Оператор insert
j Листинг 2.15. Оператор insert
fe'ba *«*.♦♦..■♦♦*・■ ■'■ »*■«»»»■ *'*;♦ **;'***.'**♦***.***«*♦*・• • • * ***********•・;** *; J*・ * *'匸*;*24;,『** ■■ ■.
fe'ba *«*.♦♦..■♦♦*・■ ■'■ »*■«»»»■ *'*;♦ **;'***.'**♦***.***«*♦*・• • • * ***********•・;** *; J*・ * *'匸*;*24;,『** ■■ ■.
mysql> mysql> mysql> mysql> mysql> mysql> mysql> mysql> mysql>
mysql> mysql> mysql> mysql> mysql> mysql> mysql> mysql> mysql>
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INTO
INTO
INTO
INTO
INTO
INTO
authors
authors
authors
authors
authors
authors
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
(0, 1 Maks ',,123\ 1maks@mail. ru1, T wv; Ф softtin®. ru I ‘I 'протравив? ‘I 'I L 0f 0);
(0, 1Igor *, '123'# 1igor@mail.ru1, 1www.softtime.ru*, ,', * Программист1, 'I 1 \407, 0);
(0, 1 Sergey1# ‘2工2‘, 1sergey@mail* ru1, ‘www.softtime.rul * 1, * Дизайнер', L г, г, 408, 0);
(0, 1 Maks ',,123\ 1maks@mail. ru1, T wv; Ф softtin®. ru I ‘I 'протравив? ‘I 'I L 0f 0);
(0, 1Igor *, '123'# 1igor@mail.ru1, 1www.softtime.ru*, ,', * Программист1, 'I 1 \407, 0);
(0, 1 Sergey1# ‘2工2‘, 1sergey@mail* ru1, ‘www.softtime.rul * 1, * Дизайнер', L г, г, 408, 0);
Начиная с версии MySQL 3.22.5, можно добавить сразу несколько записей с помощью многострочного оператора insert (листинг 2J6).
mysql> INSERT INTO authors VALUESmysql>mysql>mysql>mysql>mysql>mysql>mysql>mysql>mysql> INSERT INTO authors VALUESmysql>mysql>mysql>mysql>mysql>mysql>mysql>mysql>(0, 1Maks1, ‘123’,,maks@mail.ru1, 'www.softtime.ru * f '', 'программист L L ! 0, 0),(0r 1 Igor*, 11231/ figorSmail.ru1r'www. softtime. ru*, ' \ 'Проорамьтст' L L L 407, 0),(0, 'Sergey', ‘212*, 'sergey@mail.ru 1 www * softtiine * m1, ‘I ' Дизайнер1,, 'I ‘I 408, 0);(0, 1Maks1, ‘123’,,maks@mail.ru1, 'www.softtime.ru * f '', 'программист L L ! 0, 0),(0r 1 Igor*, 11231/ figorSmail.ru1r'www. softtime. ru*, ' \ 'Проорамьтст' L L L 407, 0),(0, 'Sergey', ‘212*, 'sergey@mail.ru 1 www * softtiine * m1, ‘I ' Дизайнер1,, 'I ‘I 408, 0);
mysql> INSERT INTO authors VALUES
mysql>
mysql>
mysql>
mysql>
mysql>
mysql>
mysql>
mysql>
mysql> INSERT INTO authors VALUES
mysql>
mysql>
mysql>
mysql>
mysql>
mysql>
mysql>
mysql>
(0, 1Maks1, ‘123’,,maks@mail.ru1, 'www.softtime.ru * f '', 'программист L L ! 0, 0),
(0r 1 Igor*, 11231/ figorSmail.ru1r
'www. softtime. ru*, ' \ 'Проорамьтст' L L L 407, 0),
(0, 'Sergey', ‘212*, 'sergey@mail.ru 1 www * softtiine * m1, ‘I ' Дизайнер1,, 'I ‘I 408, 0);
(0, 1Maks1, ‘123’,,maks@mail.ru1, 'www.softtime.ru * f '', 'программист L L ! 0, 0),
(0r 1 Igor*, 11231/ figorSmail.ru1r
'www. softtime. ru*, ' \ 'Проорамьтст' L L L 407, 0),
(0, 'Sergey', ‘212*, 'sergey@mail.ru 1 www * softtiine * m1, ‘I ' Дизайнер1,, 'I ‘I 408, 0);
При использовании такого оператора записи приводятся в круглых скобках через запятую после ключевого слова values.
Порядок добавления столбцов можно задавать самостоятельно, воспользовавшись формой оператора insert:
INSERT INTO tbl_name (col_namel, col_name2, ...) VALUES (valuel, value2,.・.);
Так, запись в таблицу authors можно осуществить при помощи SQL- запроса, представленного в листинге 2.17. При этом значения полей, не указанные в списке, следующем за названием таблицы authors, устанавливаются по умолчанию. Первичный ключ получает значение null, которое интерпретируется для полей с атрибутом auto.increment точно так же, как и значение 0: происходит генерация уникального числа.
Листинг 2.17, Определение порядка добавления столбцовЛистинг 2.17, Определение порядка добавления столбцов
Листинг 2.17, Определение порядка добавления столбцов
Листинг 2.17, Определение порядка добавления столбцов
mysql> INSERT INTO authors (name, mail, urlf about)
VALUES {*Maks r, ^ks^iriail.ru*, ,www.softtime. rur, Программист*);
Версия MySQL 3.22.10 позволяет задавать значения полей в операторе
INSERT В форме соl_name=value (ЛИСТИНГ 2」8)・
mysql> INSERT INTO authors SET name= * Maks1r mail=,maks@mail.ru1, mysql>url='www.softtime.ru Imysql>about«'программист',);mysql> INSERT INTO authors SET name= * Maks1r mail=,maks@mail.ru1, mysql>url='www.softtime.ru Imysql>about«'программист',);
mysql> INSERT INTO authors SET name= * Maks1r mail=,maks@mail.ru1, mysql>url='www.softtime.ru I
mysql>about«'программист',);
mysql> INSERT INTO authors SET name= * Maks1r mail=,maks@mail.ru1, mysql>url='www.softtime.ru I
mysql>about«'программист',);
Здесь также для полей, не получивших значения, будут выставлены значения по умолчанию.
DELETE
DELETE FROM table^name [WHERE definition]
Команда delete удаляет из таблицы tabie一name записи, удовлетворяющие заданным в definition условиям, и возвращает число удаленных записей.
Как можно удалить все записи из таблицы authors, показано в листинге 2*19.
;Листинг 2.19. Удаление записей
mysql> DELETE FROM authors;
Важной частью запросов delete, update и select является оператор where, позволяющий задать условия для выбора записей, на которые будут действовать эти команды. Следующий запрос удаляет из таблицы запись о посетителе, первичный ключ для которой равен 1 (листинг 2.20).
I',,・・,r ,♦+,,♦:,f.……■•,•丁… •-•'•'•,••■ • • 1 •品J,,《£・,,,• •
указанному критериюуказанному критериюЛистинг 2.20. Удаление записей по
указанному критерию
указанному критерию
raysql> DELETE FROM authors WHERE id_author = 1;
Условия отбора могут быть значительно сложнее. Например, в листинге 2.21
удаляются записи обо всех авторах, пароль которых Ч231 и первичный ключ превышает значение 10.
mysql> DELETE FROM authors WHERE passw ='工23’ AND id_author > 10;
Оператор and является логическим *'И". В запросах можно также применять логическое "ИЛИ" — or.
[ Замечание )
СУБД MySQL в качестве альтернативы операторам and и or поддерживает синтаксис, принятый в С-подобных языках программирования. То есть вместо AND можно применять &&, а вместо or——1 I.
SELECT
Команда select предназначена для извлечения строк данных из одной или нескольких таблиц и имеет в общем случае следующий синтаксис:
SELECT column,...
[FROM table WHERE definition]
[ORDER BY col_name [ASC | DESC],・.J
[LIMIT [offset], rows]
Здесь column — имя выбираемого столбца. Можно указать несколько столбцов через запятую. Если необходимо выбрать все столбцы, можно просто указать знак звездочки *. Ключевое слово from указывает на таблицу table, из которой извлекаются записи. Ключевое слово where, как и при операторе delete, определяет условия отбора строк. Ключевое слово order by сортирует строки запросов по столбцу coi_name в прямом (asc) или обратном порядке (desc). Ключевое слово limit сообщает MySQL о выводе только rows запросов начиная с позиции offset.
--Для выборки из базы данных при помощи оператора select добавим в таблицу forums несколько записей (листинг 2.22).
-
-
Листинг 2.22. Вставка 5 записей в таблицу forums
mysql> INSERT 工NT。 forums VALUES (0,mysql> INSERT 工NT。 forums VALUES (0,mysql> INSERTmysql> INSERTINTO forums VALUESINTO forums VALUESmysql>mysql>INSERTINSERTINTO forums VALUESINTO forums VALUES(0,(0,mysql> INSERT INTO forums VALDES (0,mysql> INSERT INTO forums VALDES (0,mysql> INSERT INTO forums VALUES (0,mysql> INSERT INTO forums VALUES (0,* Форум!1f т Форум21f тфОрумЗ', * Форум4 I 1Форум51,* Форум!1f т Форум21f тфОрумЗ', * Форум4 I 1Форум51,丄 L 5, 0);一,'I 4, 0);一,L 3, 0);L L 2『0);L ! lrО);丄 L 5, 0);一,'I 4, 0);一,L 3, 0);L L 2『0);L ! lrО);
mysql> INSERT 工NT。 forums VALUES (0,
mysql> INSERT 工NT。 forums VALUES (0,
mysql> INSERT
mysql> INSERT
INTO forums VALUES
INTO forums VALUES
mysql>
mysql>
INSERT
INSERT
INTO forums VALUES
INTO forums VALUES
(0,
(0,
mysql> INSERT INTO forums VALDES (0,
mysql> INSERT INTO forums VALDES (0,
mysql> INSERT INTO forums VALUES (0,
mysql> INSERT INTO forums VALUES (0,
* Форум!1f т Форум21f тфОрумЗ', * Форум4 I 1Форум51,
* Форум!1f т Форум21f тфОрумЗ', * Форум4 I 1Форум51,
丄 L 5, 0);
一,'I 4, 0);
一,L 3, 0);
L L 2『0);
L ! lrО);
丄 L 5, 0);
一,'I 4, 0);
一,L 3, 0);
L L 2『0);
L ! lrО);
Для того чтобы посмотреть всю таблицу forums, выполняется следующий запрос (листинг 2.23).
:: .*,:...у 1:“, ♦“….”$“”“ ”7”.\・v“I'*
:Листинг 2.23. Оператор select
mysql> SELECT * FROM forums;
В данном запросе происходит выборка всех столбцов из таблицы forums без ограничений. Результат запроса показан на рис. 2J0.
卜
1 fd_forum
—
name
+
rule 1 logo
pos 1 hide |
Форум! 0юруи2 Форум3 Форум4 Форум5
Рис. 2.10. Результат выполнения запроса из листинга 2.23
Можно выбрать не все столбцы таблицы, а лишь часть, для этого необходимо явно задать список выбираемых столбцов (листинг 2.24).
^ВЙЖН^Р^; ” 'Ti?^^^ 号".'二!Л二空"挣 1ЧГ .:中*笠豊巻 ^^;.『.? м »ул^, Мьмух^^- ■ ■,! •■ ;K^* '^V^Vr:* .♦«.„;»».».:融門邑;空炉纥产#л*—# 启
:Листинг 2.24. Частичная выборка
mysql> SELECT id_forum, name FRM forums;
В этом случае MySQL выведет лишь два столбца с первичным ключом id_forum и названием форума name (рис. 2ЛI).
I id.forum | name I+--—--—+-+
I1IФорум1I
I2IФорум21
J31 ФорумЗ 1
IЧI中叩ум4I
I5IФорум5I
Рис. 2.11. Результат выполнения запроса из листинга 2.24
Оператор limit используется для ограничения количества строк, возвра
щаемых командой select. Пример приведен а листинге 2.25.
,"•*•*• "*、*”:.,,\У f y*・f 7” •**■>••••“**,"•*•*• "*、*”:.,,\У f y*・f 7” •**■>••••“**:Листинг 2.25. Использование ключевого слова limit в операторе select:Листинг 2.25. Использование ключевого слова limit в операторе select
,"•*•*• "*、*”:.,,\У f y*・f 7” •**■>••••“**
,"•*•*• "*、*”:.,,\У f y*・f 7” •**■>••••“**
:Листинг 2.25. Использование ключевого слова limit в операторе select
:Листинг 2.25. Использование ключевого слова limit в операторе select
mysql> SELECT * FROM forums LIMIT 3;
В результате этого запроса будут выведены только первые 3 записи из 5 (рис. 2.12).
++f+十一+>I id_forurnIname| rule|logo| pos|hide|+^1--++ + 4++f+十一+>I id_forurnIname| rule|logo| pos|hide|+^1--++ + 43 I ФоруыЗ I I I 3 I О I3 I ФоруыЗ I I I 3 I О IРис. 2.12. Результат выполнения запроса из листинга 2.25Рис. 2.12. Результат выполнения запроса из листинга 2.25
++f+十一+>
I id_forurnIname| rule|logo| pos|hide|
+^1--++ + 4
++f+十一+>
I id_forurnIname| rule|logo| pos|hide|
+^1--++ + 4
3 I ФоруыЗ I I I 3 I О I
3 I ФоруыЗ I I I 3 I О I
Рис. 2.12. Результат выполнения запроса из листинга 2.25
Рис. 2.12. Результат выполнения запроса из листинга 2.25
'.•**♦*<♦**.*•**,•**,**♦»•»#?'.•**♦*<♦**.*•**,•**,**♦»•»#?Оператор limit может также принимать два числовых аргумента, которые должны быть целыми числами. В этом случае последний аргумент задает максимальное количество возвращаемых строк, а первый сообщает MySQL, начиная с какой строки производить отсчет (листинг 2.26).
'.•**♦*<♦**.*•**,•**,**♦»•»#?
'.•**♦*<♦**.*•**,•**,**♦»•»#?
.“.А*, **^^у**№・ддл,.2.&炉 с・,・ **±1・¥«д*,*.|. ・&・.*,. ¥***«•*...*.,.!.**,]!¥”*.**,,+ + *,+*,,;,:..•*,«.+!•*•/.. *・,"《・,戶.*1*:11.,,. *■ •・.・■,/..* *.*『••
;Листинг 2.26. Альтернативная форма задания limit
二番?;;•';*;♦*i轟it ;.;;,♦«=■;〔••••ir*l»*'irt«iiitTftrtStK'ft'fiiin5<ьй- .■ ■ ■>V>V<'...『♦—%“.負Л»мЧ«*»*г«>»»«*,*<*«;<»« *"
mysql> SELECT * FROM forums LIMIT 1,3;
В этом случае будут возвращены строки 2, 3 и 4 (рис. 2*13).
1 1d_forum 1 naeie | rule | logo J pos
hide ;
2 I 8py』2 II14
о j
孑 Фору«33
4 1 Форум4 1112
О 1
Рис. 2.13. Результат выполнения запроса из листинга 2*26
Оператор where применяется в команде select точно так же, как и в команде delete. Выберем из таблицы только те записи, у которых значение id_forum больше 2 (листинг 2.27).
my^ql〉 SELECT * FROM forums WHERE id_forum > 2;my^ql〉 SELECT * FROM forums WHERE id_forum > 2;
my^ql〉 SELECT * FROM forums WHERE id_forum > 2;
my^ql〉 SELECT * FROM forums WHERE id_forum > 2;
Результат показан на рис. 2.14.
1 1d_fогит
папе 1
rule
logo 1
pos
hide!
Iз
Форум3 1
0 1
4
•оруЗ 1
2
0
I5
Форум5 1
1
1
0 I
Рис. 2.14. Результат выполнения запроса из листинга 2.27
Порядок сортировки выводимых записей можно задавать при помощи оператора ORDER BY (ЛИСТИНГ 2.28).
,*^ltf^^^Wi!置产州 WmfiWmtfWftttf^yKtWtf 焚 HYttH^W*^*^?^*^!*'!^^'通究■N&^tytfft!AF ij^yNy&iy^y^^ffiy^y^HilOi Листинг 2.28. Сортировка результатов запроса
mysql> SELECT + FROM forums WHERE 土d_£omm > 2 ORDER BY pos;
В этом запросе выводятся все записи со значением поля id_£orum не меньше двух, которые при этом сортируются по значению поля pos. Результат такого запроса показан на рис. 2.15.
Рис. 2.15. Результат выполнения запроса из листинга 2.28
67
Для дальнейшего рассмотрения оператора select нам понадобится еще несколько записей в таблице forums. Разместим при помощи SQL-запроса, приведенного в листинге 2.29, группу новых форумов со значением поля hide = 1 (листинг 2.29). Форумы с таким значением поля hide являются скрытыми и недоступны пользователям.
mysql> INSERT INTO forums VALUES
(0,
1Форумб',
'1, 5,
1),
mysql>
(0,
• Форум71
'I ‘1 4,
1),
mysql>
(0,
'Форум81
1),
mysql>
(0,
'Форум9*,
L L 2,
1),
mysql>
(0,
1Форум!0*,
1);
Часто стоит задача группировки значений, которая осуществляется при помощи оператора group by, пример использования оператора приведен в листинге 2.30.
mysql> SELECT ★ FROM forums GROUP BY hide;mysql> SELECT ★ FROM forums GROUP BY hide;
mysql> SELECT ★ FROM forums GROUP BY hide;
mysql> SELECT ★ FROM forums GROUP BY hide;
Результат выполнения SQL-запроса, приведенного в листинге 2.30, показан на рис. 2.16.
I 1d_forumIname |rule |logo |pos | hide1
+|>-++-++■►
1 «орун! II5|0
13 I Форумб II5 I工 1
Рис. 2.16. Результат выполнения SQL-запроса из листинга 2.30
UPDATE
UPDATE table
SET col__namel=exprl [, col_name2=expr2 ...]
[WHERE definition] [LIMIT rows]
Оператор update обновляет столбцы таблицы table в соответствии с их новыми значениями в строках существующей таблицы. В выражении set указывается, какие именно столбцы следует модифицировать и какие величины должны быть в них установлены. В выражении where, если оно присутству-
ет, задается, какие строки подлежат обновлению. В остальных случаях обновляются все строки. Ключевое слово limit позволяет ограничить число обновляемых строк.
В листинге 2.31 разделу форума с первичным ключом 2 присваивается новое название (PHP), и атрибут hide устанавливается равным i, что делает форум невидимым.
^^^^“.У^ЮЛД^^^^У^*?"^^'" **,^^.‘‘":咨^■"';‘"""'2£^З^Й^货■'"';" Т'*****':"'*'-^”""'"*;,^"*.^1^^1^^^""3^^***^1^^^^^1^!.*"^—』^
;Листинг 2.31. Применение оператора update
九・^^;能$¥“:,也 I“》&Ic.t”. A «it.llt^fll .^Х^^*.Л^Й??3“.;4%г А ;■:. ^^. ““;.;,,,:. гХ:к.,・А,..;;“””“.”.“.^Й^^“:,:・、^Т^^^^?П““;Е:
UPDATE forums SET name^^HP*, hide=l WHERE id forum=2;
SHOW
Оператор show может принимать множество форм н предназначен для мониторинга таблиц, баз данных и сервера MySQL. При помощи оператора show character set можно выяснить кодировки, поддерживаемые сервером MySQL (рис. 2.17).
+ ——1
j Charset
Description
Default collation
Maxlen 1
big5 1 at i nl
寸is t¥s620 euckr gbZMlN
Utf8 ucs2 binary +4
4
big5_chinese_c1 1 at i nl_swedish_t i ujis_japanese_ci sps_japanese„ci tis620_iha-i_cl euckr_korean_ci gb2312_chinese_ci gbk_chinese_ci utf8_general_cl ucs2_general_ci binary
2
2 1
2
2
3
2 工
Рис- 2*17. Результат выполнения запроса SHOW CHARACTER SET.
Оператор show column types позволяет вывести перечень информации о типах столбцов, которые использовались при создании таблиц MySQL.
( Замечание )
Операторы show character set и show column types поддерживаются, начиная с версии MySQL 4.1.
Так, вывести список всех столбцов выбранной таблицы можно при помощи следующего запроса (листинг 2.32).
窪厂…;:尸…Листинг 2.32.窪厂…;:尸…Листинг 2.32.Вывод структуры таблицы при помощи оператора showВывод структуры таблицы при помощи оператора show
窪厂…;:尸…
Листинг 2.32.
窪厂…;:尸…
Листинг 2.32.
Вывод структуры таблицы при помощи оператора show
Вывод структуры таблицы при помощи оператора show
mysql> SHOW FIELDS FROM authors;
Результат показан на рис. 2.18*
4--
1 Field
1
Typ@
1
Null
1 Key
1
Default 1
Extra
1 id_author
1
int(6)
1
1 PRI
1
NULL1
auto_incr©inent
1
1 паше
1
text
]
VES
I
J
NULL 1
1
1 passx
1
text
1
YES
1
1
NULL1
1
1 email
I
text
1
VES
I
NULL1
1
1 url
1
text
1
VES
1
NULL1
1 icq
1
text
1
VES
I
1
NULL1
1
1 about
text
1
VES
1
1
NULL 1
1
1 photo
1
text
1
VES
1
1
NULL 1
1
1 time
1
datetime
1
VES
1
1
NULL1
1
1 lasjtinw
datetime
1
VES
1
1
NULL 1
1
1 themes
1
int(10)
1
VES
1
NULL 1
1
1 statususer
int(2)
1
YES
1
1
NULL 1
1
Рис. 2.18. Результат выполнения запроса из листинга 2.32
Замечание )
Получить результат, представленный на рис. 2.18, можно при помощи альтернативных операторов DESCRIBE authors и SHOW COLUMNS FROM authors.
:Замечание )
При помощи оператора show full columns from authors можно получить расширенное описание структуры таблицы.
Можно также отобразить информацию обо всех индексах конкретной таблицы (листинг 2.33).
賞"L词娄券了:裁‘*, ", 军阔堂岁1"胃"£黑:胃:』準"一筹擎?胃裁^^^^^世^^:工圈變管:寧.磔渺*三:*岁;/装中舞1置*審"琢翠龄1綴照辞‘:焉 :Листинг 2.33. Отображение информации об индексах таблицы
Eysql> SHOW INDEX FROM authors;
Оператор show processlist позволяет получить информацию о потоках, выполняющихся на сервере (рис. 2.19).
Оператор show status позволяет получить значения переменных состояния сервера. Информацию о таблицах текущей базы данных можно получить при помощи оператора show table status, выводящего многочисленную информацию: имя таблицы, тип, формат хранения строк, среднее число байт» занимаемое таблицами, реальный размер файла данных таблицы, файла индекса, следующее значение для столбца с атрибутом auto__increment и т. п.
I Id I User I HostI db I Command I Time I State I Info
■+
I 1 I ODBC I 127.0.0.1:3008 I forum I Query | G | NULL I SHOU PROCESSLIST
■+
Рис. 2.19. Список выполняемых запросов
Как уже упоминалось, при помощи операторов show databases и show tables можно просматривать список баз данных, размещенных на сервере, и список таблиц текущей базы данных.
〔 Замечание )
Программа mysqlshow, входящая в дистрибутив MySQL, позволяет осуществить просмотр информации, предоставляемой по запросу оператора show. Так, просмотр списка баз данных можно осуществить, запустив в командной строке mysqlshow без параметров. Передав в качестве параметра имя базы данных (к примеру, mysqlshow forum), можно получить список таблиц этой базы данных.
Соответствие шаблону (LIKE и NOT L/KE)
СУБД MySQL позволяет осуществлять запросы, которые отвечают определенным шаблонам, т* е. не определяя точно всего искомого значения. Для этого используются специальные операторы — like и not like, — которым передается строка с символами-заместителями. Символ соответствует любому одному символу, аналогично символу ”・"в регулярных выражениях, а символ ”电"соответствует любой поеледовательности символов или их отсутствию и является аналогом символа ”*” в регулярных выражениях.
Так, SQL-запрос SELECT name, url FROM authors WHERE name LIKE 'SS‘; К таблице authors, сформированной в листинге 2* 15, приводит к выводу зарегистрированных посетителей форума, чье имя начинается с буквы ”s“ (рис. 2.20).
I name j url|
I sergey I www.softtlme.ru |+++
Рис. 2.20. Выборка по шаблону
Функция COUNT
Функция count позволяет получить количество строк, выбранных запросом. Так, SQL-запрос, приведенный в листинге 2.34, позволяет узнать общее количество посетителей форума.
药Щ^^^^Я耨灣爭为停电體船^^й^1 鸨將緇详?妙^!^^^^艺学郑噹後^^"北? '^1 ':,^^'!^^之界‘丁工叱挚审袋鯉教俗]
:Листинг 2.34. Общее количество строк в таблице authors疗
“*« Jm^f^KWi^&RG.i^ ■ г#1*4*втПп,#'11 ■ I ir^Vr+л inyp ;:;*,*、;; “,•$,* 抖■箝% ♦♦***『!■ ^T^^^,/*;*»;*”*,*+,.**;*♦. .;•;.♦・『・.■.•♦■•...;*.,...*•,•*•*.*・》..*** $;;・ *^^;.・ *\Т* * * о ■#■»*♦/*
mysql> SELECT COUNT(*) FROM authors;
В качестве аргумента функции может выступать имя столбца таблицы: например, результат запроса, приведенного в листинге 2.35, аналогичен результату запроса из листинга 2.34.
黎"11aMi^Й^ШИ^МЙШЙШ^МЮ^ЙМ1Й^^!^З^^Ж^1^缈[Ц^**‘':"产弊 w ,* 丫 l 整产羽^^^^д
г 2.35. Альтернативный способ подсчета посетителей=
е^1制號斎森卷睛也穗魁請、М:;,…;SF/巒您陪姦,:k..、二:;;、、息;*;.•疝;*Sw»s.糜,感心:n
mysql> SELECT COUNT(name) FROM authors;
丈¥胃?丈¥胃?Листинг 2.37. Функц)Листинг 2.37. Функц)3MR3MRЧасто данные в таблицах дублируются. Получить выборку уникальных значений позволяет ключевое слово distinct, которое помещается перед именем столбца. Так, в листинге 2.36 приводится выборка URL пользователей. Несмотря на то, что у некоторых пользователей URL могут совпадать, каждый удовлетворяющий запросу URL выводится только по одному разу*
丈¥胃?
丈¥胃?
Листинг 2.37. Функц)
Листинг 2.37. Функц)
3MR
3MR
mysql> SELECT COUNT(DISTINCT name) FROM authors;
Если полученный результат не совпадает с результатом, полученным в листинге 2.34, указанное выше требование не соблюдается.
Наиболее продуктивно функция count используется совместно с оператором group by. Так, подсчет количества скрытых и доступных форумов можно осуществить одним SQL-запросом, приведенным в листинге 2,38.
;"JJV^Vt*^*%Ч***"*;,.:Fiy.","1W …¥”,?"》,為",•,:.;,式:,:::г4:6'"丁^^.*^yУ":"【:.'.
i Листинг 2.38. Совместное использование count и group by
気.*ЙИ??Й?К?Й?Х^8Л^ЖЙЙ&ЙЖ?寐品分百.;.工你:求岔3£:;二?扌5Нй?;7通書1:*:.:]..;..::—」?■;..:…"?.‘:;:).g丁*:.•;.•;::
mysql> SELECT hide, COUNT(*) FROM forums GROUP BY hide;
Результат работы запроса показан на рис. 2.21: в базе данных имеются 5 скрытых н 5 открытье форумов.
I hide I count(«) |I hide I count(«) |
I hide I count(«) |
I hide I count(«) |
Рис* 2.21. Результат выполнения SQL-запросаиз листинга 2*38
Многотабличные запросы
Все рассматриваемые до сих пор запросы осуществлялись с использованием только одной таблицы, однако СУБД MySQL, как и любая другая СУБД, позволяет осуществлять запрос сразу к нескольким таблицам базы данных. Запросы, осуществляемые к двум или более таблицам, отличаются следующими характеристиками:
□ в предложении from следует через запятую указывать имена таблиц, к которым осуществляется SQL-запрос: from tablel, talble2;
口 при обращении к именам столбцов необходимо явно указывать имя таблицы: tablel .value, table? .value.
( Замечание )
При многотабличных запросах имя таблицы при обращении к столбцам, уникальным в пределах двух таблиц^ можно не указывать.
Подсчитать количество тем, содержащихся в каждом из форумов, можно при помоши SQL-запроса, приведенного в листинге 2.39.
}»”:^^缴/霆,^^Л|掛:[当^^^y.~^g^^^^^^^g^gy^y^^jgy^^g^^q*i*B^y *整艾 *"",t:y^i**.¥ :Листинг 2.39. Выборка количества тем в каждом из форумов 缘房霭里整會蝴斎靄;;.;而方爲翩轟靠需崑仁:争办K?f^^^St匿才諡',;、击%#;С'厶;总案£需话需匕占::r"筋二荷二:…;,;,.
mysql> SELECT count(*)
mysql> FROM themes, forums
mysql> WHERE themes, id_forum = forums. id_forum mysql> GROUP BY forums.id_forum;
Еще одним примером многотабличного запроса может служить подсчет числа сообщений, оставленных каждым из посетителей форума. Такой запрос показан в листинге 2.40.
二强'嘴蠲^.L ■三颦:修E三 密密葷^ЗД^ 充芸睪犖"発f盥辞缠驟出整翎您死,袋 门号学:¥’ - -^'1*'^-F !<""?',v """■■'-'■ 7*"? V"
:Листинг 2.40, Выборка количества оставленных сообщений для каждого
i из посетителей
mysql> SELECT count(*)
mysql> FROM authors, posts
mysql> WHERE authors * name = posts.authors
raysql> GROUP BY authors .name;
Конечно, если в таблице posts сохранять не имена посетителей posts .authors, а первичные ключи, соответствующие их записям в таблице authors, SQL-запрос выполнялся бы намного быстрее. Однако на практике предпочитают не пользоваться запросом, приведенным в листинге 2,40, а обновлять счетчик сообщений (authors.themes) при добавлении посетителем нового сообщения.
В то же время имя автора должно выводиться около каждого сообщения. Если в таблице posts будет присутствовать только первичный ключ, соответствующий записи таблицы authors, это потребует каждый раз осуществи лять многотабличный запрос» направленный на выяснение имени автора (authors.паше), ПО его первичному КЛЮЧУ (authors. id_name).
: Замечание )
При составлении многотабличных запросов следует помнить, что они выполняются медленнее запросов, направленных к одной таблице, и чем меньше таблиц участвует в запросе, тем быстрее выполняется запрос.
Описанная выше ситуация показывает, что денормализация базы данных может привести к ускорению работы Web-приложения. Платой за это является увеличение объема таблицы — вместо поля типа 工nt, необходимого для хранения внешнего ключа таблицы authors, в таблице posts размещаются две строковые переменные для хранения имени пользователя (posts.паше) и его e-mail (posts .email)»
Транзакции
Транзакция — это последовательность операторов SQL, выполняемых как единая операция, которая не прерывается другими клиентами.
То есть пока происходит работа с записями таблицы (их обновление или удаление), никто другой не может получить доступ к этим записям, так как СУБД MySQL автоматически блокирует доступ к ним.
( Замечание )
Таблицы ISAM, MylSAM и HEAP не поддерживают транзакции. В настоящий момент их поддержка осуществляется только в таблицах BDB и InnoDB.
Транзакции позволяют объединять операторы в группу и гарантировать, что все операции внутри группы будут выполнены успешно. Если часть транзакции выполняется со сбоем, результаты выполнения всех операторов транзакции до места сбоя отменяются, приводя базу данных к виду, в котором она была до выполнения транзакции.
Системы, поддерживающие возможности транзакций, часто характеризуют как обеспечивающие свойства ACID (Atomic Consistent Isolated Durable), к которым относятся:
Атомарность, Операторы транзакции формируют единый логический блок, каждый элемент которого невозможно выполнить без выполнения всех остальных элементов блока;
Целостность. База данных является целостной до и после выполнения транзакции;
□ Изоляция. Отдельные транзакции не влияют на работу друг друга;
口 Длительность, При выполнении транзакции все ее результаты сохраняются в базе данных.
Для выяснения механизма транзакций рассмотрим ситуацию, которая возможна при работе интернет-магазина, торгующего книгами. Предположим, что на складе имеется 100 экземпляров книги. При этом в один момент времени оформление покупки книги начинают два покупателя.
Первый покупатель оформляет покупку книги, при этом происходят два события: регистрируется факт покупки и обновляется количество книг, доступных на складе. Регистрация покупки разрешена, если количество книг, возвращаемое оператором select, больше нуля. Далее из этой цифры вычитается количество заказываемых покупателем книг и происходит обновление базы данных склада. Программное обеспеченье по запросу select получает количество книг на складе, равное 100.
Одновременно второй покупатель начинает оформление покупки. Запрос на выборку количества книг для него также возвращает значение 100.
После регистрации покупки книги первым покупателем число книг, оставшихся на складе, обновляется, и 100 заменяется значением 99.
После регистрации покупки книги вторым посетителем интернет-магазина также происходит обновление базы данных склада при помощи оператора update, но так как поток, в котором выполняется программа обработки покупки, ничего не знает о параллельно выполняющемся другом запросе» то в результате двух покупок остаток на складе составит 99 книг вместо 98.
Для предотвращения такой ситуации выполнение запроса остатка на складе при помощи оператора select и обновление данного значения в базе данных при помощи оператора update должны выполняться как одна транзакция* Другая ситуация, связанная с выполнением группы операций, заключается в том, что при возникновении ошибки в середине группы база данных останется в наполовину модифицированном состоянии.
Если при оплате покупки происходит перевод денежной суммы со счета клиента на счет интернет-магазина, то счет клиента должен уменьшиться на сумму sum, а счет интернет-магазина увеличится на ту же сумму:
UPDATE account SET balance = balance - зшп WHERE name = 1 client';
UPDATE account SET balance ^ balance + sum WHERE name = * bookshop*;
Если в момент завершения первого запроса и перед самым выполнением второго произойдет аварийный сбой, транзакция окажется незавершенной.
(Замечание~ Q
Ряд ситуаций можно обработать без привлечения механизма транзакций. Например, блокируя таблицу с последующей разблокировкой при помощи операторов lock tables и unlock tables. Кроме того, в описанной ситуации с интернет-магазином можно воспользоваться так называемым относительным изменением, используя запрос UPDATE store SET count = count - 1;.
По умолчанию СУБД MySQL работает в режиме auto-commit (автовыполнение), т. е. каждый отдельный оператор рассматривается как отдельная транзакция и выполняется немедленно. Для того чтобы выполнять транзакции явным образом, необходимо отключение режима автовыполнения, которое осуществляется заданием оператора begin. Завершение транзакции отмечается выполнением оператора commit. Если во время операции происходит сбой, ее результаты можно отменить с помощью оператора rollback.
( Замечание )
После выполнения одного из двух операторов commit или rollback восстанавливается тот режим, который был до выполнения оператора BEGIN.
(Замечание )
Существует еще один способ выполнения транзакций ——это установка и сброс режима автовыполнения при помощи операторов set autocommit = 1 и SET AUTOCOMMIT = О;.
Для демонстрации работы транзакций создадим таблицу forums, но в отличие от аналогичной таблицы из листинга 2.5, назначим ей тип innoDB (листинг 2.41).
:,,命"Г ?•■■ ■•■ •■■.'■■'•«■•• —.• • ■ — •..„.......; • jw.………,… ■■■•
;Листинг 2.41. Создание таблицы forums
■■■—@:♦「资裁输,
mysql> CREATE TABLE forxjms (
NOT NULL AUTO_工NCREMENT,NOT NULL AUTO_工NCREMENT,NULL, DEFAULT NULL, forum)NULL, DEFAULT NULL, forum)mysql> id__forum INT (6) mysql> name TINYTEXT, mysql> rule TEXT, mysql> logo TEXT, mysql> pos INT DEFAULT mysql> hide TINYINT(l) mysql> PRIMARY KEY (id mysql> ) TYPE=InnoDB;
NOT NULL AUTO_工NCREMENT,
NOT NULL AUTO_工NCREMENT,
NULL, DEFAULT NULL, forum)
NULL, DEFAULT NULL, forum)
Обозначим начало транзакции оператором begin, добавим в таблицу две записи и подтвердим выполнение транзакции оператором commit. После чего произведем выборку значений из таблицы (листинг 2.42).
*寬岁仝Д?А:*^У|^|У'";*1№^^М^^^^^К5|^Д^:У*№43^^М^ 勲 ^№旗岂*J^Sffv:l!^^ii'^!^"^!t^"^Xi^i!tyyj?Ay”'*N*^^b^''J^|』l*''t*『^'^lS^^ir'i^^W^**'*:
1ИСТИНГ 2.42. Выполнение транзакции
mysql> BEGIN;
mysql> INSERT INTO forums SET name = 1 Форум 工,,hide = 0, pos = 1;
mysql> INSERT INTO forums SET name ^ fФорум II1, hide = 0, pos = 2; mysql> COMMIT;
mysql> SELECT id_forumf name FROM forums;
Результат выполнения группы SQL-запросов из листинга 2.42 представлен
на рис. 2.22.
I idLforum |I idLforum |namename
I idLforum |
I idLforum |
name
name
Форум I
Форум工工
Рис. 2.22. Результат выполнения SQL-запросовиз листинга 2.42
Если запустить второй экземпляр клиента mysql, выборка до выполнения оператора commit возвратит пустое множество.
Если во время отработки транзакции происходит сбой, ее работу можно отменить посредством оператора rollback (листинг 2.43).
mysql> BEGIN;
mysql> INSERT 工NT。forums SET name = 1Форум 工工工I hide = 0# pos = 1;
mysql> INSERT INTO forums SET id_forum = 1, name = 1 Форум IV*, hide = 0;
ERROR 1062 (23000): Duplicate entry 11 * for key 1
mysql> ROLLBACK;
mysql> SEIjECT id_forum, name FROM forians;
Как видно из рис. 2.23, успешно размещенная запись Форум 工工工 была удалена из базы данных forums после выполнения оператора rollback, производящего откат таблицы к исходному состоянию.
I 1d_forurn I name ]
+- ——+一―—л—-— +
1 I форум I I2 I Форум II I
Рис. 2.23. Результат работы SQL-запросовиз листинга 2.43
Индексирование
Введение индексов в таблицах базы данных позволяет практически сразу решить проблему производительности SQL-запросов. В общем случае записи в базе данных хранятся в произвольном порядке, поэтому для поиска значения необходимо сканировать всю таблицу. Индексирование одного из столбцов позволяет отсортировать записи и быстро их находить при помощи специальных позиционирующих алгоритмов.
( Замечание )
Индексирование позволяет на порядки повысить производительность при SQL- запросах, но требует дополнительного места под индексные файлы на жестком диске. Причем размер зтмх файлов может превышать размер файла данных.
Индексирование позволяет значительно ускорить выборку из базы данных при помощи оператора select, в то же время оно замедляет работу операторов insert, delete и update. Если большая часть запросов связана со вставкой, модификацией н удалением таблиц, следует избегать индексирования.
I ЗамЕчан.е )
Часто информация добавляется, модифицируется и удаляется небольшими частями, в то время как выборки могут достигать больших объемов, поэтому в подавляющем большинстве случаев индексирование приводит к увеличению скорости выполнения запросов.
Индексы наиболее эффективны при многотабличных запросах. Пусть имеются три таблицы: first, second и third, каждая из которых содержит по одному столбцу value и 1000 записей, содержащих последовательность чисел от 1 до 1000. Тогда запрос, приведенный в листинге 2.44, вернет 1000 строк, содержащих три равных значения.
mysql> SELECT first.value, second.value, third*value
mysql> FROM first, second^ third
niysql> WHERE first.value = second.value AND first.value = third.value;
Если таблицы не индексированы, то для выполнения запроса из листинга 2.44 необходимо перебрать все возможные комбинации, число которых равно одному миллиарду (1000x1000x1000), при наличии же индекса потребуется перебрать лишь 1000 комбинаций.
СУБД MySQL позволяет создавать несколько типов индексов:
обычный индекс (index). При использовании данного типа индекса значения индексируемого столбца могут дублироваться;
口 уникальный индекс (unique, primary key). Этот тип индекса запрещает дублирование значений индексируемого столбца;
индекс полнотекстового поиска (fulltext).
( Замечание )
Индекс fuletext можно создавать только для столбцов TEXT и только для таблиц типа Му ISAM. Более подробно работа с индексом fulltext будет рассмотрена в главе 9, посвященной системе полнотекстового поиска по сайту.
Индексы можно добавить в процессе создания таблицы при помощи оператора create table или уже после создания таблицы при помощи оператора CREATE INDEX ИЛИ ALTER TABLE, КЯК ЭТО Продемонстрировано В ЛИСтинге 2.45.
..■•а>.л>><> •>••>•.•..>■••■■.•0( ■f|*>V.iaaa>>>>aaa«l«<aIU4>>>»<CMia**M4UMll^«»«Г Листинг 2,45. Добавление индекса к3 •香;“扌*■■■扌為%frTtf^^i^STi疗йТКП1f■ • \¥* * 3^?i^^»* * ^кЛтгI у^Йй^t fcM*7J 'J^vRwKTSx*..■•а>.л>><> •>••>•.•..>■••■■.•0( ■f|*>V.iaaa>>>>aaa«l«<aIU4>>>»<CMia**M4UMll^«»«Г Листинг 2,45. Добавление индекса к3 •香;“扌*■■■扌為%frTtf^^i^STi疗йТКП1f■ • \¥* * 3^?i^^»* * ^кЛтгI у^Йй^t fcM*7J 'J^vRwKTSx*"'■p^s^"'■p^s^.3®^^ ^^^^щ/ ;“ •:…:;*ЖЫЖЙООШМ1ЙЖ уже существующей таблице■
..■•а>.л>><> •>••>•.•..>■••■■.•0( ■f|*>V.iaaa>>>>aaa«l«<aIU4>>>»<CMia**M4UMll^«»«
Г Листинг 2,45. Добавление индекса к
3 •香;“扌*■■■扌為%frTtf^^i^STi疗йТКП1f■ • \¥* * 3^?i^^»* * ^кЛтгI у^Йй^t fcM*7J 'J^vRwKTSx*
..■•а>.л>><> •>••>•.•..>■••■■.•0( ■f|*>V.iaaa>>>>aaa«l«<aIU4>>>»<CMia**M4UMll^«»«
Г Листинг 2,45. Добавление индекса к
3 •香;“扌*■■■扌為%frTtf^^i^STi疗йТКП1f■ • \¥* * 3^?i^^»* * ^кЛтгI у^Йй^t fcM*7J 'J^vRwKTSx*
"'■p^s^
"'■p^s^
itiysql> ALTER TABLE table ADD INDEX index_name (column);
В листинге 2.45 к таблице table добавляется новый индекс index_name для столбца column. Причем имя индекса можно не указывать — в этом случае он получит имя индексируемого столбца.
( Замечание )
При помощи оператора create index можно создать только обычный индекс INDEX И индекс типа UNIQUE.
Индексы unique и primary key похожи, единственное их различие состоит в том, что таблица не может иметь два индекса primary key и индекс primary key, в свою очередь, не может иметь пустых значений.
С Замечание )
Удалить индексы можно при помощи операторов DROP INDEX ИЛИ ALTER TABLE.
:Листинг 2.46•'♦*3**,《・r*i* ■*•.*.♦**♦,*;『,'亦;'*',*¥■',+ ♦:Листинг 2.46•'♦*3**,《・r*i* ■*•.*.♦**♦,*;『,'亦;'*',*¥■',+ ♦При оптимизации запросов посредством создания индексов необходимо уделять внимание тому, индексирование каких столбцов позволит увеличить производительность. Так, индексирование выбираемых столбцов, следующих за оператором select, не приводит к значительному увеличению скорости выполнения SQL-запроса. Наибольшего эффекта можно добиться, индексируя столбцы, появляющиеся после оператора where. Это правило отражено в листинге 2.46.
:Листинг 2.46
•'♦*3**,《・r*i* ■*•.*.♦**♦,*;『,'亦;'*',*¥■',+ ♦
:Листинг 2.46
•'♦*3**,《・r*i* ■*•.*.♦**♦,*;『,'亦;'*',*¥■',+ ♦
mysql> SELECT column// Индексирование column — нецелесообразно
mysqA FROM table
mysql> WHERE index = exper; // Лучше проиндексировать столбец index
В SQL-запросе, приведенном в листинге 2.46, индексирование столбца column не повлияет на производительность запроса, в то время как индексирование столбца index может значительно ускорить его выполнение.
При индексировании строковых столбцов следует индексировать только часть данных, включающую первые 10—20 символов. Так, увеличение индексных файлов приводит к замедлению работы таблицы вследствие обращения к жесткому диску. Также не следует индексировать все без исключения столбцы таблицы, а выбирать лишь необходимые.
Общие вопросы оптимизации
СУБД MySQL помимо стандартных операторов SQL включает набор дополнительных, предназначенных для профилирования запросов. После каждого из запросов выводится общее время его выполнения в секундах. Так как запросы выполняются очень быстро, зачастую невозможно определить точное время выполнения запроса. В этом случае можно воспользоваться функцией benchmark (num, expr), выполняющей num раз выражение ехрг. Результат продемонстрирован на рис. 2.24.
l«ysq】A SELECT BENCHMARK(1ОООООО, 1*1); *4
I BENCHMARK(IOOOOCK), 1+1) |
+h
1 row In set (О.03 sec)
Рис. 2.24. Результат выполненияфункции BENCHMARK
Кроме функции benchmark в арсенале MySQL имеется оператор explain, позволяющий выводить информацию об индексах, которые будут использоваться при сканировании таблицы, количество строк, которые будут просканированы, и другую дополнительную информацию.
〔 Замечание )
Передача оператору EXPLAIN 8 качестве аргумента имени таблицы, аналогично оператору DESCRIBE приводит к описанию столбцов таблицы.
Несколько советов при работе с СУБД MySQL.
口 Следует сравнивать по возможности только однотипные столбцы. При сравнении индексированных столбцов разного типа — индексы игнорируются. Например, тип char(10)аналогичен char(io)и varchar(io), но отличен от char(12)и varchar(12). Также при сравнении столбцов int и bigint индексы не будут учитываться.
□ Для индексов, имеющих повторяющиеся значения, после загрузки таблицы следует выполнить команду myisamchk --analyze. Это улучшит работу встроенного оптимизатора, выполняющего оптимизацию каждого запроса.
口 Для ускорения выборки значений следует использовать оператор limit, позволяющий выводить только часть выборки.
口 Для часто изменяющихся таблиц следует избегать типов varchar, text и blob, так как работа со столбцами фиксированной длины осуществляется много быстрее по сравнению со столбцами переменной длины.
口 Для таблиц, склонных к фрагментации, необходимо время от времени проводить оптимизацию с использованием оператора optimize table.
О Не следует злоупотреблять автоматическим преобразованием типов данных. MySQL автоматически преобразует типы данных, но если есть возможность, этого следует избегать. Так, прм условии, что столбец column является целочисленным, первый запрос в листинге 2.47 выполняется быстрее второго запроса.
^КУ^Д^^^:^*1;冠置}Kijii^^^CftSS^^铝^^理‘Листинг 2.47. ЗапросыЛ *.;~」+%***К.“. ■»'*V»*«*S4'J* ■■'wVrfii t》3;汽 * а£^^* *w:、、*“*去^КУ^Д^^^:^*1;冠置}Kijii^^^CftSS^^铝^^理‘Листинг 2.47. ЗапросыЛ *.;~」+%***К.“. ■»'*V»*«*S4'J* ■■'wVrfii t》3;汽 * а£^^* *w:、、*“*去
^КУ^Д^^^:^*1;冠置}Kijii^^^CftSS^^铝^^理‘
Листинг 2.47. Запросы
Л *.;~」+%***К.“. ■»'*V»*«*S4'J* ■■'wVrfii t》3;汽 * а£^^* *w:、、*“*去
^КУ^Д^^^:^*1;冠置}Kijii^^^CftSS^^铝^^理‘
Листинг 2.47. Запросы
Л *.;~」+%***К.“. ■»'*V»*«*S4'J* ■■'wVrfii t》3;汽 * а£^^* *w:、、*“*去
mysql> SELECT * FROM table WHERE coluinn = 4;
mysql> SELECT * FROM table WHERE column =『4"
На преобразование типа затрачивается время; кроме того, если столбец column проиндексирован, это может помешать использованию индекса.
Оптимизация кода PHP при работе с MySQL
В данном разделе будут рассмотрены основные функции API для работы с MySQL из PHP-скриптов, а также приемы, направленные на оптимизацию кода PHP при работе с MySQL.
PHP и MySQL
В этом разделе приводятся описания наиболее часто используемых функций, позволяющих работать с MySQL средствами PHP. Функции, не рассмотренные в данном разделе t будут вводиться по мере надобности в других главах книги.
{Заменанве )
SQL может применяться в прикладных программах двумя способами: в виде встроенного SQL и интерфейса программирования приложений (Application Program Interface, API). Первый способ напоминает использование PHP — инструкции SQL размещаются среди кода прикладной программы. В настоящий момент такой стиль не поддерживается ни MySQL, ни PHP. Второй подход заключается в том, что программа взаимодействует с СУБД посредством совокупности функций. Именно такой подход используется при взаимодействии PHP и MySQL.
mysqLconnect
Синтаксис функции представлен ниже:
resource mysql__conneсt ([string server [, string username [# string password [r bool newslink [, int client_flags]]]]1)
Эта функция устанавливает соединение с сервером, сетевой адрес которого задается параметром server. Вторым и третьим аргументами этой функции являются имя пользователя базы данных username и его пароль password соответственно.
По умолчанию повторный вызов функции mysqi_connect О с теми же аргументами не приводит к установлению нового соединения, вместо этого функция возвращает дескриптор уже существующего соединения. Если четвертому параметру newslink присвоить значение true, будет открыто новое соединение с сервером. Параметр client__flags должен быть комбинацией из следующих констант:
口 mysql_client_compress — предписывает использование протокола сжатия при обмене информации между сервером и клиентом;
口 MYSQL_CL工Ent_工gnorE—SPbce — в SQL-запросах после имен функций разрешается использование пробелов;
□ MYSQL_CLIENT_INTERACTIVE — ЖДИТЬ interactive^timeout секунд ДО закрытия соединения, если между сервером и клиентом не происходит обмена данными.
[ Заме^ан^е )
Все аргументы функции являются необязательными. В случае нх отсутствия по умолчанию для этой функции устанавливаются следующие параметры: server = 1 localhost: 3306 \ use г name принимает значение владельца процесса сервера и password принимает пустую строку.
В случае успеха функция возвращает дескриптор соединения с сервером, при неудаче возвращает значение false (листинг 2*48).
i Листинг 248. Функция mysq工.oonngct
<?php
$dblocation ^ "localhostн; // Имя сервера
$dbussr = 11 root**;// Имя пользователя
$dbpasswd = "”;// Пароль
$dbcnx = taysql_connect ($dblocation, $dbuserf $dbpasswd);
if (!$dbcnx) // Если дескриптор равен 0, соединение не установлено {
echo(и<р>В настоящий момент сервер базы данных не доступен, поэтому корректное отображение страницы невозможно.</Р>н);
exit();
}
?>
{ Замечание )
Для подавления вывода сообщений об ошибках, генерируемых PHP в окно браузера, в листинге 2.48 перед функцией mysql_connect размещен символ 亂
Переменные $dbiocation, $dbuser н $dbpasswd хранят имя сервера, имя пользователя и пароль и, как правило, прописываются в отдельном файле (к примеру, config.php), который потом вставляется в каждый PHP-файл, в котором имеется код для работы с MySQL.
mysqLclose
Функция предназначена для разрыва соединения с сервером MySQL Ее синтаксис представлен ниже:
bool mysql^clase ([resource link_identifier])
В качестве необязательного параметра функция принимает дескриптор открытого соединения iink_identifier, если этот параметр не указан, закрывается последнее открытое соединение. Функция возвращает true в случае успеха и false при возникновении ошибки.
В листинге 2.49 приведен пример использования функции.
;Листинг 2.49. функция mysql_close
<?php
// устанавливаем co единение с базой данных
$dbcnx - taysql__connect ($dblocation, $dbuser, Sdbpasswd);
if (!$dbcnx)
{
// Выводам предупреждение
echo (**<P>B настоящий момент сервер базы данных не доступен, поэтому корректное отображение страницы невозможно.</Р>°);
// Завершаем работу в случае неудачи
exit ();
if {mysql—Close ($dbcnx)) // разрываем соединение {
echo {''Соединение с базой данных прекращено**);
}
else
{
echo(мНе удалось завершить соединение");
}
?>
mysql_select_db
bool mysql_select_db (string database^name [, resource link_xdentifier]) Использование этой функции эквивалентно вызову команды use в SQL- запросе, т. е. функция mysql_select__db выбирает базу данных для дальнейшей работы, и все последующие SQL-запросы применяются к выбранной базе данных. Функция принимает в качестве аргументов название выбираемой базы данных database_name И дескриптор соединения resource. Функция возвращает true при успешном выполнении операции и false в противном случае. Пример работы функции приведен в листинге 2.50.
二二二::二二二::号2^;部"■•诉丁?;^^.啰,与,嶼…..^x^^g^^g::. ^^^^仆 :Листинг 2.50. Функция mysql select db
二二二::
二二二::
<?php
// Код соединения с базой данных
if (! @mysql_select db ($dbname, $dbcnx))
echo ( "<P>B настоящий момент база данных не доступна, поэтому корректное отображение страницы невозможно< </Р>п );
exit();
}
?>
Имеет смысл помещать функции для соединения и выбора базы данных в тот же файл (conflg.php), где объявлены переменные с нменамн сервера, пользователя и паролем (листинг 2.51).
:Листинг 2.51. Файл config.php
<?php
$dblocation = MlocalhostH;
$dbname = "softtime";
$dbuser = "root”;
$dbpasswd ="";
$dbcnx = @mysql__connect ($dblocation, $dbuser, $dbpasswd); if (!$dbcnx)
{
echo( "<P>B настоящий момент сервер базы данных не доступен, поэтому корректное отображение страницы невозможно.</Р>н );
exit ();
}
if (I @mysql_select_db ($dbnaiue, $dbcnx)) {
echo( "<P>B настоящий момент база данных не доступна, поэтов корректное отображение страницы невозможно * </Р>п );
exit ();
}
?>
mysqLquery
Эта функция применяется для отправки серверу SQL-запросов и имеет следующий синтаксис:
resource mysql_query (string query [, resource link^identifier])
Первый аргумент функции представляет собой строку с запросом query, второй (link__identifier) — дескриптор соединения, возвращаемый функцией mysql_connect.
( Заменанме )
Если открытые соединения отсутствуют, функция пытается соединиться с СУБД, аналогично функции mysql_connect, без параметров.
При передаче запроса функции mysql_query ()точку с запятой в конце запроса, обязательную при работе с клиентом mysql, можно не ставить.
Функция возвращает дескриптор запроса в случае успеха и false в случае неудачного выполнения запроса. В листинге 2.52 показан код, с помощью которого извлекаются все строки из таблицы authors базы данных forum, при этом выводится только первая строка, возвращенная базой данных.
4 Зак. 1181
н^SSfiCiLWi*■'>?■■''**■'"■* •"■"■ _*у;;-"厘V-'V?■"'®畳5^4跋坤*:•安旦‘士"**.":"• Y - •":'У7 . •
]Листинг 2.52. Извлечение одной строки из таблицы
<?php
include ” config.php”;
$ath = mysql_query (nselect * from authors n);
if($ath)
$author = mysql_fetch_array($ath);
echo "<Ьг>имя = 11. $ author [1 name 4 • " <br> M;
echo "пароль = °.$author['pas sw1]* M<br> H;
echo "e-mail = ".$author[* email1]・"<br>”;
echo "url = n.$author[T urlT].H<br>H;
echo "ICQ = M.$author[1 icq11.H<br>M;
echo "about = ”.$author[1 about']・"<br>";
echo "photo = ”.^author['photo']."<br>H;
echo °time ="・$author('time1];
)
else
{
echo "<pxb>Error :"・mysql_erxoL)・"</b></p>";
exit{);
}
?>、
Дескриптор, возвращаемый функцией mysql_query (), используется далее для получения значений, возвращаемых СУБД. Обычно это осуществляется при ПОМОЩИ ОДНОЙ ИЗ ПЯТИ функций: mysql_result (), mysql_fetch_row (), mysql_fetch__assoc (), mysql_fetch__array () И mysql_fetch__object () *
mysql_result
Функция возвращает результат запроса выполненного функцией mysqi_query(), с ее помощью можно получить доступ к отдельному полю записи. Синтаксис функции представлен ниже:
mixed mysql_result (resource result, int row [, mixed field])
В качестве первого аргумента result функция принимает дескриптор запроса, возвращаемый функцией mysqi_query( j. Второй аргумент row представляет собой номер столбца, который необходимо вернуть. Третий необяза^ тельный параметр field представляет собой имя поля таблицы. Вывести имя автора, который первым найдется в базе данных, можно при помощи скрипта, код которого приведен в листинге 2.53.
8ШМ1М1ИМ1111ШМ^Ий1ШММ11МММйМ1Ы^^1нйДМйШ^4^йа8£йММй!1Мй8111ШШМ11МЖ^ы^шицшй1аац^1оа1>11а^шмаиамм^^
[?,Г”г253. 3yHAM" myaG_esUit У
<?php
include нconfig・ php";
$ath = mysql_query("select name from authors;M); if($ath) (
echo mysql_result ($ath, 0, 'name1);
}
else
echo M<pxb>Error: n*mysql_error ()・"v/b><p>";
exit();
)
mysql_fetch_row
Данная функция обрабатывает результаты запроса, выполненного функцией mysqi_query(), и возвращает неассоциативный массив. Синтаксис функции следующий:
array mysql_fetch_row (resource result)
В качестве единственного аргумента result функция принимает дескриптор запроса, возвращаемый функцией mysql_query(). Возвращает массив, содержащий данные обработанного ряда, или false, если рядов больше нет. При работе с данным массивом удобно использовать функцию list (), преобразующую элементы массива в переменные (листинг 2.54).
:Листинг 2.54. Применение функции mysql_fetch__row
<?php
include ^config.php";
$ath 二 mysql__query ("select * from forums;M); if($ath)
// Определяем таблицу и заголовок echo "〈table border^l>n;
echo f1<tr>
<td></td>
<td>Ha3BaMwe</td>
<td>Пpaвилa</td>
<Ёс1>ПриветстБие</Ъд>
<td>no3MUMfl</td>
<:3>Скрытый /Доступный< / td>
</tr>";
// Так как запрос возвращает несколько строк, применяем цикл while(list($id_forumf $name, $rule, $logo, $pos, $hide) = mysql_fetch__row ($ath))
{
echo "<tr>
<td>$id_forum</td>
<td>$nanie</td>
<td>$rule</td>
<td>$logo</td>
<td>$pos</td>
<td>$hide</td>
</tr>'*;
}
echo ”</table>";
}
else
{
echo n<p><b>Error: ** .raysql_error (). "</b><p>n;
exit();
}
?>
mysql_fetch__assoc
Функция mysql_fetch_assoc ()обрабатывает результат запроса и возвращает ассоциативный массив. Синтаксис функции представлен ниже:
array mysql_fetch_assoc (resource result)
В качестве единственного аргумента result функция принимает дескриптор запроса, возвращаемый функцией mysqi_query (). Возвращает массив, содержащий данные обработанного ряда, или false, если рядов больше нет. Пример использования mysql_fetch_assoc() приведен в листинге 2.55.
assocassoc«■к|«1ЧАчЧШ1Ч»|»«"#"'|^*Г»'>Ф*'*«" *■■••«•*•<*••*«■к|«1ЧАчЧШ1Ч»|»«"#"'|^*Г»'>Ф*'*«" *■■••«•*•<*••*
assoc
assoc
«■к|«1ЧАчЧШ1Ч»|»«"#"'|^*Г»'>Ф*'*«" *■■••«•*•<*••*
«■к|«1ЧАчЧШ1Ч»|»«"#"'|^*Г»'>Ф*'*«" *■■••«•*•<*••*
<?php
include ,rconfig.php";
$ath = mysql_query{иselect * from forums;n); if($ath)
{
// Определяем таблицу и заголовок
echo M<table bordej?=l>n; echo ,f<tr>
<tdx/td>
<td>Ha3BaHMe</td>
<td>npaBwia</td>
<Ъд>ПриветстБие</td>
<td>no3MUHH</td>
< t с!>Скрытый/До ступный</td>
</tr>n;
//До тех пор, пока в результате содержатся рядыг помещаем их в 〃 ассоциативный массив
// Заметка: если запрос возвращает только один ряд — нет нужда в цикле // Заметка: если вы добавите extract($ath); в начало цикла, вы сделаете //доступньми переменные $id_foruiu, $name, grille, $logo, $pos,
//$hide.
while ($author = myrsql_fetch_assoc ($ath)) {
echo "<tr〉
<td>"・ $author[*id_forum*]."v/td>
<td>n.$author['name']."</td>
<td>H.^author[1rule']."</td>
<td>**. $author「logo1 ] . '*</td> vtd>".$author{*pos,].H</td> vtd>”•$author「hide•)*n</td> </tr>,r;
)
echo "</table>”;
}
else
(
echo ,I<p><b>Error: '\mysql__error () . " v/b><p>"; exit () /
?>
mysql_fetch_array
Эта функция возвращает результаты запроса, выполненного функцией mysqi^query(), в виде массива. Причем тип массива (численный или ассоциативный) может быть выбран. Синтаксис данной функции приведен ниже: array mysql_fetch_array (resource result [, int result_type])
В качестве первого аргумента result функция принимает дескриптор запроса, возвращаемый функцией mysql_query()* Второй необязательный параметр может принимать три значения:
mysql_assoc — возврат результата работы в виде ассоциативного массива;
MYSQL_NUM — возврат результата работы в виде численного массива;
口 mysqL—Both — возврат результата работы в виде массива, содержащего как численные индексы, так и ассоциативные индексы.
( Замечание )
По умолчанию второй аргумент принимает значение mysql_both.
( Замечание )
Если функция mysql_fetch_array принимает в качестве второго аргумента константы mysql一assoc или MYSQL—NUM, то ее работа аналогична работе функций mysqlefetch_assoc И mysql_fetch__row соответственно.
При возврате ассоциативного массива в качестве индексов выступают имена столбцов таблицы, из которой производится выборка. Вот как с помощью этой функции можно вывести все строки таблицы authors (листинг 2.56).
строк из таблицыстрок из таблицы:Листинг 2.56. Извлечение всех
строк из таблицы
строк из таблицы
<?php include "config.php”;
$ath = mysql_query (^select * from authors;11); if($ath)
{
// Определяем таблицу и заголовок
echo "<table horder=l>";
echo M<tr><td>WMH</td><td>napojib</td><td>e-mail</td><td>url</td></tr>H;
// Так как запрос возвращает несколько строк, применяем цикл
while ($author = mysql__fetch__array ($ath))
echo "О
<td>H. $author [ Fame,]. M</td>
<td>H.$author「passw*]."</td>
<td>” ・ $author「email*]."</td>
<td>"・ $author[* url1).H</td>
</tr>";
}
echo "</table>";
else
{
echo n<pxb>Error: r, .mysql_error () •" v/bxp>"; exit ();
}
?>
mysqL fetch, object
Данная функция обрабатывает результат запроса функции mysqi_query()и возвращает поля таблицы в виде объекта, имена членов которого совпадают с именами полей таблицы. Синтаксис функции приведен ниже:
object mysql_fetch_object (resource result)
В качестве единственного аргумента result функция принимает дескриптор запроса, возвращаемый функцией mysqi^query(). Возвращает объект со свойствами, соответствующими колонкам в обработанном ряду или false, если рядов больше нет.
( Замечание )
Имена полей, возвращаемые этой функцией, не зависят от регистра.
В листинге 2.57 приведен пример кода, который с помощью этой функции из таблицы authors выводит имя, URL и e-mail авторов из таблицы authors.
Листинг 2.57. Функция myeql_fetch_object
<?
include "config•php";
$ath = mysql_query(иselect * from authors;**);
if($ath)
while($row = mysql_fetch_object($ath))
echo H<p>name:"・ $row->name「У/р>г,;
echo "<p>uri: ”・§row->url."</p>";
echo n<p>email: ". $row->email. ,1</p>1';
}
}
else
(
echo "<p><b>Erer: ” .mysql_error () . "</b><p>”;
exit ();
}
?>
mysql_num_fields
Функция возвращает количество полей результата запроса и имеет следующий синтаксис:
int mysql_num_fields (resource result)
В качестве единственного аргумента result функция принимает дескриптор запроса, возвращаемый функцией mysql query(),
mysql_num_ro ws
Функция возвращает количество строк, содержащихся в результате запроса, выполненного функцией mysql_query(), и имеет следующий синтаксис: int mysql_num_rows (resource result)
В качестве единственного аргумента result функция принимает дескриптор запроса, возвращаемый функцией mysql_query()・
( Заменанме )
Эта функция работает только с запросами select. Чтобы получить количество рядов, обработанных функциями insert, update, delete, следует использо* вать функцию mysql^affected^rows ().
Приемы работы с MySQL
Если в предыдущем разделе были рассмотрены стандартные функции для работы с MySQL, то в данном разделе будут рассмотрены эффективные приемы, требующиеся для написания эффективных скриптов и широко применяемые на протяжении всей книги.
Средства MySQL
Все действия, которые можно выполнить средствами MySQL, следует выполнять именно ими, чтобы минимизировать обмен между серверами MySQL и Apache, где выполняется код PHP.
При работе с последовательностями уникальных индексов не следует генерировать их в РНР-скрмпте. Пусть база данных используется для хранения результатов тестирования комплектующих частей. По результатам теста выставляется балл от 0 до 10. Тесты проводятся для различных устройств, причем для каждого из устройств можно провести произвольное количество тестов, поэтому для дальнейшего рассмотрения нам понадобится две таблицы:
口 devices — таблица, в которой хранятся названия устройств;
口 tests —— таблица, предназначенная для хранения результатов тестирования.
Операторы create, создающие эти таблицы, приведены в листинге 2.58* Таблица devices содержит два поля: id^device — номер устройства и паше — его название. Таблица tests содержит четыре поля: id_test —— по-
выставленная по резуль- id_device — Номер уст- принадлежности теста, tests индексированы свыставленная по резуль- id_device — Номер уст- принадлежности теста, tests индексированы срядковый номер теста, mark оценка устройства, татам теста, putdate — время проведения теста и ройства из таблицы devices для определения Столбцы id_device Н id_test таблиц devices И применением уникального индекса.
выставленная по резуль- id_device — Номер уст- принадлежности теста, tests индексированы с
выставленная по резуль- id_device — Номер уст- принадлежности теста, tests индексированы с
Листинг 2.58. Таблицы devices и tests;
create TABLE devices ( id_device INT(ll) NOT NULL, name VARCHAR(20) NOT NULL, UNIQUE KEY id_device (id_device| )TYPE=My工SAM;
CREATE TABLE tests 《
id_test INT(ll) NOT NULL, mark TINYINT(4) NOT NULL, putdate DATETIME NOT NULL, id_device INT(ll) NOT NULL, UNIQUE KEY id__test (id_test) )TYPE=MyISAM;
При тестировании в оценке каждого из устройств могут принимать участие
рядрядэкспертов, которые выставляют опенки. Оценки в базе данных распо-
ряд
ряд
лагаются в произвольном порядке. На рис 2.25 приведены возможные значения полей этих таблиц (для наглядности в таблице tests не приведен столбец с датой оценки).
tests
Рис. 2.25. Система оценок: таблицы tests и devices
Кроме существующих устройств, эксперты также могут вносить в таблицу devices любые другие устройства по своему усмотрению. В этом случае при выставлении оценок возможны два варианта:
口 устройство еще не существует в таблице devices, и при выставлении оценки необходимо добавить запись как в таблицу tests, так и в таблицу devices;
□ устройство уже существует в таблице devices, и необходимо обновить запись только в таблицу tests.
В листинге 2.59 приведен возможный код для добавления записей.
<?php
// Соединяемся с базой данных require_once (11 config. php”);
// Выясняем, имеется ли в таблице devices устройство // с именем $device
$ query = *'SELECT * FROM devices WHERE name=1 $device1";
$dev = mysql_query ($query);
if($dev)
{
// Если функция mysql_num_rоws возвращает значение больше
// нулл, то запись для устройства с именем $device в
// таблице devices уже имеется
if (mysql_ntnn_rows ($fls) >0)
{
$devices = mysql_fetch_array($fls);
$id_device = $devices[1id_device1];
}
// Если функция mysql_num_rows возвращает значение 0,
// следовательно, в таблице devices отсутствует запись для
// устройства с именем $device else
{
// Необходимо добавить запись для нового устройства, при этом
// его порядковый номер должен отличаться от остальных
// устройств. Для этого необходимо сгенерировать уникальное 〃 значение *
$quary = иSELECT MAX(id_device) AS id_device FROM devicesH;
$unq = mysql_query($query);
if($unq)
{
// Получаем максимальный элемент столбца id_device
// таблицы devices и увеличиваем его значение на единицу
$dev = mysql_fetch_array($unq);
$id__device = $dev [ * id__device * ] + 1;
// Добавляем в таблицу devices запись о новом устройстве mysql_query("INSERT INTO devices VALUES ($id_device, '$name*)°);
else { echo H<pxb>Error: H .mysql__error ()・”《/b>vp>”; exit();
}
}
〃 После того как получено значение id_device, приступаем
// к выставлению оценки устройству $value в таблице tests.
// Для размещения в таблице tests опять требуется сгенерировать
// уникальное значение для столбца id_test
$ query = n SELECT MAX(id_test) AS id_test ГЯ(Ж tests*1;
$tst = mysql_query ($query);
if($tst)
{
// Получаем максимальный элемент столбца id_test
// таблицы tests и увеличиваем его значение на единицу
$test = mysql_fetch_array ($tst);
$id_test = $test[1id_test'] + 1;
// Добавляем в таблицу tests запись 。 новой оценке
mysql__query (н INSERT INTO tests
VALUES ($id__test, lvalue1, NOW(), $id_device) и);
}
else
{
echo 'ЧрХ^Еггог: " .mysql_error {) . "</b>vp>";
exit ();
}
}
else
{
echo l,<p><b>Error: "»mysql_error () . ”</bXp>"; exit();
}
?>
В листинге 2.59 для выяснения максимального значения индексированных столбцов применяется SQL-зарос "select MAX(id_test) as id_test from tests'*. Здесь max является встроенной функцией MySQL, возврашающей
максимальное значение столбца, а оператор as применяется для изменения имени столбца с громоздкого MAX(id_test)на более удобное id_test.
Приведенный в листинге 2.59 код является типичным примером неоптими- зированного кода PHP. Генерация новых значений осуществляется в теле скрипта; кроме того, для этого используется дополнительный запрос select к базе данных для выяснения максимального значения ключа id test.
Значительно улучшить производительность и структуру кода можно, снабдив столбцы id_device И id_test таблиц devices И tests атрибутом AUTO_工ncrement. Изменить структуру таблиц можно, воспользовавшись оператором ALTER (ЛИСТИНГ 2.60),
з:士$*^^g第:^^賞** 銓酬MY^^yV 'П^Г^г 坤/ ",, V":仔皆 kJ ■■■t"""з:士$*^^g第:^^賞** 銓酬MY^^yV 'П^Г^г 坤/ ",, V":仔皆 kJ ■■■t""" teats^*« ^^5^1 ^i^Ji^^Avf£•/***W^^^^4w r J» ^i;稔.*,・ «II t1i?2」3 f^^a、.,工\>;“*.*,】.『.*、“.“**1* •; ,;*“**工;.*+.*.,£,『;* C;*L兩.*;. ”"2 i teats^*« ^^5^1 ^i^Ji^^Avf£•/***W^^^^4w r J» ^i;稔.*,・ «II t1i?2」3 f^^a、.,工\>;“*.*,】.『.*、“.“**1* •; ,;*“**工;.*+.*.,£,『;* C;*L兩.*;. ”"2 i
з:士$*^^g第:^^賞** 銓酬MY^^yV 'П^Г^г 坤/ ",, V":仔皆 kJ ■■■t"""
з:士$*^^g第:^^賞** 銓酬MY^^yV 'П^Г^г 坤/ ",, V":仔皆 kJ ■■■t"""
teats
^*« ^^5^1 ^i^Ji^^Avf£•/***W^^^^4w r J» ^i;稔.*,・ «II t1i?2」3 f^^a、.,工\>;“*.*,】.『.*、“.“**1* •; ,;*“**工;.*+.*.,£,『;* C;*L兩.*;. ”"2 i
teats
^*« ^^5^1 ^i^Ji^^Avf£•/***W^^^^4w r J» ^i;稔.*,・ «II t1i?2」3 f^^a、.,工\>;“*.*,】.『.*、“.“**1* •; ,;*“**工;.*+.*.,£,『;* C;*L兩.*;. ”"2 i
ALTER TABLE tests
CHANGE id_test id_test 工NT(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE devices
CHANGE id^device id_device INT(ll) NOT NULL AUTO_INCREMENT;
После изменения структуры таблиц при запросе insert в качестве значений id_device и id test можно указывать значения 0 или null ——СУБД MySQL автоматически присвоит значение для столбцов, равное увеличенному на единицу максимальному значению.
: Замечание )
Столбцу с атрибутом auto_increment можно присваивать значения как самостоятельно, так и автоматически, передавая значение 0 или NULL*
На первый взгляд может показаться, что теряется преимущество контроля над установкой произвольного значения номера. При генерации порядкового номера в PHP-скрипте из листинга 2.59 следующему номеру можно было присвоить значение, равное "максимальное плюс 10", зарезервировав тем самым несколько номеров для дальнейшего использозания или отсчитывая порядковые номера через 10.
Кроме того, значение $id_device, получаемое при вставке записи, соответствующей новому устройству, далее используется при формировании SQL- запроса на вставку оценки. Если значение $id_devlce будет генерировать MySQL, то как узнать его из РНР-скрипта?
Решить эти проблемы можно средствами MySQL, воспользовавшись функ^ цией last_insert.工D(), возвращающей текущее значение auto_increment, полученное за время текущего сеанса.
( Замечание )
Если во время текущего сеанса не было сгенерировано ни одного значения AUTO_ 工 NCREMENT, ФУНКЦИЯ LAST_INSERT_ID () вернет 0.
Другим решением проблемы может служить использование функции PHP mysqi_insert_id () f также возвращающей последнее сгенерированное auto_increment значение.
( Замечание ]
В таблице может объявляться только один столбец, снабженный атрибутом auto_iNcrement. Поэтому всегда однозначно понятно, к какому столбцу имеют отношение функции LAST_INSERT_ID () иmysql_insert_id().
Оптимизированный вариант листинга 2.59 представлен в листинге 2.61.
<?php
// Соединяемся с базой данных require__once (**config.php”);
// Выясняем, имеется ли в таблице devices устройство
// с именем $device
$query = nSELECT * FROM devices WHERE nam&='$device'";
$dev = mysql_query ($query);
if($dev)
{
// Если функция mysql_nvm_rows возвращает значение больше
// нуля, то запись для устройства с именем ^device в
// таблице devices уже имеется
if (mysql_num_rows ($fls) >0)
{
$devices = mysql__fetch_array ($fIs);
$id_device = ^devices[*id_device*];
}
// Если функция mysql_nim_rоws возвращает значение 0, // следовательно f в таблице devices отсутствует запись для // устройства с именем $device else
<
// Добавляем в таблицу devices запись о новом устройстве mysq工 query(нINSERT INTO devices VALUES (NULL, "name,)”);
$id_device = mysql_insert_id ();
}
// После того как получено значение id^device, приступаем
// к выставлению оценки устройству $value в таблице tests.
// Добавляем в таблицу tests запись о новой оценке
mysql_query("INSERT INTO tests
VALUES (NULL, lvalue1, NOW(), $id___device)11);
}
else
{
echo n<p><b>Error: "•mysql_error()・ ”</b><p>"; exit ();
}
?>
Как видно из листинга 2.61, код сократился более чем в два раза, а вместо пяти SQL-запросов выполняются только три.
После того как оценки экспертов выставлены каждому из устройств таблицы devices, логичным шагом будет выяснение средней оценки для каждого устройства. В листинге 2.62 представлен один из вариантов скрипта, предоставляющего такую информацию.
;Листинг 2.62. Таблица средних оценок
<?php
// Делаем выборку для всех устройств, записи для которых присутствуют
// в таблице devices
$query_device = "SELECT * FROM devices11;
$dev = mysql_query ($query_device);
// Если запрос выполнен успешно, подсчитываем среднюю оценку для каждого // из устройств
if($dev)
<
// Выводим заголовок таблицы
echo УаЬЮ";
while ($device = mysql__fetch_array ($dev))
{
// Осуществляем запрос к таблице tests
$ query test 二 ° SELECT iriark
FROM tests
WHERE id_device=".$device['id_device'];
$tst = mysql_query($query_test); if($tst) {
$mark = 0;
// Осуществляем вычисление средней оценки
while ($mark += mysql_result ($tst));
echo ri<tr><td>H. ^device [1 name1 ] . ri</tdxtd>$mark</td></tr>**;
}
else
{
echo **<pxb>Error: '• .mysql_error () * "</b><p>M; exit();
}
}
// Выводим завершение таблицы echo H</table>";
}
else
{
echo n<p><b>Error: п.тузц1_еггог().М</Ь><р>и;
exit О ;
В листинге 2.62 происходят выборка наименований устройств н подсчет средних оценок путем формирования и выполнения запросов в цикле. Таким образом, имеет место вложенный цикл, и работа с базой данных MySQL не оптимальна. Воспользовавшись встроенной функцией AVG(expr) для подсчета среднего значения столбца, ехрг — для формирования таблицы средних оценок, можно обойтись одним многотабличным запросом (листинг 2.63).
9•…•'干…二.:":.…、中:…"•炉•”•…•戸…一•
Листинг 2.63, Оптимальный вывод таблицы средних оценок
<?php
// Делаем выборку для всех устройств, записи для которых присутствуют //в таблице devices
$query_deviсе =* "SELECT devices.name AS name, AVG (tests .mark) AS mark
FROM devices, tests
WHERE devices.id__device = tests.id_device
GROUP BY devices.di_deviceM;
$dev = mysql_query ($query__device);
if($dev)
(
// Выводам заголовок таблицы
echo n<table>°;
while($device - mysql_fetch_array($dev)) {
echo "vtr>
<td>".$device[*name1].M</td> '<td>r\$device [ ,mrk, ]. n</td> </tr>";
// Выводам завершение таблицы
echo 'У/table〉";
}
else■
{
echo **<p><b>Error: ” ・ mysql_error () . "</bXp>”; exit();
}
?>
Как видно из листинга 2*63, несмотря на то, что происходит применение двухтабличного запроса, в результате оптимизации кода удалось избавиться от вложенного цикла и л-го числа запросов select, выполняющихся в цикле.
Проверка результатов запроса на значение NULL
Определить, возвращает ли оператор select значение null, можно при помощи функции isset о.
PHP интерпретирует значения переменных, которым присвоено значение NULL так, как если бы их значения не были установленными.
Пример проверки на значения null для выборки из таблицы test, содержащей три столбца (first, second и third), приведен в листинге 2.64.
¥mWWtMfWWTAHWMf#tVWi^lf*?tfWliTfflB?tWftW^MIWi1MWftb把吋?^f^ll肥!SI^MWI^i^Wyjyiiy^tfttHiHMIftMiWHn^ftttKil^ri^H^ttytf^WW^tffl
;Листинг 2.64. Проверка на значеия null фнукцией 土ssot
<?php
// Формируем и выполняем SQL-запрос
$query = "SELECT first, second, third FROM test”;
$tst = mysql_query ($query);
// Проверяем успешность выполнения запроса if($tst)
(
echo **<pxb>Error: n,mysql_error() • "</b><p>”; exit();
}
〃 Выводам заголовок таблицы
echo rr<table>rt;
while(list($first, $second, $third) = mysql_fetch__row($tst)) (
echo "<tr>";
if (lisset ($first)) echo "<td>-</td>,';
else echo **<td>$first</td>11;
if (! isset ($second)) echo **<td>-</td>n;
else echo M<td>$second</td>n;
if (! isset ($third)) echo **<td>-</td>r,;
else echo r,<td>$third</td>H; echo "</tr>”;
}
// Выводим завершение таблицы
echo ,1</table>,1;
?>
( Замечанье )
Использование функции empty () в данном случае будет не точным, так как она возвращает true и для значения null, и для пустой строки (*•").
Аналогично функции issetо, проверку на пустое значение можно производить с помощью оператора идентичности === и константы null, как это продемонстрировано в листинге 2.65.
Листинг 2.65. Проверка на пустые значения с помощью оператора идентичности j
<?php
// Формируем и// Формируем ивыполняем SQL-запросвыполняем SQL-запрос
// Формируем и
// Формируем и
выполняем SQL-запрос
выполняем SQL-запрос
$ query = "SELECT first, second, third FROM test1*;
$tst = mysql_query($query);
// Провернем успешность выполнения запроса
if($tst)
echo M<pxb>Error: *' .mysql_error {) . "</bXp>"; exit» ;
}
// Выводим заголовок таблицы
echo n<table>**;
while(list($first, $second, $third) = mysql_fetch__row($tst)) {
echo ”<tr>";
if($first = NULL) echo "<td>-</td>”;
else echo M<tci>$first</td>M;
if($second === NULL) echo H<td>-</td>";
else echo r,<td>$second</td>M;
if($third = NULL) echo ”<td>-</td>";
else echo n<td>$third</td>n;
echo "</tr>";
}
// Выводим завершение таблицы
echo ”</table〉";
?>
Близко к этой задаче лежит задача определения пустых выборок из таблиц. Так, код в листинге 2.65 выведет таблицу с единственной строкой, содержащей три прочерка, если в таблице нет ни одной записи. В таком случае логично было бы вообще не выводить строк в таблице. Решить эту проблему можно при помощи уже упоминавшейся функции mysqi_num_rows(), возвращающей число строк в результатах запроса (листинг 2,66).
;Листинг 2.66. Запрос на число строк
<?php
// Формируем и выполняем SQL-запрос
$ query = ** SELECT first, second, third FROM test”;
$tst = mysql_query ($query);
// Проверяем успешность выполнения запроса if(!$tst)
echo "<pxb>Error: r, .mysql_error () . M</b><p>,r; exit{);
}
// Проверяем, содержит ли результат хоть одну строку
if (mysql_num_rows ($tst) >0) {
// Выводим заголовок таблицы
echo n<table>n;
while(list($first, $second, $third} = mysql_fetch_row($tst)) {
echo ”<tc";
if ($first == NULL) echo *,<td>^</tci>и;
else echo "<td>$first</td>°;
if ($second = NULL) echo l,<td>-</td>n;*
else echo M<td>$second</td>";
i£($third ^== NULL) echo ,,<td>-</td>";
else echo ''<td>$third</1d>n;
echo M</tr>°;
}
/ / Выводим завершение таблицы echo M</table>M;
Избежание повторных запросов
Пусть имеется таблица members, содержащая имена (name), электронные адреса (email) и домашние странички (uri) членов виртуального клуба. При этом поле паше является обязательным, а поля email и uri ——опииональ- ными, и в случае отсутствия у посетителя email или uri принимают значения null. Оператор create, создающий данную таблицу, представлен в листинге 2.67.
ТаблицаТаблица班:.^^5^:£m 5 Листинг 2.67.
Таблица
Таблица
CREATE TABLE members (
name VARCHAR{20) NOT NULL DEFAULT email TINYTEXT, url TINYTEXT
)TYPE=MyISAM;
Одна из задач, стоящая перед администратором клуба, состоит в выводе двух списков: членов клуба, имеющих e-mail, н членов клуба, имеющих свои виртуальные странички. Один из вариантов такого скрипта, выполняющего эту задачу, приведен в листинге 2.68.
<?php
// I таблица
// Формируем и выполняем SQL-запрос
$ query ^ 11 SELECT name, email FROM members";
$mem = mysql_query ($query);
// Проверяем успешность выполнения запроса if (! $mem)
(
echo *'<p><b>Error: H .mysql_error () . "</bxp>” ; exit О;
// Проверяем, содержит ли результат хоть одну строку
if (mysql_num_rоws ($mem) >0)
{
/ / Выводим заголовок таблицы
echo "<table>,r;
while (list ($name, $ email) = mysql_fetch_row($mem))
if ($email !== NULL) echo n<tr><td>$naine</td><td>$einail</td></tr>"; 〃 Выводим завершение таблицы echo "</tablB>”;
// II таблица
// Формируем и выполняем SQL-запрос
$query = "SELECT name, url FROM membersM;
$mwm = mysql_query ($query);
// Проверяем успешность выполнения запроса if (! $mem)
{
echo **<pXb>Error: 11 .mysql_error () . и</Ьхр>и;
exit ();
}
// Проверяем, содержит ли результат хоть одну строку
if (mysql_nurn_rows ($m=) >0)
〃 Выводим заголовок таблицы
echo "〈table”;
while (list ($name, $url) = mysql_fetch_row ($url))
if ($email != NULL) echo t,<tr><td>$name<;/td><td>$url</td></tr>и;
// Выводим завершение таблицы echo п</ЪаЫе>и;
?>
Из листинга 2.68 видно, что два раза подряд выполняется практически одинаковый код. Первое, что приходит в голову: поместить после оператора select первого SQL-запроса поле uri и использовать дескриптор запроса повторно, без выполнения второго SQL-запроса. Однако повторно использовать дескриптор $mem без дополнительного кода не удастся, так как внутренний указатель находится на последнем элементе. Решить проблему мо* жет функция mysqi_data_se£k(), способная перемещать внутренний указатель и имеющая следующий синтаксис:
bool mysql_data_seek (resource result__identifier, int row_number)
Функция принимает два параметра, первый из которых resui trident ifier — дескриптор запроса, возвращаемый функцией mysqi_query(), второй — смещение внутреннего указателя относительно первой позиции. Функция возвращает true в случае успеха и false в случае неудачи.
С использованием этой функции код в листинге 2.68 можно переписать следующим образом (листинг 2.69).
:Листинг 2.69. Вывод списков с помощью функции mysql_data_seek
<?php
// Формируем и выполняем SQL-эапрос
$ query = "SELECT name, email, url FROM members1*;
$mem = mysql_query ($query);
// Проверяем успешность вьшолнения запроса if (! $mem)
I
echo ,r<pxb>Error: H,mysql_error() • "</b><p>"; exit();
}
// Проверяем, содержит ли результат хоть одну строку
if (mysql^ium_rcws ($mem) >0)
// I таблица
// Выводим заголовок таблицы echo "vtable>";
while (list ($name, $emailf $url) = mysql_fetch_row($meni))
if($email !== NULL) echo n<tr><td>$nane</td><td>$email</td></tr>H; 〃 Выводам завершение таблицы echo ,*</table>11;
// Устанавливаем внутренний указатель в начало mysql_data__seek ($mem, 0);
// II таблица
// Выводим заголовок таблицы
echo "〈table〉”;
while(list($name, $email# $url) = mysql__fetch_row($mem))
if ($email != NULL) echo M<trxtd>$name</td><td>$url</td></tr>n;
// Выводим завершение таблицы echo "</table>1*;
Как видно, для вывода обеих таблиц оказалось достаточно одного SQL- запроса.
Удаление связанных данных
Для рассмотрения этого раздела нам понадобятся ранее созданные в листинге 2.58 таблицы tests и devices. В случае таких связанных таблиц удаление из таблицы devices должно обязательно сопровождаться удалением всех тестов, связанных с этим устройством, из таблицы tests. В противном случае в таблице будут оставаться данные, не связанные ни с каким из устройств из таблицы devices. Выполнить такое удаление может скрипт, приведенный в листинге 2.70, который принимает в качестве параметра индекс удаляемого из таблицы устройства $id_device.
Листинг 2.70. Удаление из связанных таблиц
<?php
// Удаляем все тесты из таблицы tests, относящиеся к устройству
// $id_device
if(myEq】_query("DELETE FROM tests WHERE id_device = $id_device'*))
mysql_query("DELETE FROM devices WHERE id_device = $id_device ”); }
?>
Работа c phpMyAdmin
Вы когда-нибудь озадачивались поиском клиентской программы для работы с СУБД MySQL, обладающей графическим интерфейсом? Даже при виртуозном владении SQL и PHP работа по проектированию, построению н обновлению базы данных занимает массу времени, если она выполняется при помощи штатных средств, входящих в дистрибутив MySQL
Значительно облегчить жизнь может Web-интерфейс для работы с СУБД MySQL — phpMyAdmin. Это приложение, написанное на PHP, может полностью управлять как целым сервером MySQL, так и отдельной базой данных или таблицей» быстро и легко осуществлять разнообразные запросы. Причем для работы с MySQL не требуется даже знание SQL, а интерфейс приложения переведен на 47 языков, в том числе и на русский. Поэтому приложение phpMyAdmin быстро стало де-факто стандартной панелью управления у российских хост-провайдеров.
Приложение phpMyAdmin может решать разнообразные задачи, обыкновенно выполняемые при помощи SQL-запросов и средств администрирования MySQL:
口 создание и удаление базы данных;
口 создание, копирование, удаление, переименование и исправление таблицы;
администрирование таблиц;
口 удаление, исправление и добавление полей в уже существующие таблицы;
口 выполнение любых SQL-запросов, в том числе и в пакетном режиме;
управление ключами;
загрузку информации в таблицы из текстового файла;
создание и загрузку дамлов таблиц;
преобразование данных таблиц в форматы CVS, XML и Latex;
口 управление системными процессами сервера;
口 управление учетными записями пользователей и их правами доступа;
口 проверку целостности таблиц MylSAM;
О создание PDF-изображений структуры базы данных;
глобальный поиск по базе данных или ее части;
口 поддержку таблиц типа innoDB и внешних ключей.
:З.менание )
Последнюю версию phpMyAdmin вы можете загрузить с официального сайта этого Web-приложения по адресу http://www.phpmyadmin.net/home_page/ downloads.php.
Данная глава была написана с использованием версии phpMyAdmin 2.5*5- plL Требования, предъявляемые данной версией, следующие:
口 PHP версии не ниже 4.1.0 (для отображения динамических JPEG-пик» то грамм требуется поддержка библиотеки GD2);
MySQL версии не ниже 3.23.32;
Web-браузер*
Инсталляция phpMyAdmin
Инсталляция phpMyAdmin протекает в несколько этапов.
Распакуйте архив с дистрибутивом в поддиректорию корневого каталога вашего Web-сервера (Пусть это будет phpMyAdmin). Корневой каталог сервера определяется директивой DoaumentRoot, описанной в главе 丄 Если вы не имеете прямого доступа к корневому каталогу (сервер расположен на удаленной машине), поместите файлы в директорию на локальной машине и затем, после шага 3, переместите директорию на Web- сервер, например, воспользовавшись FTP.
Откройте файл config л nc.php и измените значения хоста, имя пользователя базы данных, его пароль и режим идентификации согласно вашему окружению. Здесь "хост" означает сервер, где расположена СУБД MySQL. В случае локальной машины, можно ввести localhost. Также необходимо исправить значение для $cfд[1PmaAbsoluteUri *] для указания точного сетевого адреса phpMyAdmin: если настройка Web-приложения происходит на локальной машнне и архив был распакован в папку phpMyAdmin, то следует изменить значение этой директивы на http:// localhost/phpMyAdmin. Более подробно эти директивы будут рассмотрены в следующем разделе, посвященном настройке phpMyAdmin.
Желательно защитить директорию, в которой установлен phpMyAdmin, например, при помощи HTTP-AUTH, если только phpMyAdmin не используется на локальной машине или в сети Интранет либо используется HTTP или cookie-идентификация.
Теперь можно открыть в браузере главную страницу phpMyAdmin. Если инсталляция производилась на локальной машине (localhost) в подката-
лог phpMyAdmin корневого каталога Web-сервера, то следует воспользоваться URL: http://localhost/phpMyAdmin/index.php* При этом в браузере должна загрузиться главная страница панели управления, показанная на рис. 2.26.
Рис. 2.26. Главная страница панели управлений phpMyAdmin
Для того чтобы настроить все элементы phpMyAdmin (закладки, комментарии, журнал SQL-запросов, генерацию PDF-документов, преобразование содержимого полей и т. д.), необходимо создать базу данных phpmy- admin и разместить в ней вспомогательные таблицы. Для этого необходимо выполнить SQL-запросы, помещенные в файл scripts/ create^tables.sqL На самом деле уже сейчас можно использовать phpMyAdmin для создания баз данных и таблиц. Перейдите по гиперссылке Окно запроса в левом нижнем углу панели управления. Во всплывающем окне, изображенном на рис. 2.27, в текстовое поле поместите содержимое файла create_t3bles.sql и нажмите кнопку Пошел.
"7"7Рис. 2.27. Окно запросаРис. 2.27. Окно запроса
"7
"7
Рис. 2.27. Окно запроса
Рис. 2.27. Окно запроса
I Замечание )
Если гиперссылки на главной страницы панели управления phpMyAdmin не работают, следует еще раз проверить значение директивы $cf g [1 PmaAbsoluteUri『]. Неправильно указанный сетевой путь является наиболее распространенной ошибкой конфигурирования phpMyAdmin.
После SQL-запросов из файла create_tables.sql вы должны выставить имена таблиц в конфигурационном файле config.inc.php. Если имена таблиц не менялись, следует исправить значения директив в соответствии с листингом 2.72.
,、.".、:.…) ,假(!冷у必安] ■_,,《*幺》;4「,'/门 ",,.*."*,*《学■y+W,“y^ 匸"* V,.W a?…寸粵”
Листинг 2.72. Фрагмент файла config.inc.php
&■;;”;.;♦;'♦ ...、…;…♦•;«」•;『.;■亲;.;.„j?h :•; •…;…<*:,,““^i*;;……Яй¥“,“:.%3^^%;・
// Название базы данных, где расположены вспомогательные таблицы
// phpMyAdmin. По умолчанию это phpmyadinin.
$cfg [ * Servers * ] ($i] [ fpmadb' ]= 'phpmyadmin1;
// Далее- идут директивы с именами таблиц, расположенных в базе данных / / phpmyadmin.
$cfg [ * Servers * ] [$i] [ fbookmarktable1 ]= ' pma_bookmark,;
$cfg[* Servers1][$i][1 relation*]= rpma_relation1;
=,pma_table_info,;=* pma_t abl e_coor ds1;='pina__pd.f_pages';=1 pma^col umn_i nfo1;=t^na_histOEyl=,pma_table_info,;=* pma_t abl e_coor ds1;='pina__pd.f_pages';=1 pma^col umn_i nfo1;=t^na_histOEyl§cfg [' Servers * ] [9i] 11 table__info,] $cfg[1 Servers1]【$»]['table_coords1]
=,pma_table_info,;
=* pma_t abl e_coor ds1;
='pina__pd.f_pages';
=1 pma^col umn_i nfo1;
=t^na_histOEyl
=,pma_table_info,;
=* pma_t abl e_coor ds1;
='pina__pd.f_pages';
=1 pma^col umn_i nfo1;
=t^na_histOEyl
$cfg[1 Servers」【$ i]厂 pdf_pages1) $cfg[1 Severs1 ] [$i] [ ,colunin_info,] $cfg[1 Servers1][$i]「history1]
Приложение phpMyAdmin может применяться для администрирования нескольких серверов MySQL, поэтому будьте внимательны: исправление для одного сервера не коснется другого сервера. Одна из ошибок конфигурирования заключается в изменении перечисленных выше директив для одного из следующих серверов, в то время как для первого сервера директивы остаются неустановленными.
: Замечание )
Конфигурационный файл config.inc.php является рЬр<криптом( и его синтаксис должен соответствовать синтаксису обычных PHP-скриптов. Поэтому начало следующего сервера отмечается оператором инкремента переменной 帛丄 Авторы phpMyAdmin настоятельно рекомендуют не использовать абсолютные значения серверов по типу: $cfg [1 Servers * ] [0], $cfg [1 Servers1 ] [1] и т. д.
Если вы используете phpMyAdmin для управления базой данных, расположенной на локальной машине, то оставшуюся часть этого раздела, которая будет посвящена проблемам безопасности, можно пропустить. Однако если требуется обеспечить многопользовательский доступ к базе данных, чтобы пользователям были доступны только их собственные базы данных н oipamv чен доступ к другим, необходимо настроить идентификацию пользователей.
Приложение phpMyAdmin поддерживает три способа идентификации пользователей:
口 с помощью записи имени пользователя и его пароля в конфигурационном файле config.inc.php;
О с помощью cookie;
□ с помощью http-заголовков.
При организации доступа по первому варианту файл config.inc.php необходимо исправить согласно листингу 2.73.
;Листинг 2.73. Идентификация пользователей по конфигурационному файлу
ЗЙ1^^^. .. ^^...」;;. ;.;...;;1 ..・..;*;..;占談,;Q •••;*・.;itf•> N *•*•=•;•♦・••••*;;■** ^^・♦ X» ••♦•,、• а. •♦・•*■■■■'•、♦♦,**・,*Sill**y;***'2?*,*・:*,a* •”■■■•■••♦*•*♦" ;**";"•*•"$**;*£;***;■%
$cfg[ Servers* ] [$i] [1 auth_type1 ]= ‘config’;
$cfg[1 Serversf)I$i](1 user1]= *user1;
$cfg[1 Servers1) [$i] [password* ]= •password1;
Первая директива $cfg【tservcrsi] [$i] [*auth_type,J, сообщает, что доступ к базе данных будет осуществляться с использованием информации из конфигурационного файла. В качестве имени пользователя применяется значение директивы
$cfg['Servers1] [$i1[1 user1J,
а пароля —
$cfg[Servers,][$i] ['password1].
Если потребуется ввести еще одного пользователя, необходимо в конфнгу- рационном файле настроить следующий сервер MySQL, директивы которого следуют за $i++. Для этого нужно продублировать всю информацию, относящуюся к первому серверу, за исключением имени пользователя и его пароля.
Ничто не мешает настроить доступ к совершенно другому серверу MySQL, передав директиве $cfg[ Servers*] [$i] fhosf] имя хоста сервера и выставив остальные параметры. При этом на главной странице сервера появится окно выбора сервера, как это продемонстрировано на рис. 2.28.
參 phpMy/Ulmin на localhost - phpMyAdmin 23・Fpll - Microsoft Internet Explorer
Рис. 2.28. При настройке нескольких серверов появляется окно выбора сервера
Такой способ разграничения доступа к базам данных является достаточно условным и предназначен скорее для однопользовательского доступа к нескольким серверам MySQL.
: Заменанме )
Для многопользовательского доступа к phpMyAdmin необходимо ввести пользователей и назначить им привилегии при помощи оператора GRANT или phpMyAdmin,
Больший интерес представляет идентификация пользователей по cookie. Для перехода в этот режим необходимо изменить значение директивы $cfg[fServers1] [$i] [ *auth_type1 ] на 'cookie*'. При ЭТОМ ИМЯ пользователя и его пароль в конфигурационном файле прописывать не обязательно.
Начиная с версии phpMyAdmin 252, при использовании cookie-идентификации применяется так называемый blowfish-алгоритм шифрации паролей. Если хотя бы одна из серверных конфигураций использует идентификацию по cookie, директиве $cfg [ *blowfish_secret1 ] необходимо присвоить ключевую фразу, которая будет применена в алгоритме, В противном случае при входе phpMyAdmin выдаст предупреждение: "Конфигурационному файлу сейчас нужна секретная фраза (blowfish_secret)".
Рис. 2.29. Окно входа в phpMyAdmin при использовании режима идентификации по cookie
Если все выполнено правильно, то при обращении к phpMyAdmin браузер предложит ввести имя пользователя и его пароль (рис. 2.29).
Последний способ идентификации пользователя связан с передачей имени пользователя и его пароля через HTTP-заголовки. Для перевода приложения phpMyAdmin в этот режим работы необходимо директиве $cfg[ •Servers1 J [$i] [1 auth_type1 ] присвоить значение "http".
[ Замечание J
Этот режим работает только при использовании PHP в качестве модуля сервера Apache.
При этом идентификация будет производиться средствами сервера Apache, так же как при защите файлов и папок, описанных в главе /, во всплывающем окошке будет предложено ввести имя пользователя и пароль (рис. 230).
Рис. 2.30. Окно входа при использовании режима http
На этом инсталляция phpMyAdmin закончена, в следующем разделе описываются основные конфигурационные директивы, позволяющие настроить это Web-приложение согласно вашим требованиям.
Конфигурирование phpMyAdmin
Конфигурирование phpMyAdmin осуществляется путем редактирования многочисленных настроек в файле config.inc.php, который является PHP-
скриптом и подчиняется синтаксису PHP. В этом разделе мы рассмотрим основные директивы, предназначенные для кон фи гури рован ия phpMyAdmin.
$cfg[1 РтаAbsoluteUri1] string
Устанавливает полный сетевой адрес (URL) к phpMyAdmin. Например, http://www.your_web*net/path_to_your_phpMyAdmin_directory/ или http:// localhost/phpMyAdmin/.
(Заменан.е )
Не забывайте добавлять слэш в конце URL кроме того, данная директива должна содержать символы, допустимые для использования в URL.
Начиная с версии 2.3.0, можно попытаться оставить эту директиву пустой, так как phpMyAdmin попытается автоматически определить правильное значение для этой директивы.
$cfg[1PmaAbsoluteUri—DisableWarning'] boolean
По умолчанию когда директива $cfg [1 PmaAbsoluteUri • ] остается неустановленной и система автоматически определяет сетевой адрес к phpMyAdmin, на главной панели выводится предупреждение, напоминающее о необходимости установки значения для $cfg[ ^maAbsoluteUri1 ]. Однако если тестирование авто мати чес кого определения показало, что все прекрасно работает, можно установить данную директиву в true для подавления вывода предупреждения.
$cfg[т blowfish_secret *] string
Начиная с версии 2.5.2, при использовании cookie-идентификации применяется blowfish-алгоритм шифрации паролей. Если хотя бы одна из серверных конфигураций использует cookie-идентификацию, присвойте этой директиве ключевую фразу, которая будет применяться в blowfish -ал горитме шифрации, защищающем пароли.
$cfg[1 Servers 1] array
Начиная с версии 1.4.2, phpMyAdmin поддерживает администрирование нескольких серверов MySQL. Массив $cfg[ Servers1 ] предназначен для хранения конфигурационной информации отдельных серверов. Например, первая директива $с£д и Servers 1 ) [$i] [ ^ost 口 содержит адрес первого сервера, вторая директива $cfg( Servers1 ] [$i] I 'host1] содержит адрес второго сервера MySQL и т. д. Если осушествляется администрирование только одного сервера, следует установить директивы первого сервера, оста^ вив директивы остальных серверов незаполненными.
$cfg[1 Servers *][$i][fhost1] string
Сетевой адрес или IP-адрес $i-ro MySQL-сервера. Например, "localhost'\
$cfg[* Servers f][$i] [*port•] string
Порт $i-ro MySQL-сервера. По умолчанию, если значение не установлено, используется стандартный порт ——3306* Если в качестве сетевого адреса установлено значение localhost, MySQL соединяется с помощью сокета, не используя значение порта, приведенное в данной директиве. Поэтому если вы хотите использовать на локальной машине порт, отличный от стандартного, используйте в качестве сетевого адреса 11127,0,0. Г'.
$cfg[1 Servers*}[$i] [1 socket1] string
Данная директива определяет путь к сокету. По умолчанию не установлена.
$cfg【'Servers 1][$i] ['connect_type1] string
Определяет, какой тип соединения используется для соединения с MySQL- сервером. Может принимать два значения: soket и tcp. По умолчанию установлено tcp, так как семейство протоколов TCP/IP гарантированно доступно на всех серверах, в то время как сокеты не поддерживаются на ряде платформ.
( Замечание )
Для использования режима soket удостоверьтесь, что сервер MySQL установлен на той же машине, что и Web-сервер.
$cfg[1 Servers *]t$i][1 controluserf1 string
$cfg[*Servers1] [$i] [*controlpass *] string
Эти две директивы применяются для организации многопользовательского доступа к базе данных, в частности, для проверки, к каким базам данных пользователь может получить доступ при входе.
$cfg['Serversf] [$i] [1auth^type*] string [1 http1 I 1 cookie r|1 config1]
Определяет, какой вид идентификации будет использован при входе на сервер:
'config'——старый способ идентификации: имя пользователя и его пароль хранятся в файле config.inc.php;
口 Cookie' — режим идентификации, введенный начиная с версии 233. Позволяет входить на сервер с использованием механизма cookie. Имя пользователя и его пароль сохраняются на машине пользователя во время сессии и удаляются по ее окончанию;
'http'——режим идентификации, позволяющий входить на сервер MySQL с идентификацией через HTTP-заголовки.
$cfg U Servers•][$i]['user11 string
$cfgt* Servers *] [$i] ^password*] string
Пара пользователь/пароль, которые phpMyAdmin использует для соединения с MySQL-сервером. При идентификации в режиме HTTP или с использованием cookie установка данных директив не требуется.
5 Зак. 1181
118
$cfg[* Servers1][$i][*only_db*] string or array
Добро пожаловать в phpMyAdmin 2.5.5-рИДобро пожаловать в phpMyAdmin 2.5.5-рИMySQL 4.0.17-nt на localhost как igor@localhostMySQL 4.0.17-nt на localhost как igor@localhostПозволяет запретить просмотр баз данных, размешенных на сервере, за исключением перечисленных в этой директиве. По умолчанию если директива не установлена, отображаются все базы данных. Установка, например, $cfg[ •Servers11 ($i] [tonly_db,]=,—九 приведет к тому, что в панели управления будет доступна только тестовая база данных, в то время как остальные базы данных, включая системную базу данных MySQL (mysql) и phpMyAdmin (phpmyadmin), будут недоступны (рис. 2.31).
Добро пожаловать в phpMyAdmin 2.5.5-рИ
Добро пожаловать в phpMyAdmin 2.5.5-рИ
MySQL 4.0.17-nt на localhost как igor@localhost
MySQL 4.0.17-nt на localhost как igor@localhost
Окно запросаОкно запросаMySQLг- Создать новую БД: [Документация] Без привилегийг Показать состояние MySQLг Показать системные переменные MySQL (Документация}г Показать ,ППП11АСГЫMySQLг- Создать новую БД: [Документация] Без привилегийг Показать состояние MySQLг Показать системные переменные MySQL (Документация}г Показать ,ППП11АСГЫphpMyAdminг Language C): [Russian (ru・win1251)phpMyAdminг Language C): [Russian (ru・win1251)г- Документация по phpMyAdminг Показать информацию о PHPг- Официальная страница phpMyAdmin [ChangeLog] [CVS] [Lists]г- Документация по phpMyAdminг Показать информацию о PHPг- Официальная страница phpMyAdmin [ChangeLog] [CVS] [Lists]勺 Местная интрасеть勺 Местная интрасеть
Окно запроса
Окно запроса
MySQL
г- Создать новую БД: [Документация] Без привилегий
г Показать состояние MySQL
г Показать системные переменные MySQL (Документация}
г Показать ,ППП11АСГЫ
MySQL
г- Создать новую БД: [Документация] Без привилегий
г Показать состояние MySQL
г Показать системные переменные MySQL (Документация}
г Показать ,ППП11АСГЫ
phpMyAdmin
г Language C): [Russian (ru・win1251)
phpMyAdmin
г Language C): [Russian (ru・win1251)
г- Документация по phpMyAdmin
г Показать информацию о PHP
г- Официальная страница phpMyAdmin [ChangeLog] [CVS] [Lists]
г- Документация по phpMyAdmin
г Показать информацию о PHP
г- Официальная страница phpMyAdmin [ChangeLog] [CVS] [Lists]
勺 Местная интрасеть
勺 Местная интрасеть
I 引 phpMyAdmin на localhost - phpMyAdmin 2.5.5-pll - Microsoft Internet Explorer
№□1
Файл Правка Вид Избранное Сервис Справка
Назад ▼〉,◎£!》|; По心Избранное /Медиа 宅▼
一,设船
АДР«^ 1 题 hup: //kx^lhost/toote/phpMyAdrnin/lndex .ph р?1а ng=ru-win 1251 &serv era 1
▼ ] Q Переход Ссылки
К началу test 3
Рис. 2.31. Доступ разрешен только к одной базе данных — test,остальные базы данных не доступны
Если необходимо разрешить доступ к двум или более базам данных, то вместо строки директиве необходимо передать массив:
$cfg [1 Servers1 ] [$i] [^nly^db*] = array (^tesf , ldbll, 'db2');.
I Замечание )
Эта директива не замещает права доступа СУБД MySQL, она просто определяет, какие базы данных будут отображены на главной панели управления.
Эта директива может также определять порядок вывода баз данных в списке. Так, директива вида $cfg[ •Servers1 H$iH ,only_db, ] = array ( ^З1, ,db4l »*r); определяет, что сначала будут выведены базы данных db3 и db4, а затем все остальные в алфавитном порядке.
$cfg[* Servers *][$i] [Verbose 1] string
Эта директива полезна только при использовании многосерверной конфигурации. Она позволяет заменять строку по умолчанию в выпадающем списке выбора сервера на установленную в данной директиве.
$cfg[* Servers1][$i][^madb1] string
Начиная с версии 2.3.0, php Му Admin предлагает множество дополнительных возможностей при работе с таблицами. Для использования таких элементов, как закладки, необходимы вспомогательные таблицы. Данная директива определяет имя базы данных, где располагаются данные таблицы.
Если вы являетесь единственным пользователем phpMyAdniin, вам необходимо просто создать базу данных и использовать ее. Если настройка Web- приложения производится для многопользовательской среды, необходимо проследить, чтобы случайные пользователи не получили доступ к этой базе данных.
$cfg[1 Servers 1] [$i] [^ookmarktable1] string
Начиная с версии 2.2.0, phpMyAdmin позовляет делать закладки лля наиболее часто используемых SQL-запросов. Для этого необходимо создать таблицу при помощи SQL-запроса, расположенного в файле scripts/ create_tables.sqh и установить имя таблицы (по умолчанию bookmarktable) для этой директивы.
$cfg[1 Servers 1] [$i] [1 relation'] string
Начиная с версии 2.2.4, можно описать так называемые связанные таблицы. Поля таких таблиц являются ключами другой таблицы (внешние ключи). Приложение phpMyAdmin использует данную таблицу для:
口 доступа к данным внешней таблицы при просмотре основной таблицы;
П вывода подсказки ''отображение полей" при просмотре главной таблицы, если мышь наводится на внешний ключ таблицы;
□ вывода списка доступных внешних ключей в режиме правки/вставки;
口 отображения ссылки на страницу свойств, где можно проверить ссылочную целостность внешних ключей;
口 создания PDF-схемы базы данных.
Создать такую таблицу можно при помощи SQL-запроса, расположенного в файле scripts/сreate__tables.sqL Необходимо установить имя таблицы (по умолчанию relation) для этой днректмвн:
$cfg[1 Servers'] [$i] [1table_info'J string
Начиная с версии 2.3,0, можно описать в специальной таблице table_info поля, для которых должна выводиться подсказка при наведении на них курсора мыши.
$cfg[r Servers r J [$i] [1table_coords *] string
$cfg[* Servers *] [$i] [fpdf_pages1] string
Начиная с версии 2.3.0, phpMyAdmin может создавать PDF-страницы, отображая связи между вашими таблицами. Для этого необходимы две таблицы: pd£_pages, сохраняющая информацию о доступных PDF-страницах, и tabie_coords, сохраняющая координаты таблиц на PDF-схеме.
$cfg[1 Servers'] [$i][1colunm_info'] string
Начиная с версии phpMyAdmin 2.3.0, появилась возможность снабжать каждый столбец таблицы комментарием, который отображается на странице печати таблицы Кроме того, начиная с версии 2.5.0, комментарии отображаются при наведении на имя столбца указателя мыши и выводятся в дампе базы данных.
$cfg [1 Servers * ] [$i] [history*] string
Начиная с версии phpMyAdmin 2.3,0, появилась возможность сохранять историю SQL-запросов. Используя директиву $cfg [1 QueryHistoryMax1 ], можно указать максимально возможное число SQL-запросов, которые будет помнить сисгема.
$cfg[1ServerDefault1] integer
Если phpMyAdmin сконфигурирован для управления более чем одним сервером, при помощи данной переменной можно указать номер сервера, с которым должно соединяться приложение phpMyAdmin при его запуске. Если значение данной директивы установлено в 0, будет предлагаться список доступных серверов без автоматического входа.
$cfg[*OBGzip*] string/Boolean
Определяет, будет ли использован буферизированный GZip-поток при передаче данных по HTTP-протоколу. Принимает значения true и false для включения и выключения данного режима передачи данных. При установке значения auto phpMyAdmin пытается запустить режим буферизации и автоматически отключает его при возникновении с ним проблем у браузера.
$c£g[1PersistentConnections'] Boolean
Данная директива определяет» использовать ли постоянное соединение с базой данных (true) или нет (false).
$cfg[*ExGcTimeLimit1] integer [number of seconds]
Определяет» сколько секунд может выполняться скрипт. При установке значения этой директивы в 0 временные ограничения отсутствуют. Данное значение времени используется при импорте и экспорте дампов баз данных.
( Замечание )
Данная директива не имеет значения, если PHP выполняется в защищенном режиме.
$cfg[* ShowSQL1] Boolean
Определяет, должны ли SQL-запросы, генерируемые phpMyAdmin, отображаться в браузере пользователя.
$cfg[1AllowUserDropDatabase'] Boolean
Определяет, позволено ли пользователям стирать собственные базы данных (true) или нет (false). Если значение данной директивы установлено в false, ссылка Уничтожить базу данных не будет выводиться, а SQL-запросы вида drop database mydatabase будут игнорироваться. $cfg[1 Confirm1] Boolean
Директива определяет, выводиться пн предупреждению (true), если в ре^ зультате запроса происходит хоть какая-то потеря данных. $cfg['LoginCookieRecall11 Boolean
Определяет, будет ли выводиться имя предыдущего пользователя в режиме cookie-идентифи кацнн.
$cfg [1 IgnoreMultiSubniitErrors1 ] Boolean
Директива определяет, будет ли выполняться далее многострочный запрос, если в результате одного из запросов происходит ошибка (true). По умолчанию выставлено значение false, что приводит к немедленной остановке выполнения запросов, $cfg[1VerboseMultiSubmit1] Boolean
Определяет, будет ли выводиться результат выполнения SQL-запроса как встроенный комментарий после выполнения запроса. По умолчанию значение директивы установлено в true. $cfg[1ShowStats1] Boolean
Определяет, выводить ли статистическую информацию о базах данных и таблицах.
[ Замечание )
Вывод статистики производится при использовании версии MySQL не ниже 2.233
$cfg [1 SuggestDBNaine1 ] boolean
Директива определяет, предлагается ли при создании новой базы данных имя по умолчанию или это поле формы остается пустым.
$cfg[1ShowBlQb U Boolean
Определяет, отображается ли при просмотре содержимое полей типа blob.
$cfg[^avigationBarlconic,] Boolean
Определяет, содержат ли навигационные кнопки только текст или еще и изображения.
$cfg[1ShowAll1] Boolean
Определяет, отображается ли ссылка Показать все или нет.
$cfg[*MaxRows1] integer
Определяет максимальное количество строк в результирующей таблице. Если результат содержит большее число строк, появятся ссылки Предыду- щие/Следующие, $cfg[tOrder,] string [DESC[ASC|SMART]
Данная директива определяет порядок сортировки результата: прямой (asc), обратный (desc), "интеллектуальный0 (smart) порядок. В последнем случае результат сортируется по убыванию полей в рядах типа time, date, datetime и timestamp или по умолчанию, если эти поля отсутствуют.
$cfg[* ProtectBinary1] boolean or string
Директива определяет, защищены ли поля типа blob и binary от редактирования при просмотре полей средствами php Му Admin. Может принимать следующие значения:
false — позволяет редактирование этих полей;
口 "blob” ——позволяет редактирование всех полей за исключением blob;
"all” 一 запрещает редактирование полей типа blob и binary.
$cfg['ShowFunctionFields'] Boolean
Определяет, будут ли отображаться встроенные функции MySQL в режиме редактирования или вставки строк. $cfg[TCharEditing1J string
Определяет» какой элемент управления будет использоваться при редактировании полей типа char и varchar. Директива может принимать следующие значения:
口 input ——данный элемент управления позволяет контролировать размер вводимой информации согласно ограничениям, наложенным на столбец, но не позволяет вводить символ новой строки;
textarea —— позволяет вводить символ с новой строки, но не может контролировать размер вводимой информации.
$cfg[* ShowMysqllnfo1] Boolean
$cfg[* ShowMysqlVars *] Boolean
$cfg[1ShowPhpInfo1] Boolean
$cfg[1ShowChgPassword1] Boolean
Эта группа директив определяет, будут ли выводиться ссылки Показать состояние MySQL, Показать системные переменные MySQL, Показать информацию о PHP и Сменить пароль.
$cfg[1NavigationBarlconic1) Boolean
Директива определяет, содержат ли навигационные кнопки текст или только изображения.
$cfg(* ZipDump*] Boolean
$cfg L GZipDump1] Boolean
$cfg['BZipDump,] Boolean
Эта группа директив определяет, можно ли использовать zip/GZip/BZip2- сжатие при передаче дампов баз данных (true) или нет (false).
$cfg[*CompressOnFly,] Boolean
Определяет, разрешается ли использование GZip/BZip2・c*aTHe "на лету1' при экспорте базы данных.
$cfg [т Properties Iconic1 ] string
При установке данной директивы в true ссылки на действия с базой данных и таблицей отображаются в виде изображений. При установке в false — в виде текстовых ссылок. При передаче параметра "both" используются и изображение, и текст.
$cfg [1 PropertiesNumColumns,] integer
Директива определяет, сколько колонок таблицы будет выведено на странице свойств базы данных. По умолчанию таблицы выводятся в одноколоночном формате. Если директиве передать значение, превышающее единицу, тип таблицы будет опущен для освобождения места для многоколоночного вывода.
$cfg[1DefaultLang1] string
Определяет язык, используемый по умолчанию.
$cfg[* DefaultCharset1] string
Определяет кодировку, используемую по умолчанию.
$cfg['GD2Available'] string
Определяет, доступна ли библиотека расширения GD21ib второй или более поздней версии. Возможные значения:
□ auto — автоматическое определение;
口 yes — функции библиотеки GD2 могут быть использованы;
О по — функции библиотеки GD2 не доступны для использования.
$cfg[1 Leftwidth'] integer
Ширина левого фрейма главной панели управления в пикселах.
$cfg[1LeftBgColor1] string [HTML color]
$cfg(*RightBgColor1] string [HTML color]
Эта группа параметров определяет цвет фона левого и правого фрейма главной панели управления. Определяет максимальное количество строк, выводящееся при просмотре таблицы.
Использование phpMyAdmin
Рассмотрим интерфейс панели управления phpMyAdmin, показанный на рис. 2.32.
Рис. 2.32. Главная страница панели управления phpMyAdmin
Интерфейс панели управления phpMyAdmin разделен на 2 части. Левая часть представляет собой навигационное меню для выбора баз данных н таблиц. Содержание правой (основной) части интерфейса зависит от вы-
бранного объекта (база данных, таблица) и текущей операции над этим объектом.
Рассмотрим навигационное меню.
В выпадающем списке (в верхней части навигационного меню) перечислены все доступные базы данных, в скобках рядом с названием указывается количество таблиц в этих базах.
Чтобы выбрать базу данных для работы, достаточно выделить ее в выпадающем списке. После того как база выбрана, в навигационном меню автоматически отображаются ее название и список присутствующих в ней таблиц. Содержание основной части интерфейса также автоматически изменяется. В ней отображаются список таблиц, системная информация, характеризую' щая каждую из них, н ссылки для редактирования (рис. 2.33).
Рис 2.33. Интерфейс phpMyAdmin при работе с базой данных
Выбор таблицы для работы осуществляется щелчком на ее названии в навигационном меню. Содержание навигационного меню при выборе таблицы
не меняется. В основной части страницы phpMyAdmin отображаются структура выбранной таблицы, служебная информация и ссылки на доступные операции (рис. 2.34).
Рис. 2.34. Интерфейс phpMyAdmin при работе с таблицей
Рис. 2.35* Окно для работы с базо访 с помощью SQL-запросов
Чтобы вернуться к списку таблиц выбранной базы данных в основной части страницы, следует щелкнуть на названии базы данных в навигационном меню, которое выделено жирным шрифтом.
Чтобы вернуться на главную страницу панели управления phpMyAdmin» следует щелкнуть на ссылке К началу, расположенной в верхней часта навигационного меню.
В самом низу навигационного меню есть ссылка Окно запроса, позволяющая использовать язык SQL для работы с таблицами н базами данных. Переход по данной ссылке откроет окно для работы с базами данных с помощью SQL-запросов (рис. 235).
Главная страница phpMyAdmin
Главная страница (см. рис. 2.32) содержит 2 колонки ссылок:
П для работы с MySQL;
口 для работы с phpMyAdmin.
Колонка ссылок для работы с phpMyAdmin содержит выпадающее меню выбора кодировки страницы, ссылки на документацию по phpMyAdmin, официальную страницу phpMyAdmin в Интернете, ссылку на страницу с информацией о PHP (функция phpinfot)).
Рассмотрим подробнее пункты, которые содержит колонка для работы с MySQL.
Создание новой базы данных
Форма для создания новой базы данных. Чтобы создать новую базу данных, следует ввести в соответствующее текстовое поле ее название и нажать кнопку Создать.
К Примечание ;
Подавляющая часть хосьпровайдеров отключает возможность создания новых баз данных через phpMyAdmin. Если на вашем хостинге этот пункт неактивен и вы не имеете доступа к настройкам phpMyAdmin, то для создания новых баз данных следует обратиться в службу поддержки хост-провайдера.
Показать состояние MySQL
Нажатие на ссылку открывает страницу со служебной информацией о работе сервера MySQL. На этой странице можно узнать время работы и время последнего запуска, статистику по сетевому трафику, статистику по типам запросов к серверу, значения статусных переменных.
Переход Ссылки **Переход Ссылки **К началу[(БазьТданных) 5Выберите БДК началу[(БазьТданных) 5Выберите БДТрдфик • в час Принято 0 В^ев 0 Bytes Послано 0 Bytes 0 Bytes Всего 0 Bytee 0 BytesТрдфик • в час Принято 0 В^ев 0 Bytes Послано 0 Bytes 0 Bytes Всего 0 Bytee 0 BytesСоединенияНеудачные попытки ООтмененыОВсего14СоединенияНеудачные попытки ООтмененыОВсего14• В ЧАС%О ОО0.00 %OW0.00 %63.00 100.00%• В ЧАС%О ОО0.00 %OW0.00 %63.00 100.00%
Переход Ссылки **
Переход Ссылки **
К началу
[(БазьТданных) 5
Выберите БД
К началу
[(БазьТданных) 5
Выберите БД
Трдфик • в час Принято 0 В^ев 0 Bytes Послано 0 Bytes 0 Bytes Всего 0 Bytee 0 Bytes
Трдфик • в час Принято 0 В^ев 0 Bytes Послано 0 Bytes 0 Bytes Всего 0 Bytee 0 Bytes
Соединения
Неудачные попытки О
ОтмененыО
Всего14
Соединения
Неудачные попытки О
ОтмененыО
Всего14
• В ЧАС%
О ОО0.00 %
OW0.00 %
63.00 100.00%
• В ЧАС%
О ОО0.00 %
OW0.00 %
63.00 100.00%
導 phpMyAdmln на tocaUrost - phpMyAdnm 2.5.5-pl 1 - Mia'Moft Internet Екр1огсг
файп □?»« Ёнд Забранное Св»ис &|р«жв Адрес}, |^| http://phpmyadrmn
Сервер localhost
«крс-нгшоле j Н『1Е|1『мнм1“ При"РО1; +кг【“屮т |
Текущая информация
Этот MySQL сервер работает 0 дней. О часов, 13 минут и 20 секунд. Он был запущен Июн 02 2004 г tOB:ie.
Трафик Эти таблицы показывают статистику по сетевому трафику MySQL сервера со времени его эапускэ.
♦%■“ ,‘阳qca♦%■“ ,‘阳qca
♦%■“ ,‘阳qca
♦%■“ ,‘阳qca
Статистика запросов: Со времени запуска 112 запросов было послано на сервер. .
Рис 2*36. Страница состояния MySQL
Показать системные переменные
Нажатие на ссылку открывает страницу со списком системных переменных сервера MySQL (рис. 2.37).
Показать процессы
Нажатие на ссылку открывает страницу со списком процессов, запущенных на сервере MySQL (рис. 2.38).
Рис. 2.38. Список процессов, запущенных на сервере MySQL
Перезагрузить MySQL
Переход по этой ссылке приводит к перезагрузке сервера MySQL.
Привилегии
Переход по ссылке открывает страницу, на которой представлены список пользователей сервера MySQL, их привилегии н ссылки для редактирования (рис. 2.39).
Рис. 2.39. Список пользователей сервера MySQL и их привилегий
Рис* 2.40. Список баз данных, расположенных на сервере MySQL
Базы данных
Переход по этой ссылке открывает список баз данных, расположенных на сервере (рис. 2.40).
Экспорт
Переход по этой ссылке открывает страницу, позволяющую получить дамп баз данных или отдельных таблиц (рис. 2.41).
Экспорт баз данных
Все элементы, содержащиеся на странице, показанной на рис. 241, разделены на 3 группы:
口 Экспорт;
口 Опции;
Настройки вывода.
Группа Экспорт
Группа элементов настройки экспорта показана на рис. 2.42.
—Зкспорт -二
autotrade * calc count counter dcart forum 刁
Отметить все / Снять отметку со всех
6 SQL
Г LaTeX
「CSV для данных Ms Excel
「CSV данные
Рис* 2.42. Группа элементовнастройки экспорта
В этой фуппе собраны настройки экспорта баз данных. В верхней части расположен список всех баз данных. Отмечая элементы списка, можно вы-
брать базы данных для экспорта. Несколько баз данных можно выбрать с помощью клавиш <Ctrl> или <Shift>.
[ Замечание )
Если в навигационном меню выбрана конкретная база данных, то вместо списка баз данных здесь отображается список таблиц выбранной базы.
( Замечание )
Если ни одна база данных из списка не будет выбрана, то при экспорте будут обрабатываться все базы данных, находящиеся на сервере, что может занять длительное время. С другой стороны, это позволяет осуществить резервное копирование всех баз данных.
Помимо выбора баз данных или таблиц, группа элементов Экспорт также предоставляет возможность для выбора формата экспорта. Доступен выбор из 4 вариантов:
SQL (дамп баз данных представляет собой инструкции SQL);
LaTex (экспорт в формате LaTex. LaTex — специальный пакет, предназначенный для верстки публикаций при подготовке к печати);
CSV для данных MS Excel (текстовый формат данных с разделителями, предназначенный для экспорта в MS Excel);
口 CSV-данные (текстовый формат данных с разделителями с возможностью "тонкой" настройки экспорта).
В зависимости от выбранного формата меняется содержание группы элементов Опции. Рассмотрим настройку опций для формата SQL, как наиболее часто употребляемого.
Опции формата SQL
Перечень опций представлен на рис. 2.43.
口 Опция Добавить DROP DATABASE
Добавляет в дамп инструкции для удаления базы данных перед их созданием. Позволяет избежать ошибок при импорте, если база данных уже существует.
口 Опция Добавить удаление таблицы
Добавляет в дамп базы данных перед каждым созданием таблицы инет* рукции SQL для их удаления.
DROP TABLE IF EXISTS 1TABLE_NAME *;
Эта опция необходима для предотвращения сбоя при загрузке дампа в базу данных, если таблица с таким именем уже существует. При наличии
опции Добавить DROP DATABASE использование данной директивы нс имеет смысла.
Опции SQL ( !, ч:/ментация)
— Опции экспорта БД Г Добавить DROP DATABASE
—R Структура
广 Добавить удаление таблицы
口 Добавить значение AUTOJNCREMENT
旷 Обратные кавычки в названиях таблиц и полей
—Добавить в комментарии
厂 Creation/Update/Check dates
* Данные—
厂 Полная вставка
厂 Расширенные вставки 厂 Use delayed inserts Export type: I INSERT 3
Рис. 2.43. Опции экспорта в формате SQL
口 Опция Добавить значение AUTO__LNCREMENT
Включение этой опции добавляет в инструкцию для создания таблицы текущее значение auto_incremewt.
CREATE TABLE ripf (
'id_ipi 工NT(32) NOT NULL AUTO_INCREMENT,
'ip' TINYTEXT,
PRIMARY KEY ('id_ip')
)TYPE^MylSAM AUTO_INCREMENT=11;
Данная опция поддерживается всеми типами таблиц за исключением
ISAM.
О Опция Обратные кавычки в названиях таблиц и полей
При включении этой опции названия таблиц н полей обрамляются обратными кавычками.
口 Опция Добавить в комментарии
Включение этой опции добавляет в текст дампа для каждой таблицы комментарии с указанием времени создания и обновления таблицы.
Опция Данные
Включает экспорт данных. Если опция не включена, то дамп базы данных содержит только структуру таблиц.
Опция Полная вставка
При формировании дампа используется полный формат инструкции SQL (с перечислением названий полей таблицы) для вставки данных.
INSERT INTO 'TABLE—NAME'《'field— ' f ield2 ' ) VALUES (0, 1);
口 Опция Расширенные вставки
Вставка данных происходит с помощью только одной многострочной ин струкции INSERT:
INSERT INTOINSERT INTO'TABLE_NAMEi VALUES'TABLE_NAMEi VALUES
INSERT INTO
INSERT INTO
'TABLE_NAMEi VALUES
'TABLE_NAMEi VALUES
(60, 11, 3, 10),
(61, 14, 3, 20), (62, 20, 3, 10), {71, 81, 4, 10);
Опция Use delayed inserts
В инструкцию insert добавляется ключевое слово delayed: Команда INSERT DELAYED INTO
Опция delayed — специфическая опция СУБД MySQL. Использование этой опции позволяет клиенту, отправившему SQL-запрос на вставку, не ждать его реального завершения, а получить ответ ок сразу после отправки запроса. Реальная вставка данных произойдет, когда таблица будет освобождена другим потоком. Применение опции delayed оправдано, если для клиентов критично время ожидания выполнения запроса, и таблицы находятся в постоянном использовании. Опцию delayed рекомендуется применять только для таблиц isam. Выполнение инструкции insert вместе с опцией dalayed происходит медленнее, чем без нее.
口 Список Export type
Выбор типа инструкции для вставки данных. Доступен выбор из 3 значений:
insert — вставка записи в таблицу;
update ——обновление всех записей таблицы. Инструкцию update следует применять, если в таблице существует только одна строка, значения которой будут заменены новыми;
replace — является аналогом insert, за исключением того, что если в таблице уже существует запись с теми же значениями, что и новая вставляемая, то старая запись будет удалена прежде, чем будет вставлена новая*
Группа Настройки вывода
Содержимое группы Настройки вывода представлено на рис. 2.44.
—厂 послать
Шаблон имени файла : |—DB—( Р запомнить шаблон )*
—Сжатие- — —
« Нет 广 архивировать в zip「архивировать 8 gzip
Рис. 2.44. Группа настроек вывода
Если выбрана опция послать, то дамп базы будет сохранен в виде файла, в противном случае он будет выведен в окно браузера.
Опции группы Сжатие определяют, архивировать ли дамп базы данных и формат архивного файла*
2.4.5. Работа с пользователями и привилегиями
Для работы с пользователями к их привилегиями следует перейти на главную страницу phpMyAdmin. Для этого необходимо щелкнуть по ссылке К началу в верхней части навигационного меню. Затем в списке ссылок MySQL перейти по ссылке Привилегии. В результате будет открыта страница со списком пользователей н ссылками для редактирования их прав доступа (рис. 2.45).
Пользователь
Хост Пароль
「лобальн^е привилегии
Предоставлять
Действие
厂 Любой
%Нет
USAGE
Нет
Правка
厂 Любой
localhost Нет
ALL PRIVILEGES
Да
Правка
Г root
%
ALL PRIVILEGES
Да
Правка
Г root
localhost Да
ALL PRIVILEGES
Да
Правка
Рис. 2.45. Список пользователей
Рассмотрим поля таблицы, показанной на рис. 2*45:
口 Пользователь — содержит имя пользователя сервера MySQL;
口 Хост ——содержит имя хоста, с которого пользователь может зайти на сервер. Может содержать имя хоста, 1Р・адрес, local host и шаблоны с символами подстановок 1 % ‘ (знак процента ——любое количестэо любых символов) и '一' (подчеркивание ——любой одиночный символ). Таким образом, если поле Хост содержит символ %, то возможен доступ с любого хоста;
口 Пароль — показывает, есть ли пароль у пользователя;
口 Глобальные привилегии — привилегии, назначенные данному пользователю;
Предоставлять — показывает, есть ли право у пользователя предоставлять свои привилегии другим пользователям (оператор grant).
口 Действие —— в данном столбце размещены ссылки, позволяющие отрелак- тровать привилегии пользователей на уровне таблиц и SQL-операторов.
Добавление нового пользователя
Для того чтобы добавить нового пользователя, следует перейти по ссылке Добавить нового пользователя, находящейся под таблицей со списком пользователей и их привилегий. Откроется страница добавления пользователя (рис. 2.46).
Добавить нового пользователя
Информация логина
Имя пользователя: 1 Использовать текстовое поле Ч admin
Хосг
[Любой
хост二J J
Пароль:
Использовать текстовое поле: ▼] ]•••♦
Подтверждение:
Глобальные привилегии
Г$ЭММ9<Г■村廿•: При«1Л9Лии MySQL Зв<3*ЮПГСЯ ЬО*〃ЙЛ"^С*Н
Данные
Р SELECT
Структура 厂 CREATE
Администрирование
Г GRANT
R? INSERT
旷 UPDATE
Г
Г
ALTER
INDEX
Г PROCESS
厂 RELOAD
в西函еЛ
г
DROP
Г SHUTDOWN
* FILE
厂 REFERENCES
「Поуел 1
Рис. 2.46. Страница добавления нового пользователя сервера MySQL
Страница содержит элементы для ввода данных:
Имя пользователя;
口 Хост:
口 Пароль.
Рассмотрим содержимое этих полей подробнее.
Имя пользователя
Вы можете задать имя пользователя в текстовом поле либо установить права для любого пользователя, выбрав из выпадающего списка пункт Любой пользователь.
! Совет )
В целях обеспечения защиты не рекомендуется создавать пользователя * % 1 (любой пользователь), а если он создан, то его следует удалить.
В MySQL существуют 3 специальных зарезервированных имени пользователей: monty, admin, dummy. При желании вы можете создать их самостоятельно:
monty — суперпользователь. Может входить на сервер как с локального хоста, так и с любого хоста в сети, но обязательно должен использовать пароль, в отличие от пользователя root, который может делать это без па- рол5(. Привилегии для пользователя monty следует установить отдельно, как для локального (localhost), так и для любого хоста (%);
admin —— мо*ет входить на сервер с локального хоста без пароля. Имеет административные привилегии Reload и Process. Доступа к базам данных не имеет. Привилегии для доступа к определенным базам должны быть установлены вручную;
dummy — может входить на сервер без пароля, но только с локального хоста. Не имеет никаких глобальных привилегий, за исключением usage и grant, что позволяет ему создавать пользователей без привилегий.
Хост
Вы можете задать имя хоста, с которого данному пользователю будет разрешен доступ к серверу. Хост можно задать, используя доменное имя хоста, IP-адрес и символы подстановки *%*(процент) и '一' (подчеркивание):
% — замещает любое количество символов;
口 — замешает любой одиночный символ.
Пароль
Вы можете задать пароль для пользователя, используя текстовые поля Пароль и Подтверждение. Пароль можно не задавать, выбрав в выпадающем списке значение Без пароля.
( 9-мечание )
При работе с пользователями для того, чтобы изменения вступили в силу, производите перезагрузку привилегий. Для этого нажмите ссылку Перезагрузить привилегии в последнем параграфе на странице Привилегии либо выполните SQL-оператор FLUSH_PR工V工LEGES.
Редактирование привилегий пользователей
MySQL предоставляет следующие привилегии:
口 SELECT — права на извлечение информации из таблиц;
□ INSERT — права на вставку записей в таблицу;
口 UPDATE ——права на обновление записей в таблице;
口 DELETE — права на удаление записей из таблиц;
口 INDEX — права на создание н удаление индексов;
口 ALTER — права на модификацию таблиц: добавление колонок, удаление колонок, изменение колонок;
CREATE — права на создание баз данных и таблиц;
О DROP — права на удаление баз данных и таблиц;
口 GRANT — позволяет пользователю предоставлять другим пользователям привилегии, которыми он сам обладает;
FILE — права на выполнение команд load data infile и select. «. into outfile. Такой пользователь может читать и записывать любой файл на сервере MySQL;
REFERENCES ——права на создание связей между таблицами с помощью внешних и родительских ключей.
Привилегии для администрирования сервера устанавливаются с помощью команды mysqladmin:
RELOAD — права на перезагрузку сервера;
口 SHUTDOWN — права на остановку сервера MySQL;
PROCESS — права на вывод списка процессов на сервере н остановку любого процесса.
Для редактирования привилегий пользователя перейдите по ссылке Правка напротив его имени в таблице пользователей* Откроется страница редактирования привилегий с несколькими группами элементов (рис. 2.47).
• Редактирование привилегий
Глобальные привилегии
f^juiM*t»nu^; npuwn^wu 恤 SQL 】』8ямм<б по-ан^ли^ки
Данные
Структура
А дминистрироеание
卩 SELECT
|7 CREATE
p GRANT
Р INSERT
P ALTER
!7 PROCESS
g UPDATE
17 INDEX
戸 RELOAD
卩 DELETE
口 DROP
口 SHUTDOUN
17 FILE
P REFERENCES
Пошел I
Рис. 2.47. Список привилегий
Редактирование глобальных привилегий
Глобальные привилегии распространяются на все базы данных, расположенные на сервере MySQL. Их редактирование осуществляется с помощью группы элементов Редактирование привилегии. Она содержит список привилегий. Галочками отмечены активные привилегии для выбранного пользователя (см. рис. 2.47).
Замечание
Будьте внимательны при установке глобальных привилегий, так как они распространяются на все базы, расположенные на сервере. Более правильной считается установка привилегий пользователей только на те базы, которые им необходимы.
Установка специфических привилегий на базу данных и таблицу
Выполняется с помощью группы команд Привилегии, специфичные для базы данных.
Лрнвмлегмы, специфичные для таблицы ДействиеНет-Пр^рк.1 0>Me»'?tb.Лрнвмлегмы, специфичные для таблицы ДействиеНет-Пр^рк.1 0>Me»'?tb.• Прмемлегин, специфичные для базы данных БД Привйлегкн Предоставлять
Лрнвмлегмы, специфичные для таблицы Действие
Нет-Пр^рк.1 0>Me»'?tb.
Лрнвмлегмы, специфичные для таблицы Действие
Нет-Пр^рк.1 0>Me»'?tb.
autotrade select, insert Нет
ПошелПошелДобавить npHBHjwrwi на следующую базу | Использовать текстовое поле jJ「
Пошел
Пошел
Рис* 2.48. Привилегии( специфичные для базы данных
В группе содержится список привилегий данного пользователя. Каждая строка таблицы содержит привилегии, распространяющиеся на конкретные базы данных. В таблице содержатся имена баз данных, перечень привилегий и ссылки для их изменения или удаления.
Чтобы установить специфичные привилегии пользователя на существующую базу данных, выберите ее в выпадающем списке, расположенном внизу группы специфических привилегий. После выбора базы страница автоматически обновится. Вместо списка глобальных привилегий, отображаемых на рнс. 248, будут выводиться права доступа к выбранной базе. Вы можете выбрать нужные привилегии и нажать кнопку Пошел либо расставить права доступа на таблицы в выбранной базе данных. Выпадающий список, в котором ранее содержались названия баз данных, теперь содержит названия таблиц. Выберите в списке таблицу —— на странице появится группа элементов установки привилегий, специфичньгх для таблицы (рис. 2.49),
Редактирование привилегий
Привилегии, специфичные для таблицы
Пи/М9Ч9ние: прийилеАии AfySQL задаются по~акгли&*и
SELECT, INSERT UPDATE REFERENCESSELECT, INSERT UPDATE REFERENCESname 上 id id__parent — url二Jname 上 id id__parent — url二Jname 士idid parent —name 士idid parent —ИлиГ" НетИлиГ" НетИлиГ" НетИлиГ" НетИли 厂НетИли 厂НетИли 厂НетИли 厂НетГ Г г г 厂 广Г Г г г 厂 广DELETECREATEDROPGRANTINDEXALTERDELETECREATEDROPGRANTINDEXALTER
SELECT, INSERT UPDATE REFERENCES
SELECT, INSERT UPDATE REFERENCES
name 上 id id__parent — url二J
name 上 id id__parent — url二J
name 士
id
id parent —
name 士
id
id parent —
Или
Г" Нет
Или
Г" Нет
Или
Г" Нет
Или
Г" Нет
Или 厂Нет
Или 厂Нет
Или 厂Нет
Или 厂Нет
Г Г г г 厂 广
Г Г г г 厂 广
DELETE
CREATE
DROP
GRANT
INDEX
ALTER
DELETE
CREATE
DROP
GRANT
INDEX
ALTER
fhuj助 I
Рис. 2.49. Привилегии, специфичные для таблицы
Работа с таблицами
Для работы с таблицами выберите базу данных из выпадающего списка в навигационном меню. Откроется страница со списком таблиц (рис. 2.50).
Таблица со списком таблиц базы данных содержит столбец Действие с перечнем операций над таблицей в виде пиктограмм. Перечислим их в том порядке, в котором они расположены:
口 Обзор — просмотр содержимого таблицы;
口 Поиск — форма поиска по содержимому таблицы;
口 Вставить —— форма вставки новой записи в таблицу;
Свойства —— просмотр структуры таблицы;
口 Уничтожить — удаляет таблицу;
О Очистить — удалить все данные из таблицы.
таблица Действие Записи Тип Размер Накладные расходы 厂 answer 口 R 也图 1Г 俞 23 MylSAM EH 卜 F - Г articles 而&包 杳窃向 11 MylSAM E - Г product! □ R 6 阁 會窗 10 MylSAM 5UF® - 厂 product; ПЗ 4 把图窗嵐 71 MylSAM 日
Г productEl & 沿图 ■血22 MylSAMJ rE•
"product?口 Q 七齿會向10 MylSAM」厂•
16 таблиц(ы)Всего313“62.9 КВ0 Bytes
— Отметить есе / 1 ннть отметку со все*|с отмеченнымид|
Рис, 2.5О・ Список таблиц
Групповые операции над таблицами
Часть операций можно выполнять над несколькими таблицами одновременно. Таблицы можно выбрать, установив флажки, в первой колонке списка таблиц. Затем выбрать операцию из выпадающего списка фупповых операций:
Уничтожить — уничтожает таблицы;
口 Очистить — удаляет данные из таблиц;
П Версия для печати — выводит на экран структуру таблицы, используемое пространство, статистику ряда в удобном виде для печати;
О Проверить таблицу —— проверяет таблицу на наличие ошибок. Выполняется команда check table;
口 Оптимизировать таблицу — оптимизирует размещение данных, дефрагментирует файл данных. Рекомендуется выполнять после удаления большой части записей в таблице или если вносилось много изменений в строки переменной длины. Выполняется команда optimize table;
口 Починить таблицу — восстанавливает данные в поврежденных таблицах. Выполняется команда repair table;
口 Диализ таблицы ——анализирует и сохраняет распределение ключей. Команда analyze table.
Создание новых таблиц
Создавать новые таблицы можно несколькими способами. Один из них — воспользоваться формой, расположенной внизу страницы со списком таблиц выбранной базы данных (рис. 2.51).
• Создать нотую таб力нцу 更 5口 inss :
Имя: I
Поля :I 门0山一
Рис. 2.51. Форма для создания новой таблицы
Для создания таблицы введите ее имя и количество полей, из которых будет состоять таблица. Затем нажмите кнопку Пошел.
Откроется страница, на которой вы сможете определить параметры каждого поля (рис. 2.52).
Рис. 2.52. Параметры полей создаваемой таблицы
Заполните необходимые поля и нажмите кнопку Сохранить. Если вы все заполнили правильно — таблица будет создана.
Заключение
Мы рассмотрели основные элементы интерфейса phpMyAdmin н функции работы с базами данных, которые он предоставляет. Интерфейс phpMyadmin достаточно прост для самостоятельного изучения, его характерная черта — наличие нескольких путей решения одной задачи. И каждый, кто будет им пользоваться, несомненно выработает самый удобный для себя алгоритм решения этих задач.
Глава 3
Заголовки HTTP и работа с ними
Без понимания тонкостей работы HTTP-протокола не может обойтись ни один Web-программист. Данная глава не претендует на полный охват особенностей работы с HTTP-протоколом —— в ней описываются только НТТР- заголовкм, нужные PHP-программисту в повседневной работе.
Функции PHP
для работы с НТТР-заголовками
В этой главе будут рассмотрены наиболее часто встречающиеся НТТР- заголовки и функции, предоставляемые PHP 5 для работы с ними.
Собственно таких функций В PHP Три: header о > headers_sent о И headers_list(),
Функция header о предназначена для отправки HTTP-заголовков на сервер и имеет следующий синтаксис:
int header(string str [, bool replace [, int http_response_code]])
Обязательный параметр str ——это тот заголовок, который необходимо отправить. Необязательный параметр replace указывает, должен ли заголовок замещать предыдущий аналогичный заголовок или нужно добавить второй заголовок того же самого типа. По умолчанию предыдущий заголовок замещается следующим (replace=true). Если в качестве второго аргумента передается false, то таким образом можно ускорить создание нескольких заголовков одного типа. К примеру так, как это сделано в листинге 3.1.
i Листинг 3.1. Создание нескольких заголовков одного типа
<?php
header「WWW-Authentteate: Negotiate');
header(* WWW-Authenticate: NTLM',false); ?>
В листинге 3」второй заголовок не заменяет первый, поскольку ему переда^ но false в качестве второго аргумента, н клиенту отправлены оба заголовка.
Замечание )
Создание нескольких заголовков возможно только для заголоаков одного типа.
Третий необязательный параметр ——http response code ——заставляет HTTP вернуть HTTP-статус'код, заданный в параметре.
Отправлять заголовки можно н без использования функции header(). Стандарт HTML также позволяет работать с ними. Так, в листинге 3.2 приведен пример отправки НТТР-заголовка Location, осушествляющего редирект на страницу www.php.net.
岑二:「:第滋 整瞄 ej津能费岁:产笈'%猊招単::準羅雷峰謐J讎塁聲撼号殛衢;"芍**:・卡蒙";招等П""弋"中北出竺h£片变丫段丁举w& 寓銀紮■'工涉:零 ? Листинг 3,2. Отправка заголовкаi
《HTML><HEAD>
СМЕТА HTTP-EQUIV='Refresh * CONTENT=,0; URL=bttp;//www.php.net»> </HEADX/HTML>
В листинге 3.2 клиенту отправлен заголовок 'Refresh' с содержимым, передаваемым через ключевое слово content.
Функция headers_sent проверяет, были ли отправлены заголовки, и имеет следующий синтаксис:
bool headers_sent([string &file [, int &line]])
Функция возвращает false, если заголовки еще не были отправлены клиенту, и true, если заголовки уже отправлены. Необязательные параметры file и line позволяют получить имя файла и номер строки, в которых заголовки были отправлены.
[ Замечание )
Так как служебная информация для работы с cookie и сессиями отправляется через HTTP-заголовки, вывод в окно браузера информации при помощи функций echo (), print () и т. п. перед установкой cookie или сессий приводит к ошибочной ситуации, так как HTTP-заголовки уже отпраалены клиенту. Для отладки таких ситуаций как раз и предназначена функция headers__sent (), позволяющая выяснить, отправлены заголовки клиенту или нет. Если все-таки вывод данных необходим до использования функций» работающих с заголовками, сессиями и cookie, следует использовать буферизацию, управление которой осуществляется вызовом функций ob_start () и ob_end_f lush () или установкой в конфи!урационном файле php.ini директивы output_buffering.
Третья функция — headers_list о — возвращает список готовых для отправки заголовков в виде массива н имеет следующий синтаксис: array headers_l 1st ()
После описания основных функций, предназначенных для работы с HTTP- заголовками, рассмотрим несколько примеров.
Первый относится к тому типу заголовков, которые начинаются строкой "httf/”・ Заголовки такого типа используются для отправки клиенту НТТР- статус-кода в ответ на его запрос. К примеру, если Web-сервер Apache сконфигурирован таким образом, что для обработки запросов загрузки отсутствующих файлов используется PHP-скрипт, то возникает необходимость удостовериться, что скрипт отправляет клиенту соответствующий статус-код (листинг 3,3).
渺岁'斗承1也載»理1簿濯*‘«第й能季険疆贅承匕’м?繚R ’'期書開:牙^窗咎 咬片件常圜¥ в у—5*‘/5/
i Листинг 3・3・ Проверка статус-кода
■ ai^^jiifsfit'b ■3,*;»Т<2 ■ ■ ^5C?J#lV.,“》M®$^^^^^;T:・.、、*,;t***”;/,/*.W5^y^J^^^^V",^^t・;С: *」£*、> .
<?php
header("HTTP/l.О 404 Not Found"); ?>
; Замечание )
Строка HTTP-статуса всегда отправляется клиенту первой, независимо от того, формируется соответствующий заголовок первым или нет.
Для того чтобы указать Web-серверу Apache, каким образом реагировать на отсутствующую страницу, следует использовать директиву ErrorDocument. Директива требует два аргумента. Первый ——это стандартный код ошибки HTTP, второй —— действие, которое необходимо выполнить при возникновении ошибки. К примеру, если какой-либо запрос привел к стандартному сообщению сервера "Файл не найден'* (такой ошибке соответствует код 404), то настроенное соответствую щи м образом сообщение можно вывести, к примеру, при помощи следующей директивы:
ErrorDocument 404 "Нзвиняте, но ваш запрос неверный, так как %s".
Символом %s сервер заменяет информацию об ошибке, В качестве второго параметра директивы ErrorDocument можно указать относительный путь к файлу, информация из которого будет отправлена клиенту:
ErrorDocument 404 /error/404,html
Также возможно перенаправление посетителя по абсолютному адресу:
ErrorDocument 404 http://www.unn< ru
И наконец, можно использовать CGI-сценарий, генерирующий сообщение об ошибке, указав к нему путь
ErrorDocument 404 /cgi-bin/404.cgi
В качестве другого примера можно привести заголовок типа Location, с помощью которого пользователь перенаправляется на нужную страницу (листинг 3.4),
Г
^^^^^^^^^^Ш,建"义.'^^У,:УУ2У «4ищ^м^шч • • м • ч • * • * г *t 羅:***』^^у^^^^м: uп幫j*齡±у<,*д貪装界"азе^иргу+иыM^Jt壑肥世瞪*;tJ;H ¥,¥
Листинг 3*4 ・ Перенаправление с помощью заголовка Location
<?php
header (1fLocation: http://www.softtime.ru/"); exit;
?>
В этом случае браузер также получает статус-код redirect( 302).
〔 Замечанье )
В этом случае отсылка статус-кода происходит, только если до этого уже не был отослан статус-код такого же типа с любым аргументом Зхх.
Для успешной работы Web-приложений, работающих с заголовком Location, и независимости их от размещения в сети следует использовать относительные UR1 в качестве аргумента для заголовка Location или динамически сформированные абсолютные UR1. Для динамического формирования абсолютного URI можно воспользоваться переменными $_SERVER[ ' HTTP_HOST,)И $_SERVER [,PHP^SELF1 ] (ЛИСТИНГ 3.5).
е абсолютного URIе абсолютного URI
е абсолютного URI
е абсолютного URI
<?php
header (^Location: http://n.$_SERVER[ 'HTTP一HOST']
.di rname ($_SERVER [* PHP_SELF1 ])
?>
Приведем еще пример. Если необходимо, чтобы посетителю задавался вопрос о сохранении или открытии высылаемых ему файлов, таких как сгене・ рированный PDF-документ, МОЖНО ИСПОЛЬЗОВать заголовок ContentDisposition. позволяющий ускорить отображение браузером диалога Сохранить (листинг 3.6).
^^;ду^^^^^^^^у^у* 喏■<■!里刍4*^1^^工^^^54 ^^Mt^^^M^K'・y • : 1…"程 ^^^^W,“ v*':'“:"・^j^SS^}4'y^
;Листинг 3.6. Заголовок Content-Disposition
<?php
header("Content-type: application/pdfn);
header{11 Content-Disposition: attachment; filename=download.pdf"); // вывод pdf-файла ?>
Управление кэшированием
Механизм кэширования применяется с целью оптимизации пересылки данных между клиентом и сервером. Запрошенный по НТТР-*протоколу пользователем документ может быть сохранен в кэше промежуточного сервера, и при повторном его запросе будет выдаваться посетителю без обращения к источнику. РНР-скрнпт, использующий заголовки, взаимодействует с браузером посредством отправки соответствующих статус-кодов, о которых говорилось выше.
Кэши принято подразделять на два вида: локальные и глобальные. Локальный кэш ——это кэш, создаваемый браузером клиента, а глобальным называется кэш, расположенный на прокси-сервере провайдера (или организации, в которой имеется свой внутренний прокси-сервер).
( Замечание )
Прокси-сервером, в отличие от обычного сервера, предоставляющего доступ к какому-либо ресурсу, называют сервер-посредник, расположенный между клиентом и обычным сервером, В отличие от шлюз-сервера, осущесталяющего обычное транслирование запросов от клиента и ответов сервера, в задачи проксисервера входит предоставление дополнительных услуг, таких как преобразование медиа-типа, анонимная фильтрация, сжатие протокола, кэширование и др.
В большинстве случаев кэширование позволяет ускорить работу с Web и значительно снизить трафик в сети, но иногда кэширование может мешать работе Web-приложениЙ. Если посетители часто загружают страницы Web- сайта с редко изменяющейся информацией, к примеру, расписание пригородного транспорта на весенне-летний период, в этом случае кэширование полезно, поскольку экономит трафик и уменьшает время запроса. Когда пользователь загружает динамические страницы, например, активного форума, кэширование, без сомнения, вредно, поскольку содержимое таких страниц меняется слишком часто.
Рассмотрим некоторые наиболее часто встречающиеся ситуации. Как правило, возникает задача в запрете на кэширование Web-страниц. Однако при этом необходимо учитывать ряд моментов:
口 никогда не сохраняются в кэше данные, передаваемые методом post;
□ страницы, обращение к которым происходит по URL, содержащим параметр в запросе (index.php?id^4)^ по умолчанию в кэше не сохраняются, т. е. кэширование таких страниц происходит только при специальном указании, которое рассмотрено ниже.
Таким образом, остается не так много случаев, когда необходим принудительный запрет не кэширование- В том же случае, когда это действительно необходимо, решить эту задачу можно путем помещения в код одного из следующих заголовков (листинг 3.7).
;产;1yx炉岑斗陷产!jt:^岩个3^”*¥,胃事 ^^^^^^^Yj 广&^^^gj8^^^^^^a^^3^^:."^^^^ 胃工学季弋"н.уу ***,±*中*;",^卷f
:Листинг 3.7. Запрет на кэширование
〃 любая дата в прошлом
header("Expires: Mon, 23 May 1995 02:00:00 GMT"};
header (MLast-Modified: ”・gmdate("D, d M Y H:i:sH) . " GMT*');
header{"Cache-Control: no-cache, must-revalidateM);
header (" Pragma.: no-cachen);
Поле Expires задает дату, по достижении которой документ считается устаревшим. Поэтому задание для этого заголовка уже прошедшей даты предотвращает кэширование данной страницы.
Поле Last-Modified определяет дату последнего изменения Web-страницы. Если с момента последнего обращения к ней прокси-сервера значение этого заголовка изменилось, происходит повторная загрузка страницы. Поэтому присвоение этой директиве текущего времени при каждом обращении запрещает кэширование.
Указание этого поля актуально только для статичных Web-страниц, так как при формировании динамических страниц, например, сгенерированных при помощи PHP, происходит автоматическое внесение изменений в заголовок Last- Modified.
Поле Cache-Control предназначено для управления кэшированием. Если в качестве его значения указать no-cache, то это также приводит к запрету кэширования. В устаревшем стандарте HTTP/1.0 для запрета кэширования нужно Присвоить значение no-cache ПОЛЮ Pragma.
Приведенный код предусматривает все возможные варианты запрета кэширования. Как правило, такая избыточность не нужна, и в большинстве случаев достаточно отправки только первого из заголовков:
header(^Expires: Mon# 23 May 1995 02:00:00 GMT");
Теперь рассмотрим случаи, когда кэширование может быть полезно. Допустим, часть страниц Web-сайта меняется очень редко, и их уместно сохранять в кэше. Это можно сделать при помощи следующих заголовков (листинг 3.8).
*%:卞у碑%%?[h;■*./嵯凡對鹭域咨/翼'*4:露也冲胃碑將¥唸羅驾"3■:;'匸二'中骂>♦:'.,¥:':/: ?"* " "' .,弋演小^錮鏗国密$"
:Листинг 3.8. кэширование документов 緜霧節—/:碘绘—忌
header ("Сache-control: public'*);
header("Cache-control: private4);
Атрибут public применяется в тех случаях, когда необходимо разрешить кэширование на прокси-серверах. Атрибут private указывает, что информацию нельзя хранить на публичных серверах н ее необходимо сохранять только в локальном кэше браузера пользователя.
Можно поступать более конструктивно. Допустим, на сайте раз в час происходит обновление новостей, соответственно, до наступления нового часа эти данные можно хранить в кэше (листинг 3.9).
r^j^ "蹴開之鼬叫停:瞿挡對*卅之図喝^ 禮終喲!髓々終喳後拓决绍 ^^^•■9昱*気"*昌w罔「!|^^^?塁*.*,m也,"^^!u開^^!|(||j^^^^^^|{^歸吃般,嘴L',X*:h : ;Листинг 3.9. Новости в кэше хранятся в течение часа. ;[ ::;£
header(HCache-control: public”);
header(иCache-control: max-age=3600M);
Аналогично можно поступать с данными, обновляющимися раз в неделю, и т. д. Необходимо выделить временной интервал, в течение которого содержимое страницы остается постоянным.
; Замечание )
Ряд предкомпилированных серверов, например, русская версия Apache, по умолчанию снабжают все страницы запретами на кэширование.
Более совершенный способ управления кэшем основан на анализе предоставляемых данных. Этот прием удобно испояьзовать при выдаче изображений, больших документов, т* е. всех данных, которые изменяются очень редко или не изменяются вообще.
Пусть при обращении по адресу http://www.softtinie.nl/dic.plip7idu25 посетителю отправляется изображение, которое извлекается из базы данных. Тогда скрипт, сохраняющий изображение в кэше, может выглядеть следующим образом (листинг ЗЛО).
<?php
// Адрес сервера MySQL
$dblocation = "localhost";
// Пользователь
$dbuser = "user";
// Пароль
$dbpasswd = "password";
// Осуществлнем соединение с базой данных
@mysql_connect($dblocation,$dbuser,$dbpasswd);
6 Зак. 1181
// Осуществляем запрос на извлечение картинки из таблицы documents $doc = mysql_query ("dbase",
"SELECT docs, type FROM documents WHERE i&=$id");
// Отправляем НТТР-эаголовки
header (^Cache-Control: public# must-revalidate*1);
header (nVary: Content-ID'1);
header("Content-工D: n .md5 (mysql_result($docf 0, "docs”)));
header (° Content-type: r, .mysql_result ($doc, 0, "type"));
// Выводам картинку в окно браузера
echo mysql_result ($doc, 0# "docs'1);
// Закрываем соеданение с базой данных mysql^close ();
?>
В листинге 3.10 вычисляется хеш-код извлекаемого из базы данных документа по алгоритму MD5. Полученное число передается в качестве значения заголовка Content-ID — пока содержимое документа не меняется, контрольная сумма будет постоянной. Как только значение хеш-кода изменится, это послужит сигналом для прокси-сервера, что документ в кэше потерял актуальность и его необходимо обновить.
Кроме рассмотренных способов управления кэшированием при помощи отправки заголовков PHP 5 предоставляет еще несколько функций, с помощью которых можно управлять кэшированием. Мы рассмотрим две основные из ннх*
Функции session^cache_limiter и session_cache_expire
Функция session一cache.limiter возвращает и/или устанавливает ограничитель текущего кэша. Синтаксис:
string session_cache_limiter ([string cache_limiter])
Если определен необязательный параметр cache_limiter, то текущий ограничитель меняется на новое значение.
Ограничитель кэша контролирует заголовки HTTP, посланные клиенту. Выше говорилось, что установка ограничителя кэша в nocache позволяет отключить клиентское кэширование. Значение public, наоборот, установит постоянное кэширование. Так же как и при использовании соответствующего заголовка, параметр может принимать значение private.
( Замечание )
В режиме private заголовок Expire, посланный клиенту, может вызвать неоднозначность в браузерах, построенных на основе Mozilla. Этой проблемы можно избежать, используя вместо private режим private_no_expire.
В таком режиме заголовок Expire никогда не будет посылаться клиенту.
Ограничитель кэша восстанавливается в значение по умолчанию, хранимое в session_cache_iimiter, в начале запроса. Из этого следует, что нужно вызывать session_cache_iimiter для каждого запроса (и до вызова функции session_start).
Рассмотрим пример с этой функцией, в котором ограничитель кэша устанавливается в режим public (листинг 3J1).
:]:* W: !,• у 里:"^7 ‘ :惱个:依符•w*, ••: ■^i^f 吧・[#]."卫里1*111 庁.豐,以 y^4tf*i?;4J
… '….…八…"■『… '….…八…"■『:Листинг 3.11* Функция ee3sion_cache__limitGr
… '….…八…"■『
… '….…八…"■『
• •*#'Sxi •* .5^^^^. ;.i d^1.^^^.、^^."^^.,了Kv^\2..t;:. ;・..3^?^> ;t?.^^^^”.^^^^^^. ft» i^j^^c^ee...,/*»,N Y* 了工夕.>7^^ У
<?php
// устанавливаем ограничитель кэша в 『public1
session_cache_limiter {,public,);
$cache_limiter = session_cache_limiter ();
?>
Функция session_cache_expire()возвращает текущее время жизни кэша и имеет следующий синтаксис:
int session__cache_expire ((int new_cache_expire])
Необязательный параметр new_cache_expire определяет время жизни кэша в минутах (по умолчанию этот параметр равен 180). Если вызов функции session_cache_expire () осуществляется с параметром new_cache_expire, ТО текущее время жизни (180 минут) заменяется на значение, переданное в этом параметре (листинг 3.12).
「: ^^^^^^^^^^д^^^学^^■.■,"■『:炉,;後^^^^,/抖*."^wy /:單 ^^^举!^笠等,"*^?;[" ^дц?,"^^^^^^^^^^^^•空己 ;Листинг 3*12. Изменение текущего времени жизни кэша
I* •,• •;.;”・“西爾i^ I『Д^^^5%7・ ri ■;;■ ■■^» ■'?■• ***1^'・,**',at-任 VT・;■■• • • »w iX* • *фЛ^и«» ^* ■'• I ■ ■ ;;';* ;;•、;;;/『*♦ *;H ;*; * • ;';\ ・『#•*'・* • f;*****';'; ; •,・;'插 Л^уЛ'гмЛ»'^» ,
<?php
// устанавливаем ограничитель кэша в 1private *
se s s ion_cache_limi ter(* private');
$cache_limiter = session__cache__liniiter ();
// устанавливаем время жизни кэша, равное 30 минутам session_cache_expire(30);
$cache_expire = session_cache_expire();
// запускаем сессию session_start();
?>
Примеры НТТР-заголовков
В этом разделе приведены наиболее часто встречающиеся НТТР-заголовки, за исключением заголовков, связанных с кэшированием, поскольку они были подробно рассмотрены выше.
[ Замечание )
Полный перечень НТТР-заголовков можно найти в стандарте HTTP 1.1, описанном в RFC2616 (http://www.w3.org/Protocols/rfc2616/).
Заголовок Accept
Заголовок Accept позволяет уточнить медиа-тип, который предпочитает получить клиент в ответ на свой запрос. Этот заголовок позволяет сообщить серверу, что ответ ограничен небольшим множеством предпочитаемых типов. Синтаксис заголовка выглядит следующим образом:
Accept = "Acceptп ":" #《media-range [ accept-params ])
media-range = ( "*/*" | ( type "/" ***** ) I ( type "/" subtype )) * £ ”;"parameter )
ас cept-params ="q" "=" qvalue *( accept-extens ion )
accept-extension - ”;" token ( "=" ( token | quoted-string }]
Символ * используется для группирования типов в медиа-ряду. К примеру, символом */* задается использование всех типов, а обозначение type/* определяет использование всех подтипов выбранного типа type.
Медиа-типы отделяются друг от друга запятыми.
Каждый медиа-ряд характеризуется также дополнительным набором параметров. Одним из них является так называемый относительный коэффициент предпочтения q, который принимает значения от 0 до I, от менее пред- почитае\гых типов к более предпочитаемым. Использование нескольких параметров q позволяет клиенту сообщить серверу относительную степень предпочтения для того или иного медиа-типа,
广 Замечание )
По умолчанию параметр q принимает значение 1. Кроме того, от медиа-типа он отделяется точкой с запятой.
Пример заголовка типа Accept:
Accept: audio/*; q=0.2r audio/basic
В данном заголовке первым расположен тип audio/*, включающий в себя все музыкальные документы и характеризу1ощийся коэффициентом предпочтения 0.2. Через запятую указан тип audio/basic, для которого коэффициент предпочтения не указан и принимает значение по умолчанию, равное единице. Цитируя RFC2616, данный заголовок можно интерпретировать следующим образом: "Я предпочитаю тип audio/basic, но мне можно также слать документы любого другого audio-типа, если они будут доступны, после снижения коэффициента предпочтения более чем на 80%”.
Пример может быть более сложным.
Accept: text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x^c
Этот заголовок интерпретируется следующим образом: “Тнпы документов text/htmi и text/x-c являются предпочтительными, но если они недоступны, тогда клиент, отсылающий данный запрос, предпочтет text/x-dvi, а если и его нет, то он может принять тип text/piain°.
Заголовок Accept-Charset
При помощи данного типа заголовка определяется предпочитаемая клиентом кодировка. Синтаксис данного заголовка приведен ниже:
Accept-Charset = м Accept-Cha г setн м :**!#( (charset | "*"}[”;同可”』“ qvalue]^
Коэффициент предпочтения q имеет точно такой же смысл, как и для предыдущего типа заголовка.
К примеру:
Accept-Charset: .isQ-8859-5, unicode-1-l; 干0.8
Данный заголовок сообщает о предпочтении клиентом кодировки iso・8859-5 перед Unicode-Ы.
Заголовок Accept-Language
Этот тип заголовка позволяет определить предпочитаемый язык. Синтаксис: Accept -Language = "Accept-Language" ”:"!.#{language-range [”; ””q”“=” qvalue]>
Можно привести следующий пример:
Accept-Language: ru, en; q=0.7
Пример можно интерпретировать следующим образом: "Клиент предпочита' ет русский язык, но в случае его отсутствия согласен принимать документы на английском".
Заголовок Allow
Данный тип заголовков определяет набор методов, поддерживаемых для идентификации Request-URI. Синтаксис:
Allow = "Allow** "” #Method
Пример:
Allow: GET, HEAD, PUT
( Замечание )
Прокси-серверы ни при какмх условиях не модифицируют этот тип заголовков, даже если не определены методы, которые в нем содержатся.
Заголовок Connection
Этот заголовок, как правило, используется в единственном варианте: Connection: close
Данная инструкция сообщает о том, что соединение должно быть разорвано сразу после комплектации сообщения.
Заголовок Date
Тип Date применяется для установления даты и времени, в течение которого Web-страница является актуальной. Синтаксис:
Date = "Date" ":" HTTP-date
Дата HTTP-date устанавливается в соответствии со стандартом RFC 822, (http://www.w3.org/Protocols/rfc822/) как показано ниже в примере: Date: Tue, 15 Nov 1994 08:12:31 GMT
Заголовок Expires
Этот тип заголовка противоположен предьщущему и устанавливает дату, по достижении которой Web-страница считается устаревшей. По синтаксису и использованию этот заголовок аналогичен заголовку Date: Expires = "Expires" ":” HTTP-date
Заголовок From
Заголовок From устанавливает адрес электронной почты, передающийся в User-Agent. Синтаксис:
From = "Fam" ":" mailbox
Пример:
From: kuznetsoyOsofttime,ru
Заголовок Last-Modified
По этому заголовку серверу можно сообщить время последней моднфика- ции документа. Синтаксис:
Last-Modified = HLast-ModifiedH ":" HTTP-date
К примеру:
Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
Заголовок Location
При помощи этого заголовка посетителя можно перенаправить на нужную Web-страницу. Синтаксис:
Location = "Locmtion" “:” absoluteURI
К примеру:
Location: www.softtime * ru
Заголовок Referer
При помощи этого заголовка можно установить значение поля Referer. Синтаксис:
Referer = MReferern ":" £ absoluteURI | relativeUKI )
К примеру:
Referer; http://www.softtime.ru
Практический пример:
HTTP-аутентификация средствами PHP
Для отправки сообщения ^Authentication Required'1 клиентскому браузеру можно использовать функцию header о, которая вызовет на стороне клиента окно для ввода имени и пароля. После ввода посетителем идентмфикацион* ных данных они отправляются серверу и доступны в PHP-сценарии через переменные $PHP_AUTH_USERs $FHP_AUTH_PW И $PHP_AUTH_TYPE, В КОТОрЫХ УСТЕЬ новлены имя пользователя, пароль и тип аутентификации соответственно.
( Замечание )
HTTP-аутентификация в PHP возможна только при запуске PHP как модуля Apache и следовательно, недоступна в CGI-версии.
Замечание
В настоящее время поддерживается только один тип аутентификации — Basic.
Пример НТТР-аугентификации приведен в листинге 3.13.
…当響『理mw飞:,,:當," "у:.窗押:::"*Тv簞茨型赞*缝風睁缪Г佟押吟与資聲
Листинг 3.13. Пример НТТР-аутентификации
<?php
if (! isset ($_SERVER「PHP_AUTH_USERf ]))
header("WWW-Authenticate: Basic realm-\"My Realm、"");
header{"HTTP/1.0 401 Unauthorized”);
// выводим текст для случая, если пользователь нажал кнопку Cancel echo "Вы не пожелали авторизоваться'1;
exit;
}
else
{
echo и<р>Привет, {$_SERVER[fPHP_AUTH_USER']}•</p>”;
echo ”<р>Вы ввели ($_SERVER[1$PHP_AUTH__PW1]} в качестве свого пароля.</р>м;
Замечание )
Будьте внимательны при наборе строк заголовка. Чтобы максимально гарантировать совместимость со всеми клиентами 4 первая буква ключевого слова Basic должна быть набрана в верхнем регистре, управляющая realm-строка должна быть заключена в двойные кавычки (не одинарные), и только один пробел должен предшествовать коду 401 в строке нттр/1.0 401 заголовка.
Практический пример: получение документа по НТТР-протоколу
В этом разделе приведен пример скрипта, который по НТТР-протоколу по лучает документ с сервера (листинг 3.14).
<?php// открываем сокет<?php// открываем сокет
<?php
// открываем сокет
<?php
// открываем сокет
$fp = fsockopen (r,www,myserver. ru", 80, &$errno, &$errstr# 30);
// посылаем GET-форму в виде "UKL+xccM
fputs($fp,"GET /files/doc.zip НТГР/1< OXnHOST: www.myserver.ru \n\n");
// читаем все до начала следующего документа
while <fgets ($fp, 2048) !=Г|\г\пн && ! feof {$fp));
// копируем тоI что прочитали в переменную $buff unset($buff);
while{1feof($fp)) $buff.=fread($fp,2048);
// закрываем сокет
fclose($fp);
echo мФайл загружен, размер файла: ".strlen($buff) » и bytes » и; flush ();
// записываем загруженный файл в другой, к примеру, в файл
// с названием download»out. Если мы скачали ZIF-файл, то, переименовав
// download.out в download.zip, можно дальше работать с этим файлом как с // обычным ZIP-архивом.
$f=fopen {"download.out1',”wb+");
fwrite{$f,$buff,strlen($buff));
fclose($f);
?>
В начале скрипта открывается сокет, затем посылается GET-форма, читается и анализируется ответ. Ответ состоит из заголовка и тела документа. Нас интересует только тело документа. Поэтому все, что идет до документа, мы пропускаем, зная, что заголовок от тела документа отделяются строкой и\г\п". Заголовок мы читаем функцией fgets, а бинарные данные —— функцией fread.
Глава 4
"Хитрости" PHP
В этой главе рассматриваются "тонкие0 вопросы в стиле “handcook", связанные с эффективным программированием на PHP 5: создание эффективных регулярных выражений, некоторых полезных функций PHP 5, разработка собственной функции подсветки кода PHP, примеры небольших вспомогательных Web-приложений, используемых в дальнейших главах.
PHP и JavaScript
В этом разделе обсуждаются особенности работы со скриптами на JavaScript в PHP. В отличие от PHP, скрипты на JavaScript выполняются на машине клиента, в то время как PHP — серверный язык программирования. В отличие от технологии Java или ASP.NET, он не нмеет в своем составе средств для работы на клиентской стороне. Поэтому для создания эффективных Web-приложений необходимо комбинировать скрипты на PHP и JavaScript. Существуют две возможности такого взаимодействия: передача переменных из JavaScript в PHP н динамическое формирование скриптов на JavaScript средствами PHP*
Передача переменных из JavaScript в PHP
Одной из распространенных задач является определение разрешения экрана и глубину цвета монитора посетителя страницы средствами JavaScript с последующей передачей этих данных в РНР-скрппт.
:Замечание )
Это довольно часто встречающаяся задача, особенно при написании счетчиков посещений и создании "динамического дизайна41.
Скрипт JavaScript, выполняющий необходимые действия, размещен в файле index.html, содержимое которого приведено в листинге 4.L
:開,***&^A^fi^,感察..:?*,"一*:c*3v,4芝;,:没‘;""'.,'','±,'产〔''.:."'**'七%'''{学%融,"也^^"'""*:|:単,岂供::11.':****"*1**'^'.**,'球¥碑'唱.^4=;*.":'眠["¥^И^УЧ№й^1^^.
:Листинг 4.1. Файл index.htmlj
<Script Languag^'^avaScript1^
var height=0;
var width=0;
colorDepth = screen.colorDepth;
if (self.screen)
width - screen.width height = screen*height
else if (self*java)
(
var jToolKit = j ava.awt.Toolkit.getDefaultToolkit();
var scrsize = jToolKit.getScreensize();
width h scrsize.width;
height = scrsize.height;
}
if (width > 0 && height > 0)
{
// Производим перенаправлениe на скрипт counter.php, передавая в
// параметрах width, height и color ширину, высоту и глубину цвета * window・location.href = "http://1ocalhost/view *php?
width=fr + width +
,l£height=H + height + ”&cclor=" + colorDepth;
} else exit();
</Script>
После выполнения этого кода происходит автоматический переход на страницу view.php, на которой в окне браузера выводится разрешение экрана и глубина цветопередачи (листинг 4.2).
( Замечание )
Полученную информацию можно помещать в базу данных для набора статистики о наиболее распространенных разрешениях экранов, используемых посетителями сайтов.
;Листинг 4.2. Файл view.php;Листинг 4.2. Файл view.phpI ■ ■■ ■ ъ ■>>'■ ■■"■ 2 ;+“;+『《”*« jC; • ;*I ■ ■■ ■ ъ ■>>'■ ■■"■ 2 ;+“;+『《”*« jC; • ;*
;Листинг 4.2. Файл view.php
;Листинг 4.2. Файл view.php
I ■ ■■ ■ ъ ■>>'■ ■■"■ 2 ;+“;+『《”*« jC; • ;*
I ■ ■■ ■ ъ ■>>'■ ■■"■ 2 ;+“;+『《”*« jC; • ;*
<?php
echo |'П1ирина : '\ $_GET [1 width1 ];
echo иВысота : ” * $_GET[1height『];
echo nЦветовое разрешение : ".$_GET[1 color,]; ?>
Как видно, работа с данными из JavaScript аналогична работе с данными, отправляемыми методом get.
Как проверить, включены ли cookies?
Нередко посетители отключают cookies в настройках своих браузеров. Для корректной работы в Web-приложение, использующее cookies, необходимо помещать код, проверяющий, включены ли cookies у посетителя. Такая проверка позволит использовать другой механизм аутентификации, например, сессии, или просто позволит вывести сообщение о необходимости включить cookies. Пример такой проверки приведен в листинге 4.3.
,里犬七,W廿単逬男:,,吟+?2呼¥,乜豈黑^^^^y^:g,・AV■,产丁軟二m丄y:^^^广‘一"户七卞.^^注寸"*^^般電堂У"*Т"丹6,**謁寸,£":〔*>'煙',"Х
\.п^™^:3^ов^^
<?php
if (! isset ($_GET「probe 4))
{
// устанавливаем cookie с именем "test"
if (setcookie {"test"「set"))
I
/ / посылаем заголовок переадресации на страницу, // с которой будет предпринята попытка установить cookie header{"Location: $PHP_SELF?probe=setn);
}
)
else
{
if<!isset($_COOKIE["test"]))
echo(пДля корректной работы приложения необходимо вклкгчить cookieп);
else
{
// cookies включены, переходам на нужную страницу,
// послав заголовок, содержащий адрес нужной страницы header(нLocation: $PHF_SELF”);
}
}
?>
Для проверки корректности работы с cookies при помощи функции setcookie о устанавливается пробное значение cookie. Функция setcookie() в данном случае принимает два параметра, первый из которых —— имя, а второй ——значение cookie.
Функция имеет следующий синтаксис:
bool setcookie (string name [, string value I, int expire [, string path
[,string domain [, int secure]]]]])
Здесь:
口 name ——имя cookie;
value ——значение, хранящееся в cookie с именем пате;
口 expire ——время в секундах с I января I970 года. По истечении этого времени cookie уничтожается;
path — путь, по которому доступен cookie;
口 domain — домен, из которого доступен cookie;
口 secure — директива, определяющая способ передачи cookie. По умолчанию эта директива имеет значение 0, что означает возможность доступа к cookie по обычному запросу HTTP, если значение параметра равно 1, cookie передаются по защищенному протоколу HTTPS.
Функция setcookie о в примере из листинга 4.3 возвращает true при удачном выполнении и false — в противном случае. В случае успешного выполнения функции установки пробного cookie происходит автоматическая перегрузка страницы за счет передачи заголовка ^Location* с именем текущего файла и параметра probe, позволяющего перейти ко второму этапу проверки.
Получая параметр probe, скрипт проверяет содержимое суперглобального массива $_соок工е на наличие в нем переменной с ключом test. Если такая переменная имеется, следовательно значение cookie было успешно установи лево на машине клиента.
0 профилировании кода
Нередко возникает необходимость определить, какой участок кода выполняется дольше остальных и замедляет работу всего Web-приложения. Для решения этого вопроса прибегают к профилированию кода.
Профилированием кода называется процедура, в ходе которой измеряется время выполнения различных функций программы. Получив такую информацию, можно легко выяснить, на что в сценарии тратится большее время, и попытаться оптимизировать этот участок кода.
Для профессионального профилирования можно воспользоваться различными программам и -п рофил и ровщикам и, к примеру, свободно распространяемым профилировщиком компании Zend, который можно найти на сайте http://www.zend.coin/stort/pro<lucts/. Однако потребность в использовании профессиональных профил и ровщи ков возникает не очень часто, и в большинстве случаев можно профилировать сценарии PHP собственными силами.
Для профилирования сценария необходимо измерить время выполнения сценария путем сохранения времени начала и конца выполнения сценария с вычислением разницы между этими величинами. Сравнивая полученный результат с временами выполнения других скриптов Web-приложения, мож* но найти наиболее требовательный к ресурсам участок кода.
С целью определения времени выполнения с требуемой точностью используется функция microtime, возвращзющая строку видз "msec sec", где msec — составляющая в микросекундах, a sec — время в секундах, прошедшее с 1970 года (листинг 4.4).
;Листинг 4.4. Определение времени выполнения сценария
<?php
echo microtime О ;
?>
Результат может получиться, к примеру, таким: 0.48023600 1076713360
Такое представление не очень удобно для вычисления разницы во временн, поэтому его необходимо преобразовать в числовой вид. Иначе говоря, строку 'msec sec'1 необходимо преобразовать к виду "sec.msec ”. Сделать это можно с помощью следующего сценария (листинг 4.5).
f窪吋 L^^g^^ v ^^^j^y^^j^^y^^.^^^^^l^^^^^^iy^y 审变^^^¥t^^^S^^£^^ " ;"**л* q 甘鏡葛屮 畤^^"":"斗阳*^^"* 爲 1^「冷 j пшлчш^^м
:Листинг 4.5. Преобразование показателя времени в числовой вид
闕IKi $ 富鹹齡#;VF;;j龄•烹■ .з*;:ЯЖ??:力w **施■.需耨轟 ¥;?乐т:品議:й^1?^^^^;方 話爲''■京'+雨图^^)^* 裁翁^^5^|^禽森:?£曲^氈;■撈^^^^^^fi^WS^^^S:
<?php
$part_time = explode (1 f rftiicrotime ());
$ireal_time = $part_time [1]. substr ($part_time [0], 1);
?>
Здесь при помощи функции explode строка, принимаемая во втором параметре, разбивается на подстроки по разделителю, передаваемому в первом параметре (в нашем случае это символ пробела). Результат возвращается в виде массива, элементы которого содержат подстроки разбиваемой строки.
( Замечание )
Функция explode может также принимать в качестве третьего необязательного параметра число limit, ограничивающее число элементов в результирующем массиве.
Для получения разности между двумя числами, размещенными в строке, можно воспользоваться функцией bcsub. К примеру так, как в листинге 4.6.
?殁帛畛,$^^^j段^^^^tl jj^j^^X^^t^j^' ^^呼、Y‘迪^^tfjtfyjT'ffl^^jii^W^Ci^S^tt^^M^ ^Mt^^lO^^^^ifiWf^■燃野 ^Листинг 46 Функция bcsub
<?php
$diff_time = bcsub{$stop_time『 $start_time, 6); ?>
Функция принимает помимо двух строковых параметров ($stop_time, $start_time) также число знаков после запятой, которые необходимы в возвращаемом функцией результате.
Конечную функцию get time для профилирования кода следует разместить в смайле gettime.php (листинг 4.7).
Г Листинг 4.7. Файл gettime.php<?phpfunction gettime()Г Листинг 4.7. Файл gettime.php<?phpfunction gettime()ижж・.牡 3,*(|>|> ICF, [fc^liOXa^UL^<5>yFW”Hj* у *F,U*1 jAOLyA* U ***AtAH/&mJ «.л、**rFK”. MR、•• it! >*•<»■ M'f,Я1лл • Яг«сд7|т»?а лЛ ;A?t*『/Г«^К«,“” ♦“ижж・.牡 3,*(|>|> ICF, [fc^liOXa^UL^<5>yFW”Hj* у *F,U*1 jAOLyA* U ***AtAH/&mJ «.л、**rFK”. MR、•• it! >*•<»■ M'f,Я1лл • Яг«сд7|т»?а лЛ ;A?t*『/Г«^К«,“” ♦“
Г Листинг 4.7. Файл gettime.php
<?php
function gettime()
Г Листинг 4.7. Файл gettime.php
<?php
function gettime()
ижж・.牡 3,*(|>|> ICF, [fc^liOXa^UL^<5>yFW”Hj* у *F,U*1 jAOLyA* U ***AtAH/&mJ «
.л、**rFK”. MR、•• it! >*•<»■ M'f,Я1лл • Яг«сд7|т»?а лЛ ;A?t*『/Г«^К«,“” ♦“
ижж・.牡 3,*(|>|> ICF, [fc^liOXa^UL^<5>yFW”Hj* у *F,U*1 jAOLyA* U ***AtAH/&mJ «
.л、**rFK”. MR、•• it! >*•<»■ M'f,Я1лл • Яг«сд7|т»?а лЛ ;A?t*『/Г«^К«,“” ♦“
$part_time ^ explode (' * ,inicrotinie 0 );
$real_time 二 $part_time [1] .substr ($part_time[0], 1); return $real_time;
В качестве примера можно привести профилирование различных способов преобразования типов в PHP. В листинге 4.8 сравним преобразование в стиле С с преобразованием, осуществляемым с помощью функции settype.
Листинг 4.8. Профилируем различные способы преобразования типов
• «•■•■*^»*J₽«**'»^?»*»*»»**^««»»*^»*eVe»e» *<•••■■■ ••■•••ii'l»«e«*<**»*i<<»l>J'e»e»aee.eVe«eJ.B ••;;*,“;••・3;”“““・,.力.;:.,♦♦;,."•; •♦“,•力“,“・^^Л;.・.;C“.t>“.・,
<?php
include "gettime.php";
$var 二"5%
// профилируем преобразование типа с помощью функции settype $start_tin\e = gettiine ();
settype($var,integer);
$stop_time = gettiwef);
$diff_time = bcsub ($stop_timef $start_time, 6);
echo("преобразование типа функцией settype: н.$diff_time.M<br>,'); // профилируем преобразование типа в стиле С $start_time 工 gettime();
$var = (int)$var;
$stop_time = gettirne ();
$diff_time = bcsub ($stop_time, $start_time, 6);
echo("преобразование типа в стиле С: "* $diff time."<Ьг>и); ?>
Результат выполнения скрипта мо*ет выглядеть следующим образом:
преобразование типа функцией settype: 0.000083
преобразование типа в стиле С: 0.000026
Видно, что использование функции settype для преобразования типа более чем в 3 раза дольше, чем преобразование типа в стиле С.
Получение информации об IP-адресах и доменах
В этом разделе мы разработаем простое Web-приложение, позволяющее посетителю узнать, на кого и где зарегистрирован какой-либо IP-адрес, при помощи сервиса whois. Для этого мы с помощью функции fsockopen установим соединение с сервером whois.ripe.net, предоставляющим подобный сервис. Код приложения приведен в листинге 4.9.
( Замечание )
Сервис whois (от англ. Who Is?) является официальным сервисом центра RIPE, предоставляющим информацию о зарегистрированных на данный момент доменных именах, о DNS-cepeepax, поддерживающих IP-адреса, и о владельцах IP- адресов. Когда мы на различных сайтах проверяем, существует ли на данный момент какое-либо доменное имя, мы пользуемся именно сервисом whois. RIPE (Reseaих IP Europeens) это Европейская континентальная сеть TCP/IP, Первоначально все IP-адреса, которые мы используем в RuNet, получают RIPE Network Coordination Centre (координационный центр RIPE), который является одним из трех региональных интернет-регистраторов, поддерживающих глобальную инфраструктуру Интернета. Сеть RIPE управляется сетью EUnet. Последняя представляет собой сеть UNIX для Европы и является старейшим и крупнейшим по* ставщиком интернет-услуг в Европе. Его частью является и российское АО "РЕЛКОМ" — крупнейший поставщик интернет-услуг в России.
^•■г* 丁 **j*f ■****•-»• 1, У,:•“¥:? *”;”"】,— •••■••■»• !,J 幻盟丄弋寧 w& f£■学£,*装/[£等省£炉-号加¥争,・ Y у^^^цун U 1А"|»»»У 屮笠лад ¥”-.货!”"胃:'№-・•!
Листинг 4.9. Получение информации об IP-адресах и доменах
<html>
<head>
<ti 11е>Информация об IP-ajipece</title>
</head>
<body>
<center>
<FORM act ion= ip.php method=post>
<br>
</ix/bx/font>
<br>
<INPOT type=text name^ip size=35>
<input type=submit value=1Введите 1Р-адрес#>
</fonn>
</center>
<?php
if (! empty ($_PDST [Hlp"l))
{
// Соединение с сокетом TCP, ожидающим на сервере "whois.ripe.netH no
// 43 порту. В результате возвращается дескриптор соединения $sock* $sock = fsockopen(nwhois.ripe.netn, 43, $errno, $errstr);
if (!$SQck)
echo("$errno($errstr)”);
return;
}
else
{
// Записываем строку из переменной $_POST["ip“] в дескриптор сокета. fputs ($sock, $_POST[HipM] ^ЛгХп'1);
// Осуществляем чтение из дескриптора сокета * while {!feof($зоск))
{
echo (str_replace<”:",”: Gnbsp; **, fgets ($sockr 128))”"<br>");
}
}
// закрываем соединение
fclose ($sock);
}
?>
</body>
</html>
Как видно из листинга 4.9, скрипт содержит как саму форму для заполне- ння, так и ее обработчик. После передачи обработчику IP-адреса происходит открытие сокета при помощи функции fsockopen. Функция принимает четыре параметра, первый из которых содержит адрес соединения, а второй ——порт. В случае удачи функция возвращает дескриптор соединения $sock, а в противном случае возвращается значение false* Если при этом функции переданы два необязательных параметра $errno, $errstr, в них размещается код и текстовое описание ошибки соответственно.
( Замечание Q
Функция fsockopen может принимать также пятый необязательный параметр $timeout, определяющий максимальное время ожидания соединения в секундах.
После этого в цикле while происходит считывание информации из сокета с последующим выводом ее в окно браузера.
「 Замечание )
Для удобочитаемости после символа двоеточия ":и при помощи функции str_replace () вставляется несколько неразрывнь(х пробелов.
Результат успешного выполнения скрипта представлен на рис. 4丄
I Э| IP information - Microsoft Internet Explorer
Файл Правка Вид Избранчое Сервис Сгравкз
1 0 Назад *
0 ▼回 湧:创;'Поиск ^Избранное
寸 медиа е;Г :"
Адрес:團 http:Mocalho$Whols.php
▼| Qпереход
ССЫЛКИ А
aamni-c: techy status: notify: niiit-by: changed: source:
KVtj^-KlFH
SVG2-RIPE
ASSIGNED PA
mas^iuiuac ni
roMnuros-mki
ip-dbiu0Tipn.net 2 0001206
RIPE '
route: descr: origin: no畴: nmkby: changed: soiuce:
62.76.24S.0/22
Nizhiiv Novgorod State Uuix^ei^itx- AS8&3
noc@uicnnov.iii
ROgNKROS-MNT
uias^iuuiac.ni 2 0001208
RIPE —
person: address: address: address: address: phone: e-mail: nic-hdl: changed: changed: sowce:
Seigey \' Gorokhov
Nizlmy Novgorod State Univei sity
23. Gagai ina ave.
603 60б Nizhny Novgorod
Russia
+7 8312 33 7804
iuas(/7)ium.ac.ni
SVG2-RIPE
тай(йН1Ш1.ас nt 19950914
ky@kiae.^u 19951011 ripe'
^Готово
'厂厂
:!刃 Местная интрасеть
Рис. 4.1. Результат проверки IP-адреса
Замечание
При помощи этого же скрипта можно также получать информацию и о доменных именах. Для этого в форме для ввода IP-адреса нужно ввести доменное имя, о котором требуется получить информацию.
Немного истории
В 1982——1983 годах в Институте атомной энергии им. И. В, Курчатова с участием специалистов ряда других НИИ были начаты работы по созданию отечественной операционной системы типа UNIX, получившей название ДЕМОС (Диалоговая Единая Мобильная Операционная Система). В последующие годы ОС ДЕМОС стали стааить на различные машины, функционировавшие тогда в СССР: сначала на СМ-4, потом на Электронику-1082, Эльбрус, ЕС. Работа по созданию такой сети была отмечена премией Совета министров СССР. Компьютерная сеть Релком (от RELiable COMmunications ——надежная связь) была основана на базе РНЦ мКурчатовский институт" 1 августа 1990 года- В работах по созданию сети принимали участие специалисты ООО "Компания "Демос" (тогда — кооператив ''Демос'*), большинство из которых по совместительству являлись сотрудниками Курчатовского института. Уже к концу года было подключено около 30 организаций, среди которых были центры российской науки в Серпухове, Новосибирске, Санкт-Петербурге, Дубне. Данная сеть базировалась на технологии электронной почты, причем с возможностью переписки и на русском языке в рамках протокола UUCP/UUPC. Первый сеанс связи (с Финляндией) был проведен 28 августа того же года, а 19 сентября был зарегистрирован домен первого уровня SU — первый русскоязычный домен верхнего уровня. В сентябре же 1990 года начал работу Московский узел Интернета. Первоначально для этого использовался один компьютер Р(5-286, который впоследствии был заменен на Micro VAX 3. К декабрю 1991 года в компьютерной сети России представлены многие крупные города: Москва, Санкт-Петербург, Екатеринбург, Барнаул и другие, а также города в некоторых странах СНГ и странах Балтии. В 1992 году было создано Акционерное общество hРелком", учредителями которого стали РНЦ "Курчатовский институт*1, Технобанк и др. В ноябре 1992 года впервые организовано распространение по сети электронных версий ,гИзвестийн и некоторых других отечественных периодических изданий. В декабре 1993 года сеть EUnet/Relcom зарегистрирована в NSFNET ——опорной сети США. Этот момент можно считать началом официального существования Интернета в России. Среди стран, подключенных к NSFNET в 1993 году, перечислены Российская Федерация, Казахстан и Украина ——все эти страны получили доступ к Интернету через сеть EUnet/Relcom). 4 декабря 1993 года на собрании IP-сервиС'Провайдеров было подписано соглашение "О порядке администрирования зоны RU" (на основании этого соглашения Международный сетевой информационный центр InterNIC передал обязанности и права, связанные с администрированием зоны RU, российскому НИИ развития общественных сетей ——РосНИИРОС). А домен верхнего уровня RU был официально зарегистрирован в InterNIC 7 апреля 1994 года,
Часто используемые регулярные выражения
Регулярные выражения являются специфическим языком программирования, предназначенным для решения сложных задач обработки текста*
:Замечание )
Синтаксис регулярных выражений является достаточно сложным, и его изучение требует серьезных усилий. Наилучшим руководством по регулярным выражениям на сегодняшний день является книга Дж. Фридла "Регулярные выраже- нмя* позволяющая, по словам автора, "научиться мыслить регулярными выражениями,\
В этом разделе будет рассмотрено несколько примеров на решение наиболее часто встречающихся задач при работе с регулярными выражениями.
Одной из распространенных задач является преобразование даты из формата YYYY-MM-DD в формат DD.MM.YYYY. Эта задача решается при тюмо- 山Н функции preg_match: int preg^raatch(string pattern, string str [# array regs])
Данная функция ищет в строке str соответствие регулярному выражению, заданному в шаблоне pattern. Если соответствия подвыражений с шаблоном будут найдены, то они сохраняются в массиве соответствий regs. При этом $regs[0] содержит копию строки string, $regs [1] содержит подстроку, начинающуюся с первой сохраняющей скобки, $regs [2] хранит подстроку, начинающуюся со второй сохраняющей скобки, и т. д.
Функция возвращает число найденных соответствий. В листинге 4.10 приведен пример преобразования даты.
j Листинг 4.10. Преобразование даты
<?php
$data = ”2003-03-21”;
if (preg_match (r7 ([0-9] (4}) - ([0-9] {1,2}) - ([0-9] {1,2},) /1% 9date, $regs)) (
echo "$mgs [3] .$regs [2] .$工色gs [1]";
}
else
{
echo nНеверный формат даты: $date";
}
?>
В следующем примере проверим корректность адреса электронной почты, вводимый пользователем.
Будем исходить из того, что адрес должен иметь вид something@server.com. У адреса имеются две составляющие —— имя пользователя и имя домена, которые разделены знаком @. В имени пользователя могут присутствовать буквы
нижнего н верхнего регистров, цифры, знаки подчеркивания, минуса и точки. Для проверки разделителя между именем пользователя и именем домена в выражение требуется добавить @. Таким образом, регулярное выражение, проверяющее имя пользователя и наличие разделителя, имеет следующий вид: "/[О-9а-Z」+@[О-9a-z_A \.]+ ”
Для проверки последнего доменного имени (ru, сот) необходимо добавить следующее выражение:
n\.[a-z]{2,3JM
Символ ”「в регулярных выражениях используется для обозначения любого символа, поэтому для поиска соответствия точки в регулярном выражении этот символ экранируется:
Объединяя эти строки, можно получить следующее регулярное выражение для проверки адресов электронной почты:
”/[0-93-2」+@ L0-9a-z_A\J +\.[a-z J{2,3}/i"
Генератор паролей
Существует много различных способов генерации паролей. Одно из наиболее коротких и элегантных решений этой задачи состоит в использовании функции uniqid, применение которой позволяет генерировать пароли случайным образом. Эта функция возвращает уникальный идентификатор, основываясь на значениях текущего времени в микросекундах (листинг 4.11).
普專f^^^^^F)悶霹疗^^^曽謳嘅局覷j^^^[浦绕『越^^^^^^^^^^^^^^^皆镯尊诺季勲弊mg^^^"赞舒蹴闇寧繆| 炉我筍"鸞齊濟:送避旭杷^工 ;Листинг 4.11. Генератор паролей
<?php
$id = md5(uniqid(rand(),true)); print $ id ・'<br> *;
?>
При таком варианте генерирования пароля возвращается 128-битный хеш* код, который (или его часть) может использоваться в качестве пароля.
Подсветка кода
с помощью стандартной функции PHP
Во многих Web-приложениях требуется подсветить PHP-код: такая задача часто возникает при разработке форумов, посвященных вопросам PHP- программирования, при динамическом отображении РНР-скрнптов на страницах ресурса, в отладочных Web-приложениях и т. д. В этом разделе мы
рассмотрим реализацию подсветки синтаксиса кода при помощи стандартной функции highlight_string () (листинг 4.12),
( Замечание )
Помимо стандартной функции highlight_string (), обеспечивающей подсветку синтаксиса кода переданной в качестве параметра строки, существует также функция highlight_file (), обеспечивающая подсветку синтаксиса файла, имя которого передается в качестве аргумента.
j^干学縱忏僧呼м缰醫修婚部酸图图:^^喷举《理产瓶耳霜产^纖妣整婚1….^^^. s^wj^^w^s^^y^>^^шшшш(
:Листинг 4.12. Подсветка синтаксиса кода
// Строка, содержащая скрипт PHP
$code = y?php
if(!$flag)
{
// пишем какой-либо код
echo("Hellon);
$var = 1;
}
else break;
?>';
// вызываем функцию highlight_stгing highlight_string($code);
Как видно из листинга 4.12, строку, содержащую PHP-код, необходимо передать функции highlight_strlng(). Если в качестве второго необязательного параметра функции передать значение true, то вместо вывода результата в окно браузера функция возвратит строку с подсвеченным синтаксисом.
( Заменанзе )
Стандартные функции highlight^string () и highlight_file () подсвечивают синтаксис кода, только в том случае, если он заключен в теги <?php и ?>. Это не всегда удобно, поэтому ниже будет рассмотрено построение собственной функции подсветки синтаксиса.
Подсветка синтаксиса PHP (собственная функция)
В предыдущем разделе были описаны приемы работы со стандартными функциями подсветки синтаксиса PHP: highlight_string() И highlight_file().
Данные функции имеют два серьезных недостатка: поддерживается только подсветка PHP-кода, причем только такого кода, который размещен между тегами <?php и ?> (а также <? и ?>). Поэтому в большинстве случаев приходится применять собственную функцию подсветки синтаксиса.
В данном разделе будет рассмотрена задача построения простейших аналогов функций highlight_string {) И highlight_file (), реа-ПИЗующих собственную схему подсветки кода и, следовательно, допускающих реализацию подсветки синтаксиса языков, отличных от PHP. Строковый вариант функции будет называться shighlight()и в качестве параметра будет принимать строку, содержащую код PHP. Файловый вариант будет называться [highlight()и будет принимать имя файла с PHP-скриптом. Обе функции будут возвращать строки с разметкой HTML, обеспечивающей подсветку синтаксиса. Функции должны по возможности подсвечивать максимальное количество элементов.
Построение функции подсветки следует решать с привлечением механизма регулярных выражений, специально созданных для такого рода задач. В листинге 4.13 приведен код функции shighlight ().
[£論[同蘭痴 „:;謚:)••…"■■•"I
<?php
function shighlight(^document)
{
// Преобразуем угловые скобки для отображения HTML-тегов
$document = str_replmce("<", "<“, ^document);
$document = strreplace(">", ”>", $document);
7/ Преобразуем теги PHP <?php и ? >
$tegs = array(M'<\?php,si% "^LtA?^!", "r\?> ^i0);
$ replace = array (Ir<font color=#95001E>< ?php</font>H,
"〈font color=#95001E><?</font>"#
»<font color^#95001E>?></font>");
^document = preg_replace($tegs, $replace, $document);
// Преобразуем комментарии
$ document = preg__replace (M * ((?: fr I //) [ A\n] * | /\*. *?\*/) 1 si1' #
yfont color=#244ECC>\\K/font>HJ $document);
// Осуществляем переносы строк
$ document = preg_replace (п 1 (\n) ^i", ,,<br>\\l"r ^document);
// Преобразуем функции
^document = preg replace (" 1 ([\w]+) ([ \s] *) ( \ (] ^i1',
"<font color=#0000CC><b>\\l</bx/font>\\2 (", $document>;
// Преобразуем операторы
$separator
=array(H1\,’si”,
”,\+,5i〃, ,,f\(fsir\
'八「sin,
”'\} SP);
$replace ^ array("<font color=#lA691A>,</font>M,
$document ^
"〈font color=#lA691A>^</font>,',
"〈font color=#lA691A>+</font>"『
"〈font color=#lA691A> (</font>11,
"<font color=#!A691A>) </font>,f,
**<font color=# 1A691A>{</font〉" #
"<font color=#LA691A>}</font>M);
pregreplace{$separator,$replace,Sdocument);
〃 Преобразуем переменные PHP
$ document =
preg_replace (r, T ((\$ 1 {1 r 2}【A-Za-z」+)飞i”, n<b><font color=#0000 00>\\l</fontx/b>H, $document);
// Преобразуем строки, заключенные в одинарные и двойные кавычки $str = array(°'(\п(А\*Ч *\°) *si",
”,NEWsi”);
$replace = array(n<font color=#FFCC00>\\l</font>M, "<fcnt color^#FFCC00>\\K/font>H);
$document = preg_replace($str, $replace, ^document);
// Преобразуем зарезервированные слова $sU ^ array (нг (echo) ^i", tti (print) 1 sir,, ,f f (while) 'si", ”'{fc亡『Ю
И, (if) ,式3 и,(else)lsint ”'(switch) W,
'*» (function) 9sV'f ” 1 (array) *siT,);
$replace = array_fill《O,
count($stг),
w<b><font color=# ООО ОСО \ \ К /f ont >< /b>rr);
$document = preg__replace($str, $replace, $document);
// Возвращаем результат работы функции
return n<code>$document</code>H;
?>
Работа функции начинается с преобразования угловых скобок < и > в их HTML-представление: sit; и <; соответственно.
Следующим этапом является подсветка тегов v?php, <? и ?> темно-красным цветом. Так как на предыдущем этапе все угловые скобки были преобразованы, то регулярные выражения для этих трех тегов приобретают вид: 4it;\?php,si> ,&it;\?,si, ,\?>;,si. Эта операция осушесталяется при помощи функции preg_replace{), принимаюихей в качестве первых двух параметров массив регулярных выражений в стиле языка Perl и массив со строками замены. В качестве третьего параметра выступает строка, в которой осуществляется замена.
После преобразования тегов PHP происходит подсветка комментариев PHP светло-синим цветом. Регулярное выражение, ответственное за такое преобразование 1( (?:#l//) [A\n]*|/\*.*?\*/)^si, можно разбить на три части:
□ подвыражение /\*. *?\*/ ответственно за подсветку многострочных комментариев в стиле языка С;
□ в подвыражении(/* */)символы * экранируются обратным слэшем \, а между последовательностями /* и */ допускается произвольное число любых СИМВОЛОВ (.*?);
□ подвыражение ((?:#!//)[А\п]* является ответственным за однострочные комментарии: // (стиль C++) и 卷(стиль командных оболочек), после которых следует произвольное количество символов (включая их отсутствие) без символа перевода строки— [^\п] *. Последовательность ?: предписывает не сохранять результат для данных круглых скобок. Это позволяет не задумываться о числе сохраненных результатов и при замене с использованием функции preg_replace{), во втором параметре применять единственный сохраненный результат \\1, который относится к внешним круглым скобкам.
Для осуществления переноса строки все символы переноса строки (\п) предваряются HTML-тегом переноса строки ——<br>. Такой прием позволяет
сохранить форматирование исходного текста PHP-программы и отразить его в окне браузера.
Следующим этапом является подсветка функций. В PHP входит огромное число функций; кроме того, программисты могут вводить свои собственные функции, поэтому создание списка возможных функций, как это часто делают, нецелесообразно. Все функции объединяет то, что после их объявления обязательно следует список параметров в круглых скобках, который в общем случае может быть и пустым. Между именем функции и скобкой может бьпъ произвольное число пробельных символов или даже переводы строк, поэтому регулярное выражение принимает вид: *([\w]+)([\s]*)(\(] •si. Имя функции состоит из одного или большего числа символов, включающего обобщенный символ слов ([\w]+), после которого следует произвольное количество обобщенных пробелов ((\s] *) и экранированный символ открывающей круглой скобки ([ \ (].).
「 З^мечанве )
Обобщенный символ слова [\w] эквивалентен [a-Az-zo-9_] г а символ обобщенной)叩обела [\s] — [ \f\n\r\t\v].
Во втором параметре функции preg^repiace()используется результат, сохраненный в двух круглых скобках: \\1 соответствует имя функции, \\2 — пробелы между именем и открывающей круглой скобкой.
После этого происходит подсветка операторов темно-зеленым цветом.
Заключительный этап состоит в организации подсветки темно-синим цветом зарезервированных ключевых слов языка PHP, таких как while, if, else и т. д. Так как выделить эти ключевые слова на фоне других элементов программы достаточно сложна, они помещаются в массив и обрамляются круглыми скобками, что обеспечивает сохранение их в качестве первого параметра (\\1). Массив замены $replace, выступающий в качестве второго аргумента функции preg_repiace(), автоматически формируется при помощи функции array_fiiiо* Для обеспечения подсветки кода на других языках п рограм м и рован и я в массив $str необходимо добавить зарезервированные в этих языках слова.
После построения строковой функции подсветки синтаксиса языка PHP не составляет труда создать файловую версию этой функции —— fhighllght{), код которой приведен в листинге 4.14.
ИЯ fhighlightИЯ fhighlight
ИЯ fhighlight
ИЯ fhighlight
<?php
function fhighlight($filename)
// Открываем файл, имя которого передано в параметре $ filename
$file = fopen($fi1ename, "r”);
/ / Помещаем его содержимое в буфер $dQcument
$document = freed ($file, filesize ($fileriame));
// Закрываем файл
fclose($file);
// Вызываем функцию shighlight ()
return shighlight ($document);
1
?>
Работа функции сводится к открытию переданного в параметре $ filename файла и передаче его содержимого ранее созданной функции shighlight().
Загрузка файлов на сервер
В этом разделе будет разработано небольшое приложение, реализующее за- фузку файлов на сервер.
( Замечание )
В русскоязычной терминологии не совсем четко прослеживается направление следования данных при употреблении термина ”за「рузка". Поэтому здесь и далее используются уточнения: изагрузка на сервер" (upload) и "загрузка на машину клиента" (download).
Приложение для загрузкн файлов на сервер представляет собой HTML-форму index.html (листинг 4.15) и ее обработчик — upload.php (листинг 4*16).
^Листинг 4.15. Файл tndex.php
S'i I ri А « « > > ■ t kviffe .. .*♦;*;,♦♦ *.*;** .“.,*“,・.*“. ・*T“,・;.“**”***『*.、+.、*;*¥**■**士*У,*“3 я'^яшй ■ ■ ■*•■ ■ ••'••»я ■■tf й *i »iV*W'#*******+*******»****'i,*«***44**-*»-»»** *”■“♦*****•”**«“♦,**♦“” *“??•“
<html>
<head>
<title> Загрузка файлов на сервер </title>
</head>
<body>
<h2xp><b> Форма для за грузки файлов </b></px/h2>
vform action="upload*php" method="post11 enctype^"multipart/form-data">
<input type=,'filen name=rtfilename11 ><br>
<input type=^1^sukяn^t,^ value=11 Загрузить,fXbr>
</form>
</body>
</html>
Форма из листинга 4.15 содержит единственное поле типа file для загрузки файла и кнопку для отправки параметров формы обработчику, расположенному в файле upload.php.
[ Замечание )
Атрибут enctype формы определяет вид кодировки, которую браузер применяет к параметрам формы. Для того чтобы отправка файлов на сервер действовала, атрибуту entype необходимо присвоить значение multipart/formdata. По умолчанию этот атрибут имеет значение application/x-www-form- urlencoded.
После того как получен HTTP-запрос, содержимое загруженного файла записывается во временный файл, который создается в каталоге сервера, заданном по умолчанию для временных файлов.
Характеристики загруженного файла доступны через двумерный супергло* бальный массив $_f工les. При этом переменная со значениями этого массива может иметь следующий вид:
口 $_files["filename"] [“name”] (содержит исходное имя файла на клиентской машине);
口 $_files['•filename"] ["size] (содержит размер загруженного файла в байтах);
口 $_FILES [lrfilenamen] [*Чуре*'1 (содержит MIME-ТИП файла);
口 串_F工les ["filename"] [”tmp_file”] (содержит имя временного файла, в который сохраняется загруженный файл).
; Замечание )
В качестве имени filename должно выступать значение параметра патле file-поля HTML-формы.
</head>
<body>
<?php
if (copy ($_FILES [11 filename1'] [ Г|тр_пате|Г],
Mc:/ temp/11.$_FILES[^filename**] [**nanien])) {
echo(пФайл успешно загруженм);
}
else
{
echo ("Ошибка загрузки файла*1);
}
?>
</body>
</html>
После выполнения скрипта выбранный для загрузки файл будет помещен в каталог C:/temp, а браузер выдаст строку:
Файл успешно загружен
Для вывода характеристик загруженного файла скрипт из листинга 4.16. необходимо модифицировать следующим образом (листинг 4.17).
<html>
<head>
<title> Результат загрузки файла </title>
</head>
<body>
<?php
i f (copy ($_FILES [M filename " ] [ "Ьф—Пате”], ”c: /七飘/".$_FILES「filename"] ["name”]))
{
echo(нФайл успешно загружен <br>M);
// далее выводится информация о файле
echo(нХарактеристики файла: <br>");
echo ("血1Я файла: 11);
echo ($_FILES[ «filename,f] ["name"]);
echo(м<Ьг>Размер файла: ”);
echo($_FlLES[Mfilenamer,] [HsizeM]);
echo("<Ьг>Каталог для загрузки: ”);
echo ($_FILES [ "filename"] [”tnp_name“]);
echo(м<Ьг>Тип файла:");
echo ($_FILES ["filename11 ] ["type"]); } else (
echo("Ошибка загрузки файла");
} ?> </body> </html>
Результат работы скрипта приведен на рис. 42
引 Резулыат загрузки файла - Microsoft Internet Explorer
Файл Правка Вид избранное Сервис Справка
②Назад ▼ 0 ▼回回焼I >
Поиск
工丁 Избранное 守 Медиа 名丨
Адрес:圉 http://bcahosVupbad.php
Я Q Переход Ссылки二^^кйй^®彳工二二^^—,一^ j.~:
Файл успешно загружен Характеристика файла: Имя файла: PHP.doc Размер файла: 28672 Каталог для загруз яг С,WINDOWS'phpC.tmp Тип файла: application msword
总 Готово
£ ! 「「&J Местная интраоть
Рис. 4.2. Информация о загруженном файле
В некоторых случаях требуется ограничить размер файла, который может быть загружен на сервер. К примеру, чтобы ограничить размер загружаемого файла 3 мегабайтами, скрипт в листинге 4Л6 необходимо изменить следующим образом (листинг 4.18).
1стинг 4.18. Ограничение размера загру
口 файла
<html>
<head>
<title> Результат загрузки файла </title>
</head>
<body>
<?php
i f($_FILES[n filename H]In s i ze"1 > 1024*3*1024) {
echo(11 Размер файла превышает три мегабайта**);
exit;
i f (copy ($_FI LES [n f i lename " ] [11 tTrp_name "], 'fc:/temp/".$ 一FILES「filename”] ["name”]))
{
echo{"Файл успешно загружен <br>“);
}
else
{
echo (**Ошибка загрузки файла n);
}
?>
</body>
</html>
Максимальный размер загружаемого файла можно также задать при помощи директивы upload_max_filesize в файле php.ini, значение которой по умолчанию равно 2 Мбайт:
if ($_FILES["filename0) [,1size',J > upload_jnax_filesize)
Загрузка бинарных файлов (изображений, архивов, программ и т. д.) стандартным методом часто приводит к тому, что файлы оказываются "битыми". Это связано с тем, что Web-сервер Apache склонен к всевозможным перекодировкам. Для того чтобы этого избежать, необходимо добавить в файл httpd.conf следующие строки (листинг 4.19).
<Location />
Cha г s еtRe codeMu11 ipa rtForms Off
</Location>
Счетчик загрузки файлов с сервера
Если подсчет посещаемости страниц осуществляется достаточно просто, то подсчет числа обращений на загрузку файлов с сервера кажется на первый взгляд сложной задачей. В то же время именно скачивание файлов зачастую является определяющим фактором при формировании общего трафика сервера.
Замечание )
Создание системы учета и анализа посещаемости сайта описывается в главе 14.
Данная задача имеет несколько решений: использование JavaScript, подсчет до отправки HTTP-заголовка браузеру, анализ логов сервера. Ни один из предложенных способов не является универсальным: JavaScript поддерживается не всеми браузерами. При использовании HTTP-заголовка посетитель может установить точный путь к файлу, анализируя исходный текст HTML- страницы, и в следующий раз воспользоваться нм, минуя скрипт подсчета загрузки. Доступ к логам сервера не всегда возможен, и их анализ является достаточно ресурсоемкой задачей, так как в них фиксируется множество дополнительной информации, которую необходимо отсеять.
В каждом конкретном случае следует искать компромиссное решение, наиболее удовлетворяюшее поставленным целям и имеющимся ресурсам. В данном разделе будет рассмотрен второй вариант системы подсчетов файлов, как наиболее подходящий почти во всех случаях. Это связано с тем, что HTTP-заголовки поддерживаются подавляющим числом браузеров, а среднестатистический посетитель вряд ли будет осуществлять поиск точного пути к файлу, так как это не дает никаких преимуществ н выгод.
Допустим, на странице index.php имеется ссылка на файл test.zip, предназначенный для загрузки с сервера. Необходимо регистрировать каждую загрузку этого файла. В листинге 4.20 представлена схема, по которой будет разрабатываться счетчик загрузки файлов.
胸工屯[4毋e⑥小碗痴'…"
<HTMLXHEAD>
<?php
// Если пользователь перешел по ссылке закачки файла, учитываем
// хмт и отправляем файл клиенту
i f ($_GET [1 down1 ] — "downloads11)
7 Зак.7 Зак.11811181
7 Зак.
7 Зак.
1181
1181
// Тут размещаем код подсчета хита, например, // путем записи в базу данных или в файл
// В заголовке отправляем ссылку на загружаемый файл:
echo ”<МЕТА HTTP-EQUIV=1Refresh1 CONTENT;,。; URL=test.zip1>н;
}
</HEAD>
<BODY>
<a href=index.php?down=downloads>Скачать</а>
</BODY>
</HTML>
В ссылке, предоставляемой посетителю для загрузки файла, скрипту index.php передается параметр down, содержание которого будет определять, какой файл необходимо загрузить на машину клиента. Извлекая из ассоциативного массива $_get значение параметра down, скрипт в index.php размешает в заголовках файла строку:
СМЕТА HTTP-EQUIV=1 Refresh * CONTENT^0; URL»test.zip1>
Эта строка сообщает браузеру о необходимости принять файл test.zip. Перед выводом этой строки можно осуществить подсчет загрузки, сохранив информацию в файл или базу данных.
Далее в этом разделе будет разработана универсальная система подсчета за* груженных файлов ——FileCounter. Информация о загруженных файлах будет храниться в базе данных MySQL. Само Web-приложение будет состоять из двух блоков: непосредственно счетчика, который мы разместим в файле counter.php, и системы администрирования, размещаемой в папке /admin.
( Замечание )
Код Web-приложения FileCounter можно найти на прилагаемом к книге компактдиске или по адресу http://www.softtime.ru/info/downloads.php.
Файл counter.php будет включаться в файл, в котором размещена ссылка для загрузки файла при помощи оператора require__once("counter.php"), и будет требовать для своей работы два ассоциативных массива: $down_name% содержащий имена загружаемых файлов, и $down_fiie, содержащий абсолютный или относительный адрес файлов* В листинге 4.21 представлен фрагмент файла index.php, подключенный к счетчику.
?^留强厘維^tf^如嘅%1Ж『鶴*'+* w*大廿催i1^^^^^^^^離戈*y^y^¥iy^對''“.^^^ц^^ ** .,总^^4," *з盜』i塁堂'"儲矍送|i制随学蒙覺尚岩筈.学浙駕ftjHflj^^ffiS^^SS^^'
:Листинг 4*21. Фрагмент файла Index.php с подключенной системой FileCounter
<?php
// Регистрируем первый файл
$down_file[1testl.zip1] = "http://www.softtime.ru/files/test1.zip11;
$down_name[1testl.zip'] = "Первое Web-приложение'1;
// Регистрируем второй файл
$down_file[*test2.zip1] ^ "http://www,softtime.ru/files/test2.zip";
$down__name [ * test2. zip' ] = нВторое Web-приложение11;
// Подключаем счетчик файлов (counter.php)r который автоматически
// передаст браузеру файл при передаче обращении по ссылке
// index * php?down=testl< zip и зарегистрирует обращение в базе данных require_once(^counter,php");
?>
Как видно из листинга 4.21, в начале скрипта все файлы, предоставляемые для загрузки, явно регистрируются в массивах* Может показаться, что это несколько надуманная конструкция, делающая код неэлегантным и неэффективным, поскольку на первый взгляд гораздо проще передать в параметре точный путь к файлу (index.php?down=http://.. >), В этом случае в заголовок в качестве пути к файлу можно было бы поместить значение $_get [ * down1 ], тогда отпала бы необходимость в регистрации каждого закачиваемого файла в ассоциативных массивах и извлечении из них точного пути к файлу. Однако при таком подходе появляется опасность взлома сервера, если злоумышленник передаст в качестве параметра down путь к файлу системы. С использованием же системы регистрации устанавливается правило: запрещено все, что не разрешено —— ни один файл, не входящий в массивы, не может быть загружен с сервера.
Для регистрации статистики по загрузке файлов в базе данных Web-приложению понадобятся две таблицы в базе данных MySQL: files, в которой будут храниться записи о предоставляемых для скачивания файлах, н hit file с информяциси о ззгрузкс кзждого фяйлз из таблицы files* SQL- операторы create, создающие эти таблицы, приведены в листингах 4.22 и 4.23 соответственно.
Листинг 4.22. SQL-запрос создания таблицы files
CREATE TABLE files (
id^file INT(ll) NOT NULL AUTO_INCREMENT, name TINYTEXT,
url TINYTEXT,
description TINYTEXT, PRIMARY KEY (idfile) )TYPE^MylSAM;
Таблица files содержит четыре поля:
口 id__fiie ——первичный ключ таблицы, снабженный атрибутом auto_increment^
О паше ——значение параметра down для данного файла (индекс массивов $dcwn_f±le И $down_naine);
口 uri —— адрес файла, предназначенного для скачивания (значение массива $down_file);
□ description — название скачиваемого файла (значение массива $down__name).
Вот пример записи таблицы files из реально работающего Web-приложения:
口 id_file ^ 2;
口 name = 1 guestbook * zip1 ;
口 url ^ fhttp;//www.softtime.ru/files/guestbook* zip1 ;
口 description = тГостевая книга на PHP (Файловая)1 -
:?t*"yy 总:^^^^^?/二11",'>!7」*'%"学*"-*'':,"u±",*,'"'"" +" * 丁 寸 *** *,3\1" " "!",*!,",:;-?,,:产:,I" :,,/:,* "h',三,,"*.*\" *"",*,****:*,*t<y,*,**,jAi;*t*T**,*,* "t уп+м+;у+.t^ц^лmк tt
5 Листинг 4.23. SQL-запрос, создающий таблицу hitfile
a ■t(. + i:4r****^i»»#i>1'<i4ie«ll>;i^14e7tii»-fcii,. *<•«*«*!• *#«ЛУм##***»»^*»+4»+^»/м<г^Л11»^11в5**^>^*+*e'»*»VfiV»>'*f'ii''o",o'i»fi'■'■**•*«'*►**■♦ I «к ■w»»**#<:e'»'*ii«V*»«*'i«fc»V«*X*ii4^rfc-**«V4#'-rf+7»* *»f*eiii^ri^
CREATE TABLE hitfile (
idjxitfile INT(ll) NOT NULL AUTO_INCREMENT,
putdate DATETIME DEEAULT NULL,
id_file IOT(11) DEFAULT NULL, PRIMARY KEY (id_hitfile)
)TYPE=MyISAM;
Таблица hitfile содержит три поля:
口 id_hitfile ——первичный ключ таблицы» снабженный атрибутом
AUT。一工NCREMENT;
□ putdate ——время обращения к файлу;
口 id_file — внешний КЛЮЧ таблицы files;
Запись для этой таблицы выглядит следующим образом:
О id_hitfil.e = 12;
口 putdate ^ '2004-06-02 01:08:40';
口 id file 工 2;
При построении системы учета загрузки файлов можно использовать лишь одну таблицу files, добавив в нее поле hits. При каждом обращении к файлу при помощи оператора update можно увеличивать значения этого поля. Но это не позволяет отслеживать статистику загрузки файлов во времени, например, распределение загрузки файлов по времени суток или по дням недели. Это вовсе не значит, что однотабличный способ построения счетчика неправильный. Лишенный дополнительной функциональности, он позволяет значительно сократить расходы, связанные с хранением второй таблицы в базе данных. Учитывая, что в этом случае сохраняется информация о каждой загрузке файлов, размер базы данных очень скоро может вырасти до неприличных размеров, замедляя работу самого Web-приложения и занимая ценное серверное пространство. Тем не менее будет рассмотрен двухтабличный способ построения счетчика как более сложный в реализации вариант.
Определившись с принципом работы счетчика и способом хранения информации, можно приступить к разработке первого блока системы: файла counter.php, осуществляющего сохранение информации о загружаемом файле в базе данных MySQL. Структура файла представлена в листинге 4.24.
;Листинг 4.24. Файл counter.php
// Если посетитель перешел по ссылке загрузки файла, учитываем
// хит и отправляем файл клиенту. Параметр down, извлекаекзый из
// ассоциативного массива $_GET# содержит индекс массивов $dcwn_name и // $down_file
if ($_GET[*down']冃,”) {
if (array_key_exists ($_GET (1 down1 ], $down_file))
// Соединяемся с базой данных require_once ("config .php");
// Учитываем закачиваемый файл
// Выясняем первичный клюй (id_page), соответствующий
/ / закачиваемому файлу
$ query ^ п SELECT * FROM files WHERE name^, f,. $_GET [ Mown1 ]. $fls = @mysql_query($query);
if($fls)
if (mysql_num_rows ($fls) >0)
{
$files - toysql_fetch_array ($fIs);
$id_files = ^filesCid^file*]; }
// Если данная страница отсутствует в таблице files
// и ни разу не учитывалась - добавляем данную страницу
// в таблицу
else
(
$query = "INSERT INTO files VALUES (0,
'L$_GET['dcwn」.”',
,”.$down_file [$_GET [ 'down']】•"',
,"・$down_name [$_GET「down' ] ] J ')";
@mysql_query ($query);
// Выясняем первичный ключ только что добавленного
// файла
$id_files R 0mysql_insert_id ();
}
}
// Заносим хит в таблицу hitfile
$query__main = "INSERT INTO hitfile VALUES (0, NOW (), $id__flies)11;
@mysql_query ($query__main);
// В заголовке отправляем ссылку на загружаемый файл
echo "〈МЕТА HTTP-EQUIV>=rRefresh1 CONTEND‘0;
URS” ・ $down_file($_GET [ Mown「] ] J,,
}
}
?>
Если значение down» извлекаемое из ассоциативного массива $_get, содержит какое-либо непустое значение, производится поиск индекса в массиве $down_fiie, совпадающего с этим значением. Если переданное в параметре down имя зарегистрировано в массивах, то происходит соединение с базой данных посредством включения скрипта config.php (листинг 4.25) при помощи оператора require^once о и записи в базу данных о загрузке файла.
Листинг 4,25. Файл установки соединения с базой данных (config.php)
<?php
// Файл соединения с базой данных является общим для обоих блоков
// системы: счетчика и системы администрирования
// Имя сервера базы данных, например, $dblocation 二"mysql28 >noweb. ruu .
// Сейчас выставлено имя сервера локальной машины $dblocation = "localhost";
// Имя базы данных, где расположены таблицы files и hitfile
Sdbname = "softtime**;
// Шя пользователя базы данных
$dbuser = ”:root”;
// Пароль
$dbpasswd = "н;
// Количество позиций на странице
$pnuniber = 30;
// Соединяемся с сервером базы данных
$dbcnx = @mysql_connect (9dblocation, $dbuser, $dbpasswd);
// Выбираем базу данных
0mysql_select_db (^dbname, $dbcnx);
// Небольшая вспомогательная функция, которая выводит сообщение
// в случае ошибки запроса к базе данных (используется в системе // администрирования)
function puterror($message) {
echo("<р>$теззаде</р>п);
exit《);
?>
При занесении информации в базу данных (см. листинг 4.24) возможны два варианта. В первом случае, когда запись о загружаемом файле уже присутствует в таблице flies, необходимо по имени файла извлечь первичный ключ данной записи из таблицы files. С использованием полученного значения необходимо занести новую запись в таблицу hitfiles. Второй вариант, когда загрузка файла осуществляется первый раз, запись в таблице files, соответствующая данному файлу, отсутствует, и в этом случае необходимо добавить новую запись как в таблицу files, так и в таблицу hitfiles.
Для того чтобы выяснить, присутствует ли в таблице files запись о загружаемом файле, и по какому из вариантов необходимо следовать, к таблице files осуществляется запрос "select * from files where name=1 r,. $_get [1 down * ].n 1M, извлекающий no имени файла первичный ключ таблицы. Если запись в таблице присутствует, то функция mysql_num_rows() вернет единицу. В этом случае переменной скрипта $id_£ile присваивается значение первичного ключа, которое далее используется для формирования SQL-запроса на добавление записи в табл и-
цу hitfile. Если же функция mysql_num_rows () возвращает ноль, что говорит об отсутствии записи, соответствующей данному загружаемому файлу, то осуществляется SQL-запрос на добавление записи о новом файле в таблицу files. Имя первичного ключа, автоматически присвоенного MySQL, как предписывает параметр autq 工ncrement, извлекается функцией mysql_insert_id().
Таким образом, любой новый файл, появляющийся в ассоциативных массивах $down__name и $down_files, автоматически попадает в базу данных при первой его загрузке. Это достаточно удобно, поскольку избавляет редактора страниц от ручного согласования содержимого сайта с базой данных. Единственная неприятность, поджидающая на этом пути, связана с тем, что при удалении файла из ассоциативных массивов не происходит автоматического удаления записей из базы данных. Эта проблема будет решена при построении второго блока данного Web-приложения ——системы админ истрирован ия.
Можно заметить, что информация в системе дублируется и в файлах, и в базе данных. Первое, приходящее на ум решение, состоит в извлечении информации о закачиваемом файле при формировании Н ТТ Р-заголо вка, причем информация не хранится в ассоциативных массивах. Можно, к примеру, так спроектировать базу данных (добавив третью таблицу, содержащую информацию о страницах сайта) и систему администрирования, что добавление нового файла на сайт будет осуществляться через Web- форму, которая будет загружать файл на сервер и автоматически заносить запись о нем в таблицу files. Данный подход имеет право на существование (к сожалению, из-за ограниченного объема книги мы не можем его рассмотреть), но есть у него и один существенный недостаток. Если сервер базы данных выходит из строя, а Web-сервер продолжает работу (часто именно так и бывает), то посетители сайта не смогут загружать файлы, несмотря на то, что Web-сервер может предоставить такой сервис и без услуг сервера базы данных.
В рассмотренном выше варианте счетчика при выходе нз строя сервера базы данных учет загрузок прекращается, но данный факт не отражается на посетителях ресурса, у которых остается возможность загрузки файлов,
[ Замечание )
Символы @ около каждой функции, обращающейся к базе данных, подавляют аы0од ошибок и предупреждений в окно браузера, если сервер базы данных не реагирует на запросы. Это позволяет обезопасить дизайн сайта от искажений и скрыть от посетителей проблемы с сервером базы данных.
После того как налажен сбор н регистрация статистики в базе данных, можно переходить ко второй части Web-приложения, посвященной анализу собранной информации,——системе администрирования.
Система администрирования будет состоять из семи файлов:
口 index.php — главная страница системы администрирования;
П topcounter.php — заголовок страницы;
bouomcountei\php — завершение страницы;
main.plip — хиты страницы за пять временных интервалов: “Сегодня”, "Виера”,“За 7 дней*\ и3а 30 дней" и *'3а все время”;
hours.php — скрипт, отражающий распределение хитов как отдельного файла, так и всех загрузок по часам выбранных суток;
口 days.php ——скрипт, отражающий распределение хитов как отдельного файла, так и всех загрузок сайта по дням;
□ delfile.php ——скрипт удаления файла из таблиц системы.
Внешний вид главной страницы администрирования системы FileCounter представлен на рис. 4*3.
Рис. 4*3. Главная страница администрирования системы счетчика файлов
В таблице для каждого файла выводится общее число хитов за все время сбора информации счетчиком. Переход по названию файла приводит к странице, на которой отражена детальная статистике за пять временных интервалов: "Сегодня", "Вчера”,"За 7 дней”,"За 30 дней” и "За все время1'* Напротив каждого из файлов имеется ссылка на удаление его из системы статистики на случай, если редактор сайта удалил его с ресурса.
( Замечание )
В листингах книги для удобства восприятия будет опущен дизайн страниц. Если необходимо Web-приложение с наложенным дизайном, можно воспользоваться готовыми скриптами, расположенными на компакт-диске, прилагаемом к книге, или по адресу http://www.softtlme.ru/downloads.php.
В листинге 4.26 представлен код файла index.php.
<?php
/ / Данные переменные определяют название страницы м подсказку.
$title='Статистика&пЬэр;загрузки&пЬзр;файлов *;
$pageinfо=1Ниже перечислены Web-приложения, которые участвуют в статистике, По гиперссылкам можно получить детальную статистику по каждому отдельному Web-приложению *;
/ / Включая заголовок страницы include нtopcounter.phpM;
// Если в запросе не переданы никакие параметры, выводам усредненную
// статистику по всему сайту. В противном случае, если указан // конкретный файл ($id_file), выводам статистику по нему. $раде = “";
$раде = $_GET「раде1 ];
$ order = •*";
border = $_GET[,ordert];
if($page = n") $page = 1;
$begin = ($page - 1)*$pnuxnber; //$pnumber - число позиций на странице if($order = "") $orderstr = ^description";
else $orderstr = "num DESC";
// Формируем и выполняем SQL^aanpocbi
$pgs = mysql_query (11 SELECT hit file. id_file, files.description, count(hitfile.id_file) AS num FROM hitfile, files
WHERE hitfile.id_flie = files.id^file GROUP BY files.id_file ORDER BY $orderstr
LIMIT $begin, $pnurnber;M);
$num = mysql^query (4 SELECT count (*) FROM files;");
$tot = mysql_query(°SELECT count(*) FROM hitfile;и); if ($pgs && $num && $tot) {
// Шводим ссылки на другие страницы
$total = mysql_fetch_array($num);
$number = (int)($total[^ount(*)1]/$pnumber);
if ((float) ($total [1 count (*) 1 ] /$pnuniber) -$number != 0) $пшпЬег++; for($i = 1; $i<=$number; $i++) {
if($number = $i)
(
if($page — $i)
echo ”「.(($i - l)*$pnumber + lbM-M.$total[1 count (*) f] ^Ч"; else
echo "<a href=index. php?pagera". $i." border^1'. $ order • ”> [n.
(($i - 1) *$pnumber + 1) . "-',.$total [1 count (*) 1 ] • ^1^/^'1; }
else
{
if($page == $i}
echo ”[”.(($i - 1)*$pnumber + 1)•”一"・$i*$pnumber・"]"; else
echo "<a href=index.php?page=". $i, n&order=n.$order. ,r> [n.
(($i - l)*$pnumber + 1) ."-•'.$i*$pnumber/1]</a>H; }
// Выводим таблицу с адресами страниц, участвующих в
// статистике, и общее количество хитов для каждого из
〃 файлов.
// Заголовок таблицы
?>
<table>
<trxtd>
<р><а href=index.php?page=<? echo $page ?»Страница</a></p>
</td>
<td>
<р><а href=index »php?page=<? echo $page ?>&огс!ег=1>Количество посещений</а></p>
< / td><td><p>fleiicT вие</р>< / tdx/t r >
<?
// Таблица
$totalhit = mysql_fetch_array($tot);
while ($pag = mysql_fetch__array ($pgs))
{
echo o<tr><td>
<a href=main.php?id_file^H,$pag[1id_file1].n>
”・ $pag[* description1]- °
</a>
</td>
<td align=center>M.$pag「num' ] . "</td>
<td>
<a href=delfile,php?id_fiie=".$pag [1 id_file* ]."> Удалить</а>
</td></tr>°;
}
echo ,,</table>";
} else put error (° Ошибка при обращении к таблице файлов");
// Включаем завершение страницы
include "bottomcounter.phpM;
Файл index.php включает в свой состав два файла: topcounter.php и bottomcounter.php, которые формируют *'шапку'1 и нижнюю часть страницы соответственно. Остальной код располагается между вставками этих двух файлов. Файл bottomcoitnier.php выводит дату начала регистрации информации системой и имя файла, для которого производится сбор статистики (см* рис. 4,3). Если посетитель выбрал ссылку для просмотра временной статистики всего сайта, будет выведена фраза Весь сайт. Код файла bottomcounter.php, за исключением тегов HTML, приведен в листинге 4.27.
;.『.:..;••..;...;.墨.::.:.“.,“:£:…”у "£2*匕尤尸,靠不.;:..挛v、,^^二咨,
Стинг 4.27. Код файла bottomcounter.php
• ••••^««^•••«••••^••■•«••••«•««•«•••а «■•>•••>••■ •>•■•••*•.•«•• •♦♦♦•.、、、、、♦♦<•、,、、.'、、♦♦••,♦,,、.,.”■
<?php
$id_file 二 0;
$ id^file = $_GET['id_file'];
// Соединяемся с базой данных require_once(n../config.php”);
// Если в качестве параметра передан первичный ключ таблицы files, // выясняем имя закачива^дого файла
if {帛 GETL'id—file」!='『) {
$pgs = mysql_query("SELECT files.description
FROM files
WHERE files .id_file==$id_filetf);
if($pgs)
{
Spage ^ mysql_fetch_array ($pgs);
Snainepage = $page [' description'];
// Далее переменную $namepage можно использовать для вывода // имени файла }
〃 Иначе вместо имени выводим фразу "Весь сайт”
} else $namepage = 11 Весь сайт";
// Выясняем число дней, прошедцпих с момента начала сбора статистики
$dat = mysql^query("SELECT UNIX^TIMESTAMP{min(putdate))
FROM hitf±l£;")『 if (^dat) {
$date = mysql_fetch_array ($dat);
echo "Дата качала регистрации данных:".
date{"Y-m-d H: i:sM,$date[, UN IX_TIMESTAMP (min (putdate) )4).
nnbsp;
printf (г|Пропшо:<b> %d </Ь>даей",
((time() -$date[ ^IX^TIMESTAMP(min(putdate)) r] )/3600/24));
} else puterror(пОшибка при обращении к таблице хитов”); ?>
Возвращаясь к листингу 4.26, нужно отметить, что после включения файла boitomcounter.php формируется таблица с именами файлов и количеством обращений к ним. На одной странице выводится не более $pnumber файлов. Переменная $ртшЬег, по умолчанию принимаюшая значение, равное 10, находится в файле config.php, в котором может быть выставлено любое другое значение для этой переменной. На файлы, не уместившиеся на первой странице, выводятся гиперссылки. После этого формируется заголовок
таблицы, имена столбцов в котором представляют собой гиперссылки. Переходя по этим гиперссылкам, можно отсортировать столбцы (по имени файла или по числу обращений). Напротив каждого из файлов имеется ссылка на удаление файла из системы статистики, осуществляемое скриптом, расположенным в файле delflle.php (листинг 4.28).
:Листинг 4.28. Файл delflle.phpMWT^W^Wt^???,;二:.第..*. г. <т№\'> ?Г^?Л».'. ■ илсйй» • *'г * ЛЛ'. г.У^^ли. .,Л.:Листинг 4.28. Файл delflle.phpMWT^W^Wt^???,;二:.第..*. г. <т№\'> ?Г^?Л».'. ■ илсйй» • *'г * ЛЛ'. г.У^^ли. .,Л.
:Листинг 4.28. Файл delflle.php
MWT^W^Wt^???,;二:.第..*. г. <т№\'> ?Г^?Л».'. ■ илсйй» • *'г * ЛЛ'. г.У^^ли. .,Л.
:Листинг 4.28. Файл delflle.php
MWT^W^Wt^???,;二:.第..*. г. <т№\'> ?Г^?Л».'. ■ илсйй» • *'г * ЛЛ'. г.У^^ли. .,Л.
<?php
// Скрипт удаления информации о файлах из системы статистики include ”…/ config * php”;
// Возвращаем парметр id_file
$id__file = 0;
$ id^file = $_GET「土d_file * ];
// Удаляем записи из таблицы hitfile
if{mysql_query("DELETE FROM hitfile WHERE id_file=$id_file;11))
{
// Удаляем запись из таблицы files
if (mysql_query("DELETE FROM files WHERE id_file=$id_file;n))
{
echo H<HTML><HEAD>
<META HTTP-EQUIV® * Refresh * CONTENT=,0; URL^index.php1> V/HEADX/HTML>";
}
else
(
echo H<B>Error: n »mysql_error();
exit ();
}
}
else
{
echo tr<B>Error: ".mysql_error();
exit ();
}
Данный скрипт производит очистку таблиц hitfile и files от записей^ поле id_file которых равно значению, передаваемому через параметр строки запроса id_file. После успешного удаления записей происходит автомата-
ческий переход на главную страницу администрирования, осуществляемый при помощи НТТР-заголовка:
<МЕТА HTTP-EQUIV»1 Refresh * CONTENT=*0; URL=^index. phpf>.
Теперь приступим к реализации дополнительных возможностей, появляющихся в результате хранения в таблице hitfile времени обращения к файлу. Эта информация позволяет отслеживать динамику изменения обращений к файлам во времени. Из листинга 4.27 видно, что название каждого файла является гиперссылкой на скрипт main.php, которому в качестве параметра передается первичный ключ, соответствующий данному файлу. Код файла main.php приведен в листинге 4.29.
Гл^^„^
<?php
$title=,XnTbi,;
$pageinfo= * При переходе по
ссыпкам г, <Ь>Сегодня< /Ь>11, 11 <Ь>Вчера</Ь>м отображается
детальная почасовая статистика загрузки за выбранный день.
При переходе по ссылкам п<Ь>За 7 дней</Ъ>и и "<Ь>За 30 дней
</Ь>" отображается детальная суточная статистика за эти
периода времени.1;
$menu-l;
$helppage='';
〃 Заголовок страницы include иtopcounter.php";
// Вычисляем статистику ^Сегодня"
$hitl = get_hits(1,0,$ id_file);
// Вычисляем статистику "Вчера11
$hit2 = get_hits (2,1# $id_file};
// Вычисляем статистику "За 7 дней"
$hit7 = get_hits (7,0r$id_file);
// Вычисляем статистику "За 30 дней"
$hit30 - get_hits (30, 0, $id__file);
// Вычисляем статистику **3а все время"
$hitall = get_hits (0,0,$id_file);
?〉
<table>
<tr>
<td><a href= ■ hours.php?da^0&id_file=<?php echo $id_file; ?>f > CeroAHH</a></td>
<tdxa href^1hours.php?day=l&id_file=<?php echo $id_file;?>,> Енерам / ax / td>,
vtdxavtdxaЗаЗаvtdxavtdxa<td>3a<td>3ahref=,days.php?id_file=<?php echo $id_file; ?>*>7 дней</а></td>href-* days,php?id_file=<?php echo $id_file;?>&daystst=30 * > 30 днeй</a></td> все BpeMH</td>href=,days.php?id_file=<?php echo $id_file; ?>*>7 дней</а></td>href-* days,php?id_file=<?php echo $id_file;?>&daystst=30 * > 30 днeй</a></td> все BpeMH</td>
vtdxa
vtdxa
За
За
vtdxa
vtdxa
<td>3a
<td>3a
href=,days.php?id_file=<?php echo $id_file; ?>*>
7 дней</а></td>
href-* days,php?id_file=<?php echo $id_file;?>&daystst=30 * > 30 днeй</a></td> все BpeMH</td>
href=,days.php?id_file=<?php echo $id_file; ?>*>
7 дней</а></td>
href-* days,php?id_file=<?php echo $id_file;?>&daystst=30 * > 30 днeй</a></td> все BpeMH</td>
</tr><tr><td>XHTbi</tid><td><?php echo<tdx?php echo<tdx?php echo<tdx?php echo<tdx?php echo</tr></table></tr><tr><td>XHTbi</tid><td><?php echo<tdx?php echo<tdx?php echo<tdx?php echo<tdx?php echo</tr></table>$hitl;$hit2;$hit7;$hitl;$hit2;$hit7;?></td> ?></td> ?></td>?></td> ?></td> ?></td>$hit30; ?></td>$hitall; ?></td>$hit30; ?></td>$hitall; ?></td>
</tr>
<tr>
<td>XHTbi</tid>
<td><?php echo
<tdx?php echo
<tdx?php echo
<tdx?php echo
<tdx?php echo
</tr>
</table>
</tr>
<tr>
<td>XHTbi</tid>
<td><?php echo
<tdx?php echo
<tdx?php echo
<tdx?php echo
<tdx?php echo
</tr>
</table>
$hitl;
$hit2;
$hit7;
$hitl;
$hit2;
$hit7;
?></td> ?></td> ?></td>
?></td> ?></td> ?></td>
$hit30; ?></td>
$hitall; ?></td>
$hit30; ?></td>
$hitall; ?></td>
<?php
// Включаем завершение страницы include "bottomcounter.php";
// Функция возвращает
// количество хитов.
// $begin - число дней, которое необходимо вычесть из текущей даты,
// для того чтобы получить начальную точку временного интервала
// $end - число дней, которое необходимо вычесть из текущей даты,
// для того чтобы получить конечную точку временного интервала
// $id_file 一 первичный ключ записи таблицы files, // соответствующей загружаемому файлу function gejhits ($begin, $end, $id_file) {
// Эта переменная определяет, осуществл月етс貝 запрос к конкретной
// странице или ко всему сайту.
if($id_file —叫 $tw = ”"; else 帛tmp = "id_file=$id_filen;
// Запрос на статистику за временной интервал, определяемой
// параметрами $begin и $end
if ($begin == 0) $泣2 ^ "":
else $tn^2 = "putdate>=date_sub(DATE_FORMAT(NOW(), 1 %Y-%m-%d 23:59:59'), INTERVAL fbegiiV DAY)n;
$tmpl = l,putdate<date_sub (DATE_FORMAT (NOW (), ' %Y-%m-%d 23:59:59'), INTERVAL *$end1 DAY)11;
〃 Расставляем and
if ($tnp2 != ”” I I Strap != ") $andl = " and ”;
if ($tmp2 != "” && $七叩!= "") $and2 = " and 力
// Формируем SQL-за пр о с
$hits = нSELECT count(*) fKOM hitfile
WHERE ,\$tmpl. $andl. $tmp2. $and2. $tmp;
// Осуществляем запрос к базе данных $pht = mysql_query ($hits);
if($pht) (
// Если запрос выполнен удачно, то преобразуем результат в массив $total_hit = mysql_fetch__array ($pht);
return $total_hit[1 count(*)'];
else
echo ^срхЬЖггог: ,f .mysql error ( ) . М</Ь><р>т,;
puterror("Ошибка, при обращении к таблице хитов..,");
} } ?>
Скрипт в листинге 4.29 создает таблицу с динамикой обращений к файлу за пять временных интервалов: "Сегодня*'5 "Вчера1*, "За 7 дней11, "За 30 дней0 и "За все время". Для возвращения количества обращений к файлу (хитов) нс- пользуется функция get_hits ($begin, Send, $id_file), KOTOpa兄 принимает три параметра:
口 $begin — число дней, которое необходимо вычесть из текущей даты для получения начальной точки временного интервала. Например, передав в качестве данного параметра L 10, 30, можно получить статистику за I, 10 и 30 дней соответственно. Если параметр принимает значение 0, число хитов будет вычисляться за все время сбора статистики;
口 $end —— число дней, которое необходимо вычесть из текущей даты для получения конечной точки временного интервала. Так, если $ beg in принимает значение, равное 2, a Send — равное 1, это соответствует статистике “Вчера”. Аналогично, значения параметров Sbegin = 14, $end = 7 соответствуют статистике пНеделю назад”. Если $end принимает значе-
ние 0, число хитов будет вычисляться включительно до текущего момента;
口 $id_fiie — переменная, передающая функции первичный ключ таблицы files, соответствующий файлу, для которого производится расчет количества обращений за интервал времени.
SQL-запрос к базе данных строится таким образом, чтобы временной интервал начинался с 0 часов 0 минут и заканчивался 23 часами 59 минутами и 59 секундами.
Внешний вид таблицы, формируемой скриптом в файле main.php, представ
.[口 I xl.[口 I xlg Q Переход ! Ссылкиg Q Переход ! Ссылкилен на рис. 44
.[口 I xl
.[口 I xl
g Q Переход ! Ссылки
g Q Переход ! Ссылки
暮 Хнгы Mkrosoft Internet fjcptorer
Фа Ал Правка Вид Избранное Сервис Справка
©Назад ▼ 0 ■自 回 力];、门0^ ^fИзбранное 寸Медиа
Адрес; |tiht^//loalho5^cri^^ecountor/adnW
Система статистикиГосте&ая кнмга аа PHP (Файловая)Система статистикиГосте&ая кнмга аа PHP (Файловая)Адмннж:трнровани_в с 电йта6еинугьсд_н月_с_а虹—
Система статистики
Госте&ая кнмга аа PHP (Файловая)
Система статистики
Госте&ая кнмга аа PHP (Файловая)
Дата начала регистрации данных 2004-06-01
22:67:31 npwio 11 晔口
FП吋口н任раяк目
При переходе по ссылкам Хегодня ", "Вчера1' отображается детвпьная почасовая статистика загрузки за выбранный день. При переходе по ссыпкам "За 7 дней" и н3а 30 дней ” отображается детальная суточная статистика за эти периоды времениПри переходе по ссылкам Хегодня ", "Вчера1' отображается детвпьная почасовая статистика загрузки за выбранный день. При переходе по ссыпкам "За 7 дней" и н3а 30 дней ” отображается детальная суточная статистика за эти периоды времениГотово「I 丨 用 Местная интрасеть石Готово「I 丨 用 Местная интрасеть石Хиты
При переходе по ссылкам Хегодня ", "Вчера1' отображается детвпьная почасовая статистика загрузки за выбранный день. При переходе по ссыпкам "За 7 дней" и н3а 30 дней ” отображается детальная суточная статистика за эти периоды времени
При переходе по ссылкам Хегодня ", "Вчера1' отображается детвпьная почасовая статистика загрузки за выбранный день. При переходе по ссыпкам "За 7 дней" и н3а 30 дней ” отображается детальная суточная статистика за эти периоды времени
Готово「I 丨 用 Местная интрасеть石
Готово「I 丨 用 Местная интрасеть石
Рис 4.4. Внешний видстраницы распределения хитов во времени
Заголовки таблицы являются гиперссылками, переход по которым позволяет просмотреть детальную почасовую статистику выбранных суток (периоды времени Сегодня н Вчера) и суточную статистику для более длительного временного интервала (За 7 дней и За 30 дней). За отображение суточной статистики несет ответственность файл days.php, за отображение почасовой —hours.php.
На рис. 4.5 приведен внешний вид страницы, формируемой файлом days.php. В качестве параметров файла ему были переданы первичный ключ записи таблицы files, соответствующей одному из файлов (id_fiie), и
требование вывода статистики за последние 7 дней, передаваемое при помощи параметра daystst.
Рис. 4.5. Внешний вид страницы, формируемой файлом days.php
Код файла days.php приведен в листинге 4*30.
<?php
// Если число дней не передано, считаем, что вывод
// о суще ствляет с я за 7 дней
$daystst ="”;
Sdaystst = $_GET[*daystst1];
if($daystst = "”) $daystst = 7;
$title=rtXMTbi з a 5nbsp; последим e $daystst дней";
$pageinfo = 'Нз этой странице вы можете видеть детальную статистику посещений за выбранный день с группиро в кой по часам.';
$menu = 1;
// Выводим заголовок страницы include "topcounter.php";
?>
<table>
<tr><td><p>XMTbi</p></td></tr>
<?php
for($i=0; $i<$daystst; $i++)
{
$hit = get_hits ($i+l, $i, $id_file);
echo H<tr>
<td>
<a hrefe * hours. php?day==$ 1 & id f i le=$ id_f i le' > $i да. Назад
</a>
</td>
<td>$hit</td>
</tr>M;
) echo "</tabla>";
// Включаем завершение страницы include "bottomcounter.php";
广 Замечание )
В листинге 4.30 не приводится код функции get_hits (), рассмотренной выше, при описании листинга 4.29.
Отличие листинга 4.30 от 4.29 заключается в том, что вывод суточной статистики по обращению к файлу происходит в цикле for.
Наконец» код последнего файла days.php, ответственного за вывод почасовой статистики для выбранных суток, задаваемых при помощи параметра day, приведен в листинге 4.31.
T号屮"工總”:h”‘“肥可'修营产"’…零吋"胃—整^У^屮“*浊"‘吋^^У^心^^^^11^'"*:*";"'”一:…'…”:"'…""*""…""*于“‘f”!^^"…""…'…"11""'…'.*'***…"
:Листинг 4.31. Содержимое файла days.php
<?php
$title='Хиты&пЬзр;c группировкой no часам';
$pageinfо=1На этой странице вы можете видеть детальную статистику посещений за выбранный день с группировкой по часам. 1;
$menu=l;
Sday = 0;
$day = $_GET[’day'];
// Выводим заголовок страницы include ntopcounter. phpTT;
〃 Выводим таблицу
?>
<table>
<tr><tdxp>4acbi</td><td><p>XHTH</td></tr>
<?php
for($i=0;$i<24;$i++)
$hit = get_hits($i+l,$i,$day,$ld_file);
echo M<trxtd>$i</tdxtd>$hit</td></tr>"; }
echo "</table>H;
// Включаем завершение страницы include T,bottomcounter. php";
// функция, возвращающая количество хитов.
// $ begin - число часов, которое необходимо вычесть из текущей даты,
// для того чтобы получить начальную точку временного интервала
// $end - число часов, которое необходимо вычесть из текущей даты, // дая того чтобы получить конечную точку временного интервала // $day - число дней, которое нужно вычесть из текущей даты, чтобы // получить ин терес ующий нас день *
// $id_file - первичный ключ залижи таблида files, соответствующей // закачиваемому файлу
function get_hits($begin, Send, $day, $id_file) {
// Переменная $id_file определяет, осуществляете я ли запрос
// к конкретной странице или ко всему сайту, if($id_file = ”") $tmp = ”"; else $tmp = "id_fi工@=§id_file";
// Запрос на статистику за временной интервал, определяемый
// параметрами $begin,$end
if{$begin = 0) $tmp2 ="”;
else $tnp2 = rlputdate>^ate__add (DATE_FORMAT (DATE_SUB (NOW (), INTERVAL 『$day' DAY),1%Y-%m-%d 00:00:001),INTERVAL fend' HOUK)”;
$tmpl = nputdate<date__add (date_format (date_sub (now (),
INTERVAL '$day‘ DAY), ^Y-lm^d 00:00:001), INTERVAL ^begin* HOUR)11;
// Расставляем and
if ($trap2!-tMi I I $tmp 1") $andl = " and ”;
else $andl = ”";
if ($tmp2!=Mt, && $tmp !=”")$and2 = ” and、
else $and2 ="”;
// Формируем SQL-запрос
$hits = "SELECT count(*) FROM hitfile
WHERE H. $tmpl. $andl. $tmp2. $and2. $tir^);
// Выполняем SQL-запрос
$hit = mysql__query($hits);
if($hit)
(
// Если запросы выполнены удачно, преобразуем результат в массив
$total = mysql_fetch_array($hit);
return $total[T count(♦)1];
}
else
{
echo M<b>Error: n .mysql error <) . H<b>r,;
puterror("Ошибка при обращении к таблице хитов..*");
}
}
?>
Код листинга 4.31 практически полностью аналогичен коду листинга 4.30. Точно так же в цикле при помощи функции get_hits()происходит формирование таблицы с почасовой статистикой, соответствующей файлу с переданным в скрипт первичным ключом id_fiie. Единственное различие заключается в том, что функция get_hits() содержит дополнительный параметр $day, определяющий дату, для которой производится подсчет хитов.
4.11. Предотвращение загрузки страниц
В предыдущем разделе была рассмотрена задача учета загрузки файлов, предназначенных для загрузки файлов в сервера. Загрузка файлов часто поощряется владельцами ресурсов, так как регулярное обновление библиотеки файлов является существенным стимулом для посетителей возвращаться на сайт.
Другое дело — информационные материалы, размещенные на сайте. Их загрузка при помоши файловых менеджеров закачек является нежелательной, так как при этом не происходит просмотр рекламы, за счет которой, в основном, и существуют ресурсы, и не растет рейтинг сайта, так как посещения его различными роботами н менеджерами закачек относятся к незасчи- танньш хитам. Проблема является очень серьезной для бесплатного хостинга, где владелец ресурса должен выдерживать определенное соотношение между просмотром рекламы его посетителями и трафиком создаваемым загрузкой рекламы.
Ситуация усугубляется при динамическом формировании содержимого сайта, когда адрес ресурса представляет собой строку вида http:// www.site.ru?id_art-01 (02, 99). Такого рода ресурсы являются идеальной мишенью, так как позволяют легко сформировать пакетное задание для за- грузкн всего сайта.
Кроме менеджеров, позволяющих полностью или частично загрузить сайт, немалый процент посещений составляют различные роботы. Зачастую они выполняют полезные функции, в частности, собирают статистическую информацию для поисковых систем, но они же могут похищать различную информацию с сайта и автоматически размешать ее на сайте своего "хозяина*'* Нужно определенное искусство для отделения запросов, работающих на ресурс и против него.
Алгоритм предотвращения загрузки страниц менеджерами закачек и роботами очень прост: необходимо разрешать загрузку браузерам пользователей и запрещать остальным многочисленным ^посетителям" сайта.
Замечание )
Процент "посетнтеп&6-не6раузеров" может быть значительным. В зависимости от содержимого ресурса он колеблется в районе 20%.
Определить, кем является посетитель, можно анализируя переменную $http_user_agent, содержащую строку, возвращаемую браузером клиента. В состав этой строки входит информация о типе и версии браузера м операционной системы посетителя.
Вот типичное содержание ЭТОЙ строки: °Mozilla/4.0 (compatible; MSIE 6.0; windows nt 5.1)”. Наличие подстроки *'msie 6.0” говорит о том, что посетитель просматривает страницу при помощи Internet Explorer версии 6.0. Строка "windows nt 5.1й сообщает, что в качестве операционной системы используется Windows ХР.
( Замечание )
Для Windows 2000 переменная $http_user_agent выглядит следующим об- раз0М: "Mozilla/4* 0 (compatible; MSIE 5.01; Windows NT 5.0)*)м,в
то время как для Windows ХР ——**Mozilla/4.0 (compatible; MS工Е 6.0; Windows NT 5.1)”
Если посетитель воспользуется браузером Opera, то содержимое SHTTP一USER AGENT МО*еТ ВЫГЛЯДСТЬ следующим образом: ,,Mozilla/4.0 (compatible; MS IE 5,0; Windows 98) Opera 6.04 [tu]“. Подстрока msie б. о здесь также присутствует, сообщая, что браузер Opera является со* вмести мым с браузером Internet Explorer и использует те же динамические библиотеки Windows. Поэтому при анализе строки, возвращаемой браузером, следует иметь в виду, что к Internet Explorer относится строка, содержащая подстроку ms IE 6.0 и не содержашая подстроки "opera". Кроме того, из данной строки можно заключить, что пользователь использует операционную систему Windows 98.
При использовании браузера Netscape содержание переменной $HTTP_USER_AGENT МОЖСТ ВЫГЛЯДСТЬ следующим образом: "Mozilla/S.O (Х11; U; Linux 168 6; en-US; гv:1.4) Gecko/20030 62 4 Netscape/7,l'\ Принадлежность к этому браузеру можно определить по наличию подстроки Netscape'*. Кроме того, можно узнать, что посетитель выходит в Интернет, используя операционную версию Linux с ядром, оптимизированным под Pentium IV, находясь в графической оболочке Windows. Этот механизм удобно использовать для сбора статистической информации, которая позволяет дизайнерам оптимизировать страницы под наиболее распространенные браузеры.
[ Замечание )
В главе 14, где рассматриваются учет и анализ посещаемости сайта, будет рассмотрена система, позволяющая собирать информацию о посетителях и предоставлять процентное соотношение используемых ими браузеров и операционных систем.
Таким образом, запрет загрузки страницы можно легко осуществить, как это показано в листинге 4.32, остановив выполнение скрипта PHP, если содержимое переменной $http_user_agent нас по каким-то причинам не устраивает.
黄Г'"^^е["………:К「"岸擀1岩4会:曝戈…у1丁 7:T‘"":"「'当УT;限…T:“:::…F”T"":"** ,…………r"ThL 了上"了"二1豈之 :Листинг 4.32. Запрет на загрузку страницы браузером
<?php
/ / Если в строке SHTTPJJSER^GENT, передаваемой браузеромх присутствует // хоть что-то напоминающее слово Windows, остановить выполнение скрипта, // тем самым, обезопасив свой ресурс от 98-процентной армии посетителей, // использующих Windows в качестве операционной системы.
if (strpos ($HTTP__USER_AGENTf "Win») !==false) exit ();
?>
「qasa & “Хмтростм" PHP
Разумеется, следует поступать очень осторожно н не действовать по принципу “все что не разрешено — запрещено'*, как это продемонстрировано в листинге 4.32. Не стоит сразу же запрещать посещение страницы роботу, который кажется подозрительным. Поисковые роботы всегда работают во благо ресурса, позволяя посетителям быстро находить ваш ресурс.
(Замечание )
В англоязычной литературе роботов часто называют Web-spiders (Web-пауки).
Если саран и цы сайта посещаются роботами поисковых систем, по строке $http_user_agent можно определить их принадлежность. Ниже перечислены строки, которые обычно возвращают наиболее известные поисковые роботы:
口 f*StackRambler/2.0м ИЛИ MStackRambler/2.0 (MSIE incompatible)')"— Rambler;
О "Yandex/l*01 *001 (compatible; Winl6; I)*' — поисковый робот Yandex;
"Googlebot/2.1 (+http: //www. googlebot. com/bot #html) " — Google;
"Aport** —Aport.
:Замечание )
Если необходимо, чтобы поисковые роботы не индексировали часть страниц вашего ресурса (внутренние форумы, страницы администрированияt служебную информацию), не следует прибегать к вышеописанному способу, для этого следует использовать штатные средства сервера Apache, описанные в главе 1, или разместить в заголовках ННТР-документа строку <мета name=**robots** CONTENT=н noindex ” >.
Интересно содержимое $http_user_agent поискового робота Google, в нем приводится адрес, где можно ознакомиться с задачами поискового робота. Это достаточно распространенно практика.
Вот несколько примеров строк, оставленных роботами:
口 irisnbot/0.11 (+http: //search.msn.cain/msnbot.htm);
口 SurveyBot/2.3 (Whois Source};
口 LWP::Simple/5•50.
Приведем несколько примеров строк, возвращаемых серверу менеджерами закачки, которые являются кандидатами на запрет:
口 Teleport Рго/1.29 ——один из популярнейших менеджеров закачки, позволяющий выкачать сайт с сохранением его структуры;
口 Download Master;
口 GetRight/4.3;
П FlashGet;
О iaarchiverj
口 DA 5.0.
Описываемый в данной главе способ борьбы с нежелательной закачкой содержимого сайта при помощи менеджеров не является универсальным. Так как строку $http_user_agent формирует клиентская сторона, а тем более менеджер, цель которого во что бы то ни стало скачать страницу, ее часто подделывают или просто оставляют пустой. Так, строка wMozilla/4.04 [еп] (Win95; I; Nav)", якобы относящаяся к посетителю с операционной системой Windows 95, на самом деле оставлена некогда распространенным менеджером NetVampire. Тем не менее предложенный способ позволит оградить ваш сайт от неискушенных пользователей.
Не следует использовать именно эти строки для запрета. Сначала нужно выяснить, есть ли вообще проблема, связанная с использованием менеджеров, и какими именно менеджерами злоупотребляют посетители. Для этого необходимо собрать статистику, создав ловушку в начале каждого файла, фиксирующую переменную $http_user__agent каждого из посетителей с сохранением ее в файле или базе данных, как это продемонстрировано в листинге 4.33.
:Листинг 4.33. Сохранение строки $htte_user^gent в базе данных
<?php
mysql_query(nINSER INTO useragent VALUES (0J$HTTP_US£R_AGENT');”); ?>
Таблица useragent создается при помощи SQL-запроса, приведенного в листинге 4.34.
Листинг 4.34, SQL-запрос на создание таблицы для хранения
$HTTP-USEIVAGENT
CREATE TABLE useragent VALUES ( id_hitfile INT(ll) NOT NULL AUTO_工NCREMENT, usragnt TINYTEXT;
)TYPE=MyISAM;
При желании в таблицу useragent можно добавить поля для времени запроса и адреса страницы. Проанализировав дамп базы данных или создав соответствующую страницу администрирования, всегда можно быть в курсе, кто посещает ресурс.
Определение размера загружаемого файла
При размещении на Web-сайте файлов для загрузки их посетителями правилом хорошего тона считается указание размера файла. Решение этой задачи средствами PHP позволит, во-первых, автоматически определять размер файла, а во-вторых, будет служить своего рода тестом на ошибки HTML- верстки (указание ошибочных ссылок на файлы).
<?php
// функция определения размера файла function getfilesize($filename)
(
// определяем размер файла
$filesize = filesize($filename);
if ($filesize}
{
// Пересчитываем в килобайты
$filesize = $filesize/1024;
// Округляем дробную часть
$filesize = sprintf (fl% * 0fM, $filesize); return $filesize-м Кб.и;
}
else
{
return "Оайл не доступен'1;
}
}
?>
При HTML-верстке переменной $fiiesize ставится в соответствие имя файла, и в дальнейшем для указания гиперссылки и определения размера используется только эта переменная (листинг 4.36).
:'***Й:ЛГ !HW'v' **Л^ "',.•**¥*'"*;“,…"'…■ V:'"jMY-・:虽¥'7 *^Н'£屮♦.'" *:А^^ддд^^^Ц[^&阁 ki^gy^^y施qwj 产”.・;дд^у|у^ц^^д^*^^^д(уду1.
:Листинг 4.36. Установка соответствия имени файла и переменной $£il❷siz❷
■ >.Л^>^1<7*7ЛП>оГ<а>>>>>>1«>«аЛ/Лаа<7а?Ла?^МГЯ^<«Л>а*аЛ|»<^^^^^^геЛ^И^^^№^ЭТГбН>1П?ЛгХ«1* a ■'aS^rwatTwaia^VliaA/■{'а ■■/>;*<>■•'■>> >>'2l'a<・".'♦ .: ■兔♦. ?9^K …:
<?php
// указываем имя файла
$ filename = "files/file. zip*';
?>
<а href=**<?php echo $filename ?>н>Скачать файл</а>
Размер: <?php echo getfilesize($filename) ?>
Теперь замена файла новой версией приводит к автоматическому обновлению размера сайта на странице.
PHP и CSS:
динамическое оформление содержания сайта
Рассмотрим задачу.
На всех страницах сайта необходимо разместить меню, содержание которого не зависит от текущей страницы. Выбранный пункт меню необходимо выделить другим стилем для обеспечения комфортной навигации по сайту. Кроме этого, желательно обеспечить легкое внесение изменений в меню (добавление новых пунктов) без необходимости изменения кода.
( Замечание )
Список пунктов меню обычно выделяют в отдельный файл, например, menu.php.
:Листинг 4.37. Статическое оформление (файл menu.php)
»;*.*;、* ,,'f『;**+«,* ;~K%
<а class=normal liref="pagel. phpn>0 компании</ахЬг>
<а class^normal href=l,page2.рЬри>Продукция</а><Ьг>
<a class=normal hre^Hpage3. рЬрнЖонтакты</а><Ьг>
<a class=normal href=,*page4.рЬр">Документы</а><Ьг>
<a class=normal href=Hpage5.phpMЖарта сайта</а><Ьг>
Указанный файл (menu.php) подключается на каждой странице с помощью конструкции require_once ("menu.php").
Заменанме )
Во избежание случайного повторного включения файла следует использовать оператор require once вместо require или include.
Для оформления меню используются таблицы стилей CSS. Всем ссылкам присваивается класс CSS с именем normal (ciass^normal). При этом выбранный в данный момент пункт меню необходимо выделить, определив для него класс CSS с именем active (class=active).
На каждой странице перед подключением меню (inenu.php) определяем переменную с номером пункта меню, который необходимо выделить, как это продемонстрировано в листинге 4.38.
«*1”'“';1+**,・Г.«*1”'“';1+**,・Г.Листинг 4.38. Подключение меню
«*1”'“';1+**,・Г.
«*1”'“';1+**,・Г.
<?php
// выделим пункт 3 меню (Контакты) $id_menu = 3;
requi re_once (wmenu. php");
Пример динамического оформления меню приведен в листинге 4.39.
•*^yv* * ^^:^J^^v^^'^%^,**^'''^Yr,^■"■,^*^V^,'--»,'**"'**?"'*,+*^'S^^V^*''*^** ^ ■■*^2',*'^^^^^K*,'^*?f^'^'^^,'**^'^-*''l"*J^*'* ■*''*''*■**■■'*^^,'**'*H^',,*^"*,'*'i^^*,*л*'l'''"''^''4■^'*','1**•^*^'^'**" 1
i Листинг 4.39. Динамическое оформление (файл menu.php)
f府立Г; ;f:H»;“,.W. iff言.;;;京,:;;:;\玄展『.,;3W;・;'** 席『“•* %;*;#;《 *:;•;,斯 1 •;“;;;%% 器".;;:“露;力二・Л\*:**..、:*♦♦*;; :-*、』;点С”Ц」;|?总
<?php
eval (**\$^ctive$id_menu = 1;");
function stylemenu($activemenu)
{
if (isset($activemenu))
return "class^active";
else
return "class=nomalM;
echo echoecho echostylemenu($activel) stylemenu($active2) stylemenu($active3) stylemenu($active4) stylemenu($active5)stylemenu($activel) stylemenu($active2) stylemenu($active3) stylemenu($active4) stylemenu($active5)?> hre f^"pagel. php11 >0 компании</a><br>
echo echo
echo echo
stylemenu($activel) stylemenu($active2) stylemenu($active3) stylemenu($active4) stylemenu($active5)
stylemenu($activel) stylemenu($active2) stylemenu($active3) stylemenu($active4) stylemenu($active5)
?> href="page2.рЬрп>Продукция</а><Ьг>
?> href= ,Tpage3. рЬрн>Контакты</a><br>
?> href=Hpage4, рЬр'*>Документы</а><Ьг>
?> href =11 page5. php11 >Карта сайта</a><br>
Переменная выбранного пункта меню инициализируется при помоши функции eval:
eval (*'\$active$idjuenu = 1;")
К примеру, для третьего пункта ($id_m邑пи = з) меню эта конструкция ннм- иналнзнрует переменную $active3 二 1. Переменные для других пунктов останутся неинициализированными.
В каждом пункте меню для динамического определения стиля вызывается ФУНКЦИЯ stylemenu,
<а <? echo stylemenu($activel) ?> href="pagel.php”>0 компании</а></Ьг> Функция stylemenu проверяет, инициализирована ли переменная, и в зависимости от этого определяет стиль (листинг 4.40),
限|!制辟畔噺■绯駆般閣患,£圖时畢缁里恢洋嘗晔咨嚼?承1明М*縊^^^Д^^^^^У豊里啸理学油耀餐;Г竺举!111HAM叫津將,册;削图配"|<題电季口認曜瑞工气
:Листинг 4.40. Функция stylemenu
<?php
function stylemenu ($ activemenu)
(
if (isset($ activemenu))
{ return "class=activeM;
}
else
t return ис1аз5=пота1";
}
}
В качестве параметра функция stylemenu принимает переменную $activemenu. Если переменная и н и циализи рована, то функция возвращает строку class = active (активные пункты меню). Для неинициализированных переменных будет возвращена строка class = normal (неактивные пункты меню). Таким образом, оформление отделено от кода. Содержание меню отделено от содержания страницы. При этом создание дополнительных страниц н пунктов меню требует минимальных усилий.
Динамическая оптимизация страниц
Разделяя дизайн сайта и его содержание, часто страницу сайта делят на 3 блока (файла):
口 верхняя часть шаблона дизайна ("шапка"): top.php;
口 содержание страницы: namepage.php;
口 нижняя часть шаблона дизайна: bottoni,php.
Файлы top.php и bottom,plip содержат в себе элементы дизайна н, как правило, являются общими для всех страниц сайта» а файл namepage.php — информационное наполнение, включающее предыдущие два файла (top.php и bottom.php).
В результате все информационные страницы имеют одну н ту же шапку (top.php) с одним и тем же заголовком (Title)* описанием (Description) и ключевыми словами (Keywords). Но для успешной оптимизации сайта поисковыми системами необходимо» чтобы каждая страница имела свои заголовок, описание и ключевые слова. Поэтому часто вводятся управляющие переменные, позволяющие настроить заголовки, описания и ключевые слова, определив для этого переменные $title, ^description и $ keywords, разместив их перед включением фдйла top.php в основную страницу (листинг 4.41).
Г Листинг 4.41. Информационная страница (файл namepage.php)
<?php
$title = "Заголовок*1;
$description = "На этой странице содержится очень важная информация";
$keywords ^ "важная информация1 *;
require_once("top•php”);
<!— Содержание страницы --> <Ь1>Важная информациЖ/Ы> <?
require_once("top・php”);
После этого с ними можно работать в файле top.php (листинг 4.42).
:货冏а聲у^?讐咕m胃也置1翻¥Р纥寸►盥дm弟W^jyjj^^l^^;酬葡1評管鳳jj色|^^д|^^^|^用第皆■臘割跄整^^^^|^^^g| :Листинг 4.42. Шапка страниц (файл top.php)
<?php
// Если значения дая тега Title и мета-тегов не определены, // то подставляем значения по умолчанию
if (empty($title))$title="Уиебнмкпо PHP";
if (empty($description))$description="УчебникPHP. Самоучитель11;
if (en^ty(^keywords))$keywords="учебникPHP руководство PHP";
?> <html> <head>
<title><?php echo $title; ?></title>
<meta name=ndescriptionM contend1 <? echo $description; ?>f >
<meta nane=nkeywordsи content=H<? echo $keywords; ?>**> ?>
Дата последнего обновления страницы
Правилом хорошего тона в Интернете является указание даты последнего обновления страниц. Рассмотрим, как можно решить зту задачу средствами PHP.
Код PHP для получения даты последнего обновления вынесем в отдельный файл getdaie.php и будем подключать этот файл на каждой странице сайта инструкцией: require_once (Mgetdate.php");
Код файла getdate.php приведен в листинге 4.42.
! Листинг 4・42・ Получение даты последнего обновления страницы.
:(файл getdate.php)
<?php
// устанавливаем локаль для форма тирования дат функцией strftime set locale (LC_TIME, "ru11);
/ / получаем имя текущей страницы из переменной $PHP_SELF
// получаем базовое имя файла иэ переменной $PHP_SELF
// убираем ненужные компоненты пути (директории) , если они есть Sfilename^ basename ($PHP_SELF);
// форматируем вывод даты в соответствии с установленной локалыо echo strftiine ("%d %В %Yn, fxlemtime ($filename));
?>
Автоматическое распознавание ссылок
Часто возникает необходимость в работе с данными, выводимыми на страницу, уже после того, как они опубликованы на ней. Например, разместить извлеченные из базы данных значения заголовка страницы (Title) и описания страницы (мета-тег Description). При зтом обращение к базе может происходить после того, как заголовок и описание страницы уже выведены на страницу HTML Другой задачей является отправка НТТР-заголовков, работа с сессиями или cookies после вывода в окно браузера при помощи функций echo, print И Т. П.
Еще один пример — автоматическаУ! расстановка ссылок на ресурсы в гостевой книге. Именно этот пример н будет рассмотрен (листинг 4.43).
Допустим, гостевая книга по соображениям безопасности не поддерживает HTML-теги. Посетители сайта оставляют в гостевой книге сообщения, где гиперссылки на ресурсы указаны в виде текста, например: http:// www.pecypc,ru.
LЛистинг 4.43. HTML-код страницы без автоматического распознаванияLЛистинг 4.43. HTML-код страницы без автоматического распознавания,” “.…«.у ■ • »■ •••••*5«*v*,” “.…«.у ■ • »■ •••••*5«*v*ССЫЛОКССЫЛОК
LЛистинг 4.43. HTML-код страницы без автоматического распознавания
LЛистинг 4.43. HTML-код страницы без автоматического распознавания
,” “.…«.у ■ • »■ •••••*5«*v*
,” “.…«.у ■ • »■ •••••*5«*v*
ССЫЛОК
ССЫЛОК
<р>Привет! Я Вася. Вот моя страничка: www.vasia.com </р>
<р>Вас приветствует 工Т-студи只 SoftTime 一 www.softtime.ru </p>
Содержимое страницы гостевой книги необходимо модифицировать таким образом, чтобы гиперссылки, набранные в виде текста, выводились как полноценные гиперссылки в HTML
Модифицировать содержание страницы после того, как она была “сформирована”,можно с помощью функций буферизации вывода.
В самом начале страницы поместим функцию:
obstart()
Эта функция включает буферизацию вывода. В результате все содержимое страницы, кроме шапок (headers), поме ищется во внутренний буфер, который в дальнейшем можно редактировать.
Функции для обработки содержимого буфера размещаются после блока содержимого страницы, которое необходимо модифицировать (листинг 4.44).
:Листинг 444" Автоматическое распознавание ссылок
<?php
ob_start(); ?>
Код гостевой книги. В теле сообщений содержатся ссыпки в виде текста.
<?php
// занесение содержимого буфера в переменную
$mtrtn^ = obgetcontents();
// очищение буфера вывода и отключение буферизации вывода obend^clean《);
// замена текстовой ссылки ссыпкой HTML. Заменяются строки формата
/ / http: //www. ресурс и www. ресурс
$strtmp = preg_replace("/(http:\\/\\/www|www)(\\S+)/si"/ "<a href=Hhttp: //www\\2,l>www\\2</a>l\ $strtmp);
// вывод модифицированного содержимого страницы в браузер echo $strtinp;
?>
8 Зак 1181
После размещения содержимого буфера во временной переменной $strtmp при помощи регулярных выражений все URL, встреченные в тексте, заменяются на соответствующие гиперссылки. Для п редотвраше н и я преждевременного вывода буфер страницы очищается при помощи функции ob_end__ciean(). После того как все преобразования совершены, результат в переменной $strtmp выводится в окно браузера. Результатом работы скрипта, приведенного в листинге 4*44, является HTML-код в листинге 4.45.
страницы с автоматическим распознаванием ссылок
.:....-i^^J. . .'.ч.-. .-Л^о; .Л;:оЛ ^|ЖЙ* лй . . . '.-i^l. /. . , ] . . X :;“;『.*:; £拧^^二:“厘:,:“;;二證W .JTJj.r:??®■孫*工
<р>ГТривет!月 Вася* Вот моя страничка:
<а href = "http: / /www.vasia. com">wwv/. vasia. com</a></p>
<p>Bac приветствует IT студия SoftTime -
<a href=Mwww.softtime.run>www.softtime.ru</a></p>
Глава 5
Безопасность создаваемых приложений
В этой главе рассмотрены вопросы безопасности создаваемых Web- приложений. Безопасность в среде Интернет включает в себя много различных аспектов, таких как система защиты Web-сервера, безопасность баз данных, проверка данных, вводимых пользователями, приемы и методы шифрования и др* По каждой из этих тем можно написать хорошую книгу, и, безусловно, в этой небольшой главе невозможно полностью рассмотреть все вопросы, относящиеся к этой обширной сфере, поэтому данная глава является скорее введением в вопросы безопасности.
Проверка корректности данных, вводимых пользователем
Проверке корректности данных, вводимых посетителем необходимо уделять большое внимание, поскольку необработанные ошибки, возникающие при неправильном вводе данных, приводят к серьезным ошибкам в работе скрипта. Допустим, в HTML-форме для отправки пользователем письма посетителю необходимо ввести адрес электронной почты. В этом случае для корректной работы Web-приложения нужно проверить следующее:
口 поле, в которое заносится электронный адрес, непустое;
соответствие введенного адреса с помощью регулярного выражения.
Кроме случайных ошибок посетителя, необходимо также исключить ситуации, в которых возможно злонамеренное введение некорректных данных, к примеру, различных скриптов. Для этого вводимый текст необходимо обработать функциями удаления HTML-тегов (для исключения возможности написания скриптов на JavaScript и Visual Basic) и обратных слэшей (для исключения возможности написания скриптов на Perl). Таким образом, ми-
нимальный набор действий, необходимых для проверки корректности данных, вводимых пользователем, включает следующие этапы:
проверка заполнения посетителем обязательных для ввода полей;
проверка допустимости вводимых данных (как правило, осуществляется при помощи регулярных выражений);
обработка введенного текста фуикцией htwispecialchars для удаления HTML-тегов;
口 обработка введенного текста функцией stripslashes для удаления обратных слэшей.
Проверка заполнения обязательного поля
Пусть имеется HTML-форма с двумя текстовыми полями: для имени пользователя и его e-mail. Форма размещается в файле index.php (листинг 5.1).
;Листинг 5.1. Файл index.php,-j
*機;W*;;;*;F;盘?ЖСЗ.:;г* .;^«;;г'Л...^.*.Л...,:萩•池:* 需勿^^心吉 f 展品U.K—* ^i,^^*iAii^『第、;#态佈;入口* <;«:
<form action=handler.php metho^post> Имя : <input typ^text name-name><br> e-mail : <input type=text name=emailxbr> <input type-submit уа1ие=0тправить> </form>
Проверить, что посетитель ввел данные, можно с помошью функции isset (листинг 5.2).
.屮 ММДИМ-*-?: ±「,,*¥М**,4^$Ч1 '产匕11 Ш_^^МУУ ‘:卡 КАМР^ЗДКИЬЛистинг 52 Файл handler.php.屮 ММДИМ-*-?: ±「,,*¥М**,4^$Ч1 '产匕11 Ш_^^МУУ ‘:卡 КАМР^ЗДКИЬЛистинг 52 Файл handler.phpg屮咨營袁喻V ■黯辞單^^^f :彳辞r龄弊;嘴^口g屮咨營袁喻V ■黯辞單^^^f :彳辞r龄弊;嘴^口
.屮 ММДИМ-*-?: ±「,,*¥М**,4^$Ч1 '产匕11 Ш_^^МУУ ‘:卡 КАМР^ЗДКИЬ
Листинг 52 Файл handler.php
.屮 ММДИМ-*-?: ±「,,*¥М**,4^$Ч1 '产匕11 Ш_^^МУУ ‘:卡 КАМР^ЗДКИЬ
Листинг 52 Файл handler.php
g屮咨營袁喻V ■黯辞單^^^f :彳辞r龄弊;嘴^口
g屮咨營袁喻V ■黯辞單^^^f :彳辞r龄弊;嘴^口
<?php
// Проверяем, передано ли значение полю name if (1isset($ POST[1name'1))
// если переменная $name не установлена, просим повторить ввод имени echo пНе введено значение в обязательное к заполнению поле name<br>"; echo °<а href=# onClick=rhistory.back()'>Вернуться к правке</а>";
exit ();
else
〃 Производим обработку данных HTML-формы
}
?>
В листинге 5.2 приведен обработчик HTML-формы index.php, посылающей методом post значение поля паше< Если элемент суперглобального массива $_post [1 name1 ] не установлен, обязательное поле пате не было заполнено и в окно браузера выводится предупреждение и ссылка возврата, реализованная при помощи функции JavaScript — history.back(}.
Код, приведенный в листинге 5.2, не решает проблему пробелов, которые можно ввести в качестве значения текстового поля name. Для этой же цели можно использовать функцию empty (листинг 5.3).
;…胃…;^£** r0\……*:..,.:*:":・…丁…**"^J 里,Wf ум^^;1yН*”::/.翼聲^^^^^^^^.、. uq.;g[” f **:.….^^.;*;『" *,дд>" ■ \*:**d*扌"y±勺-w*[県靠不
:Листинг 5.3. Другой вариант файла handler.php
<?php
// Удаляем пробельные символы в начале и конце строки
If (isset ($_POST [ fnanie 口}) $name = trim($_POST [ 'пашйт ]);
else $name 工”";
// Если значение переменной пусто, выводам сообщение об ошибке if (empty ($паше))
{
// если переменная $паше не установлена, просим повторить ввод имени echo "Не введено значение в обязательное к заполнению поле naine<br>"; echo "<а href=# onClic)^rhistory.back()^Вернуться к правхе</а>";
exit ();
}
else
{
// Производим обработку данных HTML-формы
}
?>
Проверка допустимости вводимых данных
Допустим, необходимо проверить данные формы для отправки сообщения гостевой книги. Как правило, такая проверка осуществляется при помощи
регулярных выражений. В качестве примера можно привести регулярное выражение для проверки адреса электронной почты, рассмотренное ранее в главе 4,
Исходя из того, что адрес должен иметь вид something@server.com, регулярное выражение для проверки адресов электронной почты принимает следующий вид:
н/[0-Эа-zJ+@[0-9a-z_A \・]+\J a-z J{2,3}/土"
Точно таким же образом можно проверить и остальные заполняемые пользователем поля. Так, если поле допускает ввод только чисел, то проверку допустимости можно осуществить при помощи следующего простого регулярного выражения: ”/[0-9]+/"
Это выражение соответствует одному или большему количеству чисел. Если формат вводимого числа строго определен, например, для почтового индекса, то задать число цифр можно при помощи фигурных скобок: ”/【0-9]⑹/"
Данное регулярное выражение соответствует шестизначному числу, например 600 300.
Проверить IP-адрес на соответствие можно при помощи выражений вида VI0-9] {1,3)\. [0-9] {1,3)\. [0-9] {1,3}\. [0-9] <1лЗ)/»
[ Замечание )
Приведенное выше выражение не совсем точно описывает IP-адрес, так как допускает ввод неправильных IP-адресов вида 999.999.999.999, но является хорошим компромиссным решением. Для более точного, но и более сложного решения следует обратиться к книге Дж. Фридла ''Регулярные выражения'*.
Совет )
Не забывайте о том, что, пользуясь вашим приложением, посетитель не должен мувствоеать себя словно в клетке. По этой причине не стоит задавать жесткий формат, например, для поля Имя. Следует давать возможность пользователю ввести то имя, которое он хочет, а оно может состоять из любых видимых символов.
Удаление HTML-тегов и обратных слэшей
Вводимый посетителем текст необходимо обрабатывать функциями удаления HTML-тегов (для исключения возможности написания скриптов на JavaScript и Visual Basic) и обратных слэшей (для исключения возможности написания скриптов на Perl). К примеру, если переменная $пате содержит
текст с именем пользователя, то обработка этого текста может выглядеть следующим образом (листинг 5.4).
инг 54 Обработка текста с именем пользователя
I K'#Av^ffl^4"i4^S^r» ・珏**. ♦才а “ #* г^^А ”,* irMr^tfS*、■ ■ nt # > ■?!■^^R-rr##ьл*■?*♦* * * *, ♦.,."Т?^^,* Г.. !#*.」г?工】:“,в ■ ■ ■ ■ ■ ■ в.«"^Jb^rB>n ^пЬ,;“i,X. **
<?php
$name = html specialchars (stripslashes ($_POST [Fame'])); ?>
Применение функции htrtilspeciaichars гарантирует, что любой введенный пользователем код (PHP, JavaScript и т. д.) будет отображен, но выполняться не будет. Функция имеет следующий синтаксис:
string htmlspecialchars (string str [, int quote_style [t string charset]])
Через первый обязательный параметр str функции передается обрабатываемый текст, который возвращается после преобразования как результат работы.
Второй необязательный параметр quote__styie задает режим обработки одинарных и двойных кавычек. По умолчанию этот параметр соответствует константе ent_compat, в данном режиме двойные кавычки заменяются символом ''";“,а одиночные остаются без изменений. Кроме этого, параметр может принимать два других значения: eZt_quotes и enjnoquotes* В первом случае помимо двойных кавычек преобразованию подвергаются также одинарные кавычки, которые заменяются символом "';°. Значение параметра ent_noquotes задает режим, в котором не один из видов кавычек не подвергается преобразованию.
Последний параметр charset определяет кодировку, напрнмер, "ср1251" или "КО18 K.
Функция htmlspecialchars предназначена для отображения кода и HTML- разметки на Web^TpaHHixe, но вводимые пользователем данные не мешает пропускать через нее во избежание неприятностей.
В листинге 5.4 функция stripslashes предназначена для удаления обратных слэшей н имеет следующий синтаксис: string stripslashes (string str)
Функция принимает единственный параметр str с обрабатываем。访 строкой. Результатом работы функции является строка str, в которой удаляются экранирующие обратные слэши (символ \『преобразуется в f, двойные обратные слэши (\\) преобразуются в одиночные (\) и т. д.)-
220220Часть 丄 Основы разработки пр酬门о*ен!4@ на PHP 5
220
220
Публикация изображений и файлов
Часто Web-приложения, такие как форум или фотогалерея, позволяют посетителям публиковать собственные изображения или файлы. Это могут быть фотографии, музыкальные, текстовые или бинарные файлы. Передача пользовательских файлов на сервер уже несет в себе потенциальную опасность и при невнимательном кодировании может привести к серьезным последствиям. Для демонстрации угрозы, исходящей из пользовательских файлов, разработаем небольшое Web-приложение, которое закачивает файл пользователя на сайт и предоставляет ссылку для скачивания или его просмотра (если это изображение или текстовый файл).
Приложение будет состоять из двух файлов: index.php, который является HTML-формой Ш1Я отправки файлов на сервер, и handler.php — обработчика формы. Содержимое файла index.php представлено в листинге 55
Листинг 55 Файл index.phpЛистинг 55 Файл index.php,.、:;;;;.К.: —:“:Т.Н,.、:;;;;.К.: —:“:Т.Н
Листинг 55 Файл index.php
Листинг 55 Файл index.php
,.、:;;;;.К.: —:“:Т.Н
,.、:;;;;.К.: —:“:Т.Н
<fom enctype=Multipart/form-data1 action=handler.php method=post> Файл : <input type=file name=filename><br>
<input type^submit value=OTnpaBHTb>
</form>
HTML-форма нмеет единственное поле ввода типа file, а также кнопку для отправкн данных обработчику handler.php, содержимое которого приведено в листинге 5.6.
Листинг 5.6. Файл handler.php
<?php
// Проверяем содержимое параметра filename, передаваемого обработчикуr //из форьы, в файле index.php
if(isset($_POST[T filename1])) $filename = trim($_POST[1 filename']); else $filename =电”;
// Если обработчику передано пустое поле filename
// или содержащее пробелы, игнорируем обращение,
//в противном случае выводам ссыпку на файл
if (!empty($filename))
(
// Если копирование произведено удачно, выводим ссылку на файл.
if (copy ($f ilename, $__FILES [1 f ilenainef ] [r name * ]))
echo n<a href=". $ FILES [ * filename1 ] [ 'name1 ]. и>Посмотреть</а>м;
else
echo "Ошибка при передаче файла на сервер.н;
} else
// если переменная $filenanie пуста, просим повторить загрузку файла echo "Имя файла не введено, повторите, пожалуйста, операцию.<br>11; echo ”va href=# onClick=,history.back()'>Вернуться к отправке</а>"; exit ();
После отправки при помощи HTML-формы, приведенной в листинге 5.5, изображения или текстового файла посетителю предоставляется возможность посмотреть содержимое отправленного файла при переходе по ссылке Посмотреть,
На первый взгляд, код в листинге 5.6 производит впечатление достаточно проработанного и предусматривающего все возможные варианты развития ситуации. Тем не менее данное Web-приложение позволяет полностью уничтожить сайт, на котором оно расположено. Отправка при помощи данной формы файла, например, приведенного в листинге 5.7, приводит к рекурсивному удалению файлов сайта.
:+*^^>^^^^^^****1|^^^^^^^^看"[『*',""置**'"]::::':/?*^^1^**.号 ^^*' .潼 ^^^^^^^^^^^"''"'''"*Я^^"^^*^Г 宾^^^c^^^^s
;Листинг 5.7. Рекурсивное удаление файлов сайта
,>♦*;♦***:衣*>***.*****“г*Е“з:・*・・П・・”“:』*・*а・《,工*+♦♦;*♦•**;**、**»♦;・♦'> J.**.;”*.»*,,**;,”“工 j**“*M“*“,;3“"***;;;*.w**..*+”***i.”*\+«i.
// Вызываем функцию,// подняться на один// Вызываем функцию,// подняться на одинудаляющую файлы с сервера, с параметром "・*", чтобы уровень выше: это позволит уничтотвзггь больше файлов,
// Вызываем функцию,
// подняться на один
// Вызываем функцию,
// подняться на один
// так как наверняка Web-приложение находится в отдельной директории. delfiles(”..");
// Функция, удаляющая каталоги и файлы сервера
function delfiles(^catalog)
$dir = opendir(Scatalog);
while(($file = readdir($dir)))
// Если текущий объект является if{is_file(^catalog.'7M.$file)) // Если текущий объект является// Если текущий объект является if{is_file(^catalog.'7M.$file)) // Если текущий объект являетсяфайлом - уничтожаем егоunlink($$catalog«"Z".$file);каталогом 一 рекурсивно вызываемфайлом - уничтожаем егоunlink($$catalog«"Z".$file);каталогом 一 рекурсивно вызываем
// Если текущий объект является if{is_file(^catalog.'7M.$file)) // Если текущий объект является
// Если текущий объект является if{is_file(^catalog.'7M.$file)) // Если текущий объект является
файлом - уничтожаем его
unlink($$catalog«"Z".$file);
каталогом 一 рекурсивно вызываем
файлом - уничтожаем его
unlink($$catalog«"Z".$file);
каталогом 一 рекурсивно вызываем
// функцию delfiles(), заботливо избегая каталогов "." и
// соответствytcu^tx текущему и вышележащему каталогам.
else if (is_dir($catalog,V/H.$file) && ($file !="・"} && ($file !=del files ($catalog. ,7t**$file);
}
// Закрываем директорию
closedir(Sdiг);
// Удаляем директорию nndir($catalog);
В результате работы приведенного в листинге 5.7 кода будут уничтожены все файлы приложения и файлы, расположенные уровнем выше. После небольшой моднфнкаинн рекурсию можно направить вверх по дереву каталогов» и если скрипт обладает достаточными правами, можно уничтожить файлы всего сервера.
Решить данную проблему может проверка расширения файла. При этом не следует запрещать расширения, например, php, phtnil или php3, их следует разрешать, и публиковать только то, что непосредственно разрешено (листинг 5.8). В противном случае можно не учесть другие расширения, например, расширения Perl-скриптов pl, файлы с расширениями html и htm, позволяют не только публиковать безобидный HTML-код, но и получать файлы с сервера при помоши НТТР-заголовков.
: Замечание )
HTTP-заголовки подробно рассмотрены в главе 3.
Если же не представляется возможность проводить разрешающую политику, необходимо свериться с конфигурационным файлом сервера Apache и запретить все зарегистрированные файлы.
• "j^'f •J^^^^^^^^y^^^^J^l^^^^^f^^^jj^^^ ушу •>"匸屮 T^^tffi^^X^^^y^.^y^y^^tfitf^yff1^」,•••° •• *u^*gHH^M^wMwj :Листинг 5.8・ Файл handler.php
<?php
// Проверяем содержимое параметра filename, передаваемого обработчику // из формы, в файле index.php
if(isset($_POST[1 filename1])) $filename = trim($_POST[* filenamer]); else $filename 口 "”;
// Если обработчику передано пустое поле filename
// или содержащее пробелы, игнорируем обращение, в противном случае
/ / выводам ссылку на файл
if (!empty(Sfilename))
// Формируем массив разрешенных к публикации файлов
$extentions = array (н.gif"# ".txtri, "* jpg11);
/ / Если расширение файла совладает с одним расширением из массива
// $extentions, закачиваем файл на сервер
if(in_array(strrchr($_FXLES[1attach1][1name'], ".°),$extentions))
{
// Если копирование произведено удачно, выводим ссылку на файл.
if (сору($filename, $_FILES[1 filename'][*name1]))
echo "<a hEef=".$_FILES[1 filename1][1 name,]. ,|>Посмотреть</а>н; else
echo "(^дибка при передаче файла на сервер. °;
}
else
{
// Если расширение файла имеет недопустимое значениеf выводим
// предупреждение и ссылку возврата.
echo "Файл имеет недопустимое расширение.";
echo "<а href=# onClick^11history.back()1>Вернуться к отправке</а>н; exit ();
}
}
else
// Если переменная $filename пуста# просим повторить загрузку файла echo "Имя файла не введено, повторите, пожалуйста, операцию.<Ьг>"; echo "<а href=# onClick='history.back()1>Вернуться к отправке</а>п; exit();
}
?>
Продемонстрированная "днра” в безопасности показывает, насколько легко можно допустить ошибку при проектировании Web-приложения. Подобного рода ошибки совершить очень легко; приемы, описанные в данной главе, не оберегут ото всех возможных ошибок. Поэтому необходимо проводить регулярное резервное копирование файлов сервера и дампов баз данных. Лучше, если это будет выполнять не человек, а автоматическая система.
Методы шифрования
В этой главе мы рассмотрим основные функции, при помощи которых можно осуществлять шифрование средствами PHP*
Однонаправленное шифрование
При однонаправленном шифровании строка зашифровывается таким образом, что не подлежит обратной расшифровке. На первый взгляд, это может показаться странным, в действительности же такой метод шифрования используется очень часто. Функции, с помощью которых реализуется однонаправленное шифрование, называются функциями хеширования* При использовании таких функций создается уникальный "отпечаток1' строки. Наиболее часто в качестве алгоритма хеширования используется алгоритм MD5, реализовать который можно с помощью одноименной функции: string mdb(string str [, bool raw_output])
В качестве обязательного аргумента эта функция принимает строку str, которую необходимо зашифровать, и возвращает ее уникальный 128-битовый отпечаток (хеш-код). Если необазате/гьньЕ访 аргумент raw_output имеет значение true, то возвращается бинарная строка из 16 символов. Вероятность того, что две строки дадут одинаковый хеш-код, стремится к нулю.
:Зам.нание )
Функция md5 так же часто используется для создания уникального хеш-кода объемных файлов, которые передаются по сети. Загрузив файл, всегда можно проверить его целостность, вычислив код по алгоритму MD5 и сравнив полученный результат с хеш-код ом, предоставляемым распространителем. Это позволяет отследить повреждения файла, вызванные передачей через сеть, а также предотвращает фальсификацию дистрибутива.
При помощи этой функции можно зашифровывать различные данные, к примеру, пароли пользователей. Это позволяет организовать следующий алгоритм авторизации пользователей. При первой регистрации пользователя сохраняется хеш-код его пароля (к примеру, в базе данных). При дальнейших посещениях странички хеш-код вводимого пользователем пароля сравнивается с сохраненным ранее хеш-кодом. Если эти отпечатки совпадают, авторизация считается успешной (см. листинг 5.15).
; Замечание )
Такая схема авторизации не позволяет совершить похищение пароля из базы данных; даже если злоумышленнику станет доступна база данных, он не сможет воспользоваться зашифрованными паролями*
Рассмотрим небольшое Web*пpилoжeниe, осуществляющее авторизацию пользователя по сохраненному ранее в базе данных паролю. Web- приложение включает HTML-форму, расположенную в файле index.php (листинг 5.9), и ее обработчик, расположенный в handler.php (см. листинг 5Л0).
:Листинг 5.9. Файл Index.php
<fom action=handler.php method^post>
Имя посетителя : <input type=text name=name><br> Пароль : <input type=password name=passwordxbr> <input type=submit va1ие=0тправить > </form>
HTML-форма в листинге 5.9 имеет два поля: текстовое поле для имени посетителя, а также поле типа password для ввода пароля. Кроме того, в форме присутствует кнопка, позволяющая отправить обработчику handler.php (листинг 5.10) данные из формы.
翠* 二:…¥實昔
Листинг 5.10. Файл handler.php
<?php
// Код, извлекакяций по имени посетителя паше хеш-код, соответствующий его / / паролю, из базы данных и помещающий его в переменную $hash 〃…
// если хеш-кода совпадают, авторизация прошла успешно
if (md5 ($_POST [ password1 ]) — $hash) echo "Hello, $name";
else echo нВведен неправилычый паролья;
?>
Для этих же целей используется функция crypt: string crypt(string str [, string salt]) Первый аргумент — str — представляет собой строку, которую необходимо зашифровать, а второй —— salt представляет собой необязательную строку» на которой основывается шифрование.
:Замечание )
Если аргумент salt не передан, он будет выбран случайным образом.
К примеру, если передать этой функции строку с паролем "gfkjxrb99T' и аргумент salt, равный "七七七”,то будет возвращена строка "ttHcYCTyRezve1*,
которая не может быть дешифрована. Однако поскольку результат работы функции сгрого определен в том случае, когда параметры str и salt постоянны, ее можно использовать для проверки паролей. Так, файл handler.php можно переписать следующим образом (листинг 5.11).
,.,■>■ • 1 ••*>.......... •.<.,,,. ::, ••■,. :::
;Листинг 5J1, Файл handler.php
<?php
// Кодг извлекающий по имени посетителя name хеш-код, соответствующий его
// паролю, из базы данных и помещающий его в переменную $hash
〃…
// если хеш-коды совпадают, авторизация прошла успеизно
if (crypt($maks_password, 'ключ, фраза1) = $hash) echo "Hello, $nameH; else echo trВведен неправильный парольи;
?>
Для однонаправленного шифрования можно также использовать функцию shal, имеющую следующий синтаксис:
string shal ( string str [, bool raw__output])
Возвращает хеш-код строки str, вычисленный по алгоритму US Secure Hash Algorithm 1.
Замечание )
Данный алгоритм подробно описан в RFC3174 (wwwJetf.org/rfc/rfc3174.txt).
Хеш-код представляет собой 40-разрадное шестнадцатеричное число. Если необязательный аргумент raw__output имеет значение true, хеш-код возвращается в виде двоичной строки из 20 символов.
Симметричное шифрование
При симметричном шифровании строки шифруются с помощью ключа, который известен как отправителю, так и получателю. Алгоритмы симметричного шифрования в PHP реализованы в библиотеки mcrypt, и ознакомиться с полным набором функций и алгоритмов этой библиотеки можно на сайте http://www.php.net.
( Замечание )
Функции mcrypt работают только, если подключена библиотека mcrypt. Для этого PHP необходимо скомпилировать с опцией --with^mcrypt. Саму библиотеку libmcrypt-x.x.targz можно загрузить по адресу http://mcryptheHug.gr/.
В листинге 5.12 приведен пример шифровки и дешифровки строки при помощи алгоритма 3DES (Triple-DES),
:Листинг 5Л2
mHXii 不斯Лт)磊■;大詰怎1.二;;>^7Л> ■ > л>»• Л гиг;»»»..詩г* 正概 л.< >“;;';:.&:巻靠1;^?^.診抬杀1
<?php
// зашифровываем пароль
$ user_pas sword = "gf kjxrb99,T;
$key = "Это секретный ключ1*;
// шифруем пароль с использованием ключа $кеу
$user_crypt = mcrypt_ecb (MCRYPT_3DES, $кеу, $maks_passwordr
MCRYPT—ENCRYFT);
echo 11 Зашифрованный пароль - ".$user_crypt;
// расшифровываем пароль
$u$er_crypt= merypt_ecb (MCRYPT_3DES, $key, $user__cryptf
MCRYPT—DECRYPT);
echo и Расшифрованный пароль - м.$user_crypt; ?>
В листинге 5Л2 пароль, размещенный в переменной $ us er__pas sword, сначала шифруется функцией mcrypt_ecb () с применением ключа $кеу, а затем проводится его обратная дешифрация.
Проблемы безопасности, связанные с cookie
Cookie это механизм хранения данных браузером удаленной машины» который применяется в основном для отслеживания или идентификации возвращающихся посетителей. Иногда в cookie приходится хранить конфиденциальные данные, в этом случае разработчик Web-приложения должен позаботиться о том, чтобы информация, хранящаяся в cookie, не попала третьим лицам. Существует несколько методов защиты информации, хранящейся в cookie:
установка области видимости cookie;
口 шифрование;
ограничение доступа для доменов;
отправка cookie по защищенному запросу.
Наилучшим решением является комплексное применение всех этих способов.
{Замечание )
Более подробно познакомиться с технологией cookie можно в RFC2965 (www.ietf.org/rfe/rfc2965.txt).
Установка области видимости cookie
Поскольку по умолчанию доступ к cookie имеют любые Web-приложения, расположенные в любом месте сети, работа с cookie может создать ^дыры'1 в системе защиты. Это означает, что если Web-приложение с другого ресурса "угадает” имя cookie, оно будет ему доступно. Функция setcookie(), предназначенная для установки cookie, предоставляет гибкие возможности для ограничения доступа к cookie. Так, код, приведенный в листинге 5.13, разрешает работу с установленными cookie только лля страниц, расположенных в конкретном каталоге, к примеру, /web.
■”:"3嘗m,吃寄*党!邂尹"^戏咨華給'瓏国穿‘'押,痺第竺图革罐孳;蟠弊衿屮注+三二^二f *巻三=7""滲泸丁 2厅賢*充*于**?:
Листинг 5.13. Ограничение доступа с cookie
set cookie (nname", $value, 11/web/1');
Однако и в этом случае каталоги /web/index.php, /web/page.html н т. д. будут удовлетворять этому ограничению* Если такое положение также является нежелательным, можно ограничить область видимости cookie конкретной страницей (листинг 5.14).
買:学甯督军4W 臂F",買:学甯督军4W 臂F",出咗嘗*/^混?琴忆岸学岂‘虫;沿‘:関第”'出工"制‘:整飴”抬"轉:'""一*^^‘
買:学甯督军4W 臂F",
買:学甯督军4W 臂F",
;Листинг 5.14. Ограничение области видимости cookie
setcookie (Hname", $ value # 11 / web/index, php'1);
Тем не менее такой способ в полной мере не решает проблему перехвата cookie чужими Web-приложениями, так как в этом случае доступ к инфор' мации, содержащейся в cookie, может получить, к примеру, скрипт /web/index.php^scriptZanti^cookie.php. Если опасность доступа сохраняется, то возникает необходимость в шифровании.
Для дополнительной безопасности можно ограничить список доменов, имеюших доступ к cookie. Это можно сделать при помощи следующего кода (листинг 5」5).
叩,学^^С?‘曜* uw强江窮:^^望*^^1 做"岁^^38й^&"炉у W,'WLL*.y"w*f с …”'""'"ww:
;Л"0TM工5 發卫 rp 映ченне спнск*оМнов …^^^g^Ht^^^gj
setcookie ("name", $ value, ” /web/ index. php'*, " . server, com");
При таком ограниченнн заданной области видимости условию будут соответствовать домены с именами server.com, myservser.com, php.server.com и т. д., поскольку проверка на допустимость области видимости домена осуществляется по принципу концевого соответствия.
Если шестому необязательному параметру присвоить значение 1, cookies будут устанавливаться через защищенное HTTPS-соединение (листинг 5.16).
;Листинг 5.16. Отправка cookies по защищенному запросу" И—?
setaookie("name0, $value, time() + 600, "/web/n, server•com", 1);
Шифрование cookie
Для обеспечения дополнительных мер безопасности секретную информацию, хранящуюся в cookie, можно подвергать шифрованию. Часто в форумах, интернет-магазинах и других Web-приложениях, требующих авторнза- цин, предоставляется следующий сервис: авторизовавшись один раз, посетителю в течение некоторого времени разрешается не вводить имя и пароль, так как постоянный ввод данных может раздражать посетителя. Такой сервис реализуется либо при помощи механизма сессий, либо cookie. Сессии формируют достаточно длинный URL, затрудняющий продвижение ресурса в поисковых системах, так как поисковые роботы предпочитают не работать с длинными URL. Поэтому часто прибегают к механизму cookie. Здесь будет разработано небольшое Web-приложение, демонстрирующее шифрование cookie в этом случае. Приложение будет состоять из двух файлов, HTML-формы index.php (листинг 5Л7) и ее обработчика handler.php (см. листинг 5.18).
|•^,y••^••*.•^,•,/'.*■*•.* м •■••••••••■•••••••••••••••••у «••■•••••••••»••»•,•••••■••,• v",*V-,".,,,,4t*l'*,**W>'*i,A*-,,'*-**?*,**',*,'*,W",,"''IV!*,,W***,***,",**,VV'*'S^y,",,*X, Листинг 5Л7:Файл^т^^^^ 」
<?php
// вектор начального состояния остается неизменным
$кеу = мЭто секретный клюй";
// Расшифровываем пароль г извлеченный из cookie
$password= mcrypt_ecb(MCRYPT_3DES, $key, $_COOKIE[1password1]f
MCRYPTDECRYPT);
?>.
<form act ion=handler.php method=post>
РЬля посетителя : <input type^text name=name
value=<?php echo $_COOKIE [ *name1 ] ?»<br>
Пароль : <input type=password name=password value=<?php echo $password; ?»<br>
<input type=subnit value=OrnpaBKTb >
</form>
HTML-форма, приведенная в листинге 5.17, имеет два текстовых поля: для ввода имени name и пароля password, после заполнения которых введенная в них информация отправляется в обработчик формы handler.php (листинг 5Л8).
<?php
// создаем вектор начального состояния для шифрования
$кеу м "Это секретный ключ";
^password = mcrypt_ecb (MCRYPT_3DES, $key, $maks_password, MCRYPT__ENCRYPT);
// устанавливаем cookie с именем посетителя и паролем (зашифрованным)
// на двое суток
setcookie(nuserH, $_POST[1name1], time() + 3600*24*2);
set cookie (npas sword'*, $ password, timet) + 3600*24*2);
// Выводим приветствие
echo nЗдравствуйте,"・ $_COOKIE[1user1];
echo "<br><a href-index.рЬр>Вернуться к форме</а>н
?>
В обработчике handler.php полученный из формы пароль шифруете只 по симметричному алгоритму 3DES, имя пользователя и зашифрованный пароль сохраняются в cookie со сроком действия двое суток. Теперь если вернуться к форме, то ее поля будут уже заполнены.
Безопасная установка PHP
PHP-интерпретатор может быть установлен как в качестве внешнего CGI- скрипта, так и в качестве модуля Web-сервера Apache. Установка PHP в качестве модуля не только увеличивает производительность сервера, но и значительно повышает безопасность, так как в этом случае PHP работает в окружении сервера Apache (обычно наделенного очень малыми привилегиями), а не в системном окружении сервера. Главная особенность PHP заключается в том, что это серверный язык программирования, способный выполнять команды операционной системы и имеющий доступ к файловой
системе сервера. Эти особенности несут потенциальную угрозу безопасности системы.
Необходимо использовать последние версии PHP, так как в нем, как в любом сложном программном обеспечении, постоянно находят и исправляют ошибки.
При установке PHP в качестве двоичного модуля его обычно размещают в каталог cgi-bin, откуда он вызывается как обычный CG1-обработчик. При такой установке PHP существует два типа атак:
口 доступ к системным файлам;
口 доступ к любому документу на сервере.
Доступ к системным файлам можно получить, запросив страницу http:// my.host/cgi-bin/php?/etc/passwd. В результате этого PHP проанализирует и выведет содержимое файла /etc/passwd в браузер атакующего.
〔 Замечание )
Дело в том, что Web-сервер передает этот файл PHP в качестве первого аргумента, что вынуждает PHP принимать этот файл за сценарий, который должен быть выполнен. А поскольку никакого PHP-кода в этом файле не содержится, информация из него выводится прямо в браузер атакующего.
Доступ к защищенным документам можно получить, запросив, к примеру, страницу http://my.host/cgi-bin/php/protected/file.html. В этом случае обходятся механизмы защиты Web-сервера по причине того, что PHP не запрашивает файл у сервера, а открывает его непосредственно в файловой системе.
Существует несколько способов предотвращения подобного вида атак.
Первый СПОСОб СОСТОИТ В КОМПИЛЯЦИИ PHP С ОПЦИеЙ enable-force-cgi- rediract, в результате чего PHP не будет принимать запросы непосредственно к с и нтакс и ческому анализатору,
( Замечание )
Опция enable-force-cgi-redirect тестировалась только на Web-серверб Apache, и ее работа с другими серверами не гарантируется, поскольку работа этой опции основывается на установке Apache нестандартной переменной окружения CGI redirect_STATUS для перенаправленных запросов.
Другой способ повышения уровня безопасности состоит в ограничении прав доступа PHP с помощью директив doc_root и user_dir конфигурационного файла php.ini. Директива doc_root позволяет установить корневую директорию для РНР-скриптов.
I )
Корневую директорию можно установить также через переменную окружения PHP_DOCUMENT_ROOT.
Если эта переменная установлена, PHP всегда будет конструировать имя открываемого файла с применением значения директивы doc roct н пути, извлеченного из URL.
Другая используемая в этом случае директива — user_dir, задающая путь к папке домашних каталогов (обычно это \home). В этом случае HTML- файлы и PHP-скрипты можно хранить в домашнем каталоге пользователей, а при запросе к серверу после адреса указывать знак тильды (、) и имя пользователя. Например, при обращении к домашней страничке пользователя user Web-сервера http://www.my.host/ следует обращаться по адресу http://www.my.host/^user/.
Следует помнить, что если директива user_dir не установлена, то при обращении по адресу http://www.niy.liost/*user/ посетитель будет перенаправлен в подкаталог ~и$е『 каталога, задаваемого директивой doc_root.
Установка user_dir происходит независимо от установки doc_root, поэтому можно контрол ировать доступ к директории Document Root и домашнего каталога пользователей независимо друг от друга.
Другим надежным способом является помещение исполняемого модуля PHP вне корневого Web-каталога, к примеру, в папке /usr/local/bin/php. При этом файлы, содержащие PHP-скрипты, следует пометить как исполняемые, а в начале строки поместить строку, сообщающую системе, где расположен РН Р-интерпретатор:
#!/usr/local/bin/php
Таким образом, скрипт будет рассматриваться так же, как и любое другое СОЬприложение, написанное на Perl или любом другом скриптовом языке, который использует # ! в начале файла для указания интерпретатора.
Чтобы PHP корректно обрабатывал информацию ратн_ 工 nfo и path一translated при такой установке, PHP должен быть скомпилирован с опцией конфигурации -enable-discard-path.
Когда PHP используется как модуль Apache, он наследует права пользователя, с которыми был запущен Web-сервер (обычно это пользователь nobody'). В этом случае не стоит поддаваться соблазну и наделять пользователя * nobody' какими бы то ни было допол н ител ьными правами (например, доступа к базе данных) сверх того, что он имеет.
Вполне возможна ситуация, когда Web-робот неверными запросами страницы администратора базы данных уничтожит все данные или даже структуру БД. Избежать такой ситуации можно при помощи авторизации Apache или
разработав собственную модель доступа, используя LDAP, файлы htaccess или любые другие технологии, внедряя соответствующий код в скрипты.
Можно также использовать настройки безопасности, при которых PHP имеет минимальные привилегии, например, лишен возможности записи в пользовательские директории.
[ Замечание )
Распространенной ошибкой является запуск сервера Apache с правами суперпользователя или любое другое расширение полномочий Web-сервера.
Для ограничения дерева доступных директорий, в которых может работать PHP, следует использовать директиву open_basedir, которая задает корневой каталог для PHP.
Безопасная настройка PHP
В файле конфигурации PHP содержатся различные параметры, при помо- ши которых можно сушественно повысить защищенность создаваемых скриптов.
Параметр display_errors
Этот параметр отвечает за вывод ошибок в браузер и должен быть установлен в значение false, поскольку при возникновении ошибок PHP предоставляет информацию о путях и именах переменных, что может дать атакующему нужную информацию. В директиве конфигурации error log не' обходимо установить путь к файлу, в который будет производиться запись о произошедших ошибках.
Параметр error_reporting
Данная директива позволяет установить виды ошибок, о которых выводятся сообщения. Подробно этот параметр описан в главе /. При отладке лучше всего установить значение этого параметра в e all, а в реальной работе лучше оставить только сообщения об ошибках, а вывод предупреждений и уведомлений отключить.
Параметр variables_order
При помоши этого параметра устанавливается порядок, в котором PHP регистрирует переменные окружения (Environment) и переменные post, get, cookie и server. По умолчанию значение этого параметра равно ”epgcs”, что означает регистрацию переменных в указанном порядке. Переменные,
зарегистрированные позже, переопределяют переменные, установленные ранее. То есть переменные окружения заменяются GET-переменными, которые, в свою очередь, заменяются на POST-переменные, н т. д” При помощи этого параметра можно отключить регистрацию переменных из определенного источника, к примеру, от переменных окружения, для чего параметр variabies_order нужно установить в значение "PGCS".
Параметр open_basedir
Этот параметр задает каталоги» доступные для функции fopen. К примеру, если необходимо открывать файлы только нз директории /home/php/, то это значение нужно установить для параметра open__basedir. Для того чтобы задать несколько каталогов, их нужно разделить точкой с запятой (в Windows) и двоеточием (в UNIX). Значение, установленное для этого параметра по умолчанию, позволяет открывать любой файл, что является нежелательным при работе PHP на реальном сервере.
Параметр disab!e_functions
При помощи этой директивы задается список функций, выполнение которых запрещено. По умолчанию разрешено выполнение любых функций. В число функций, запрещение которых желательно, входят следующие функции:
ехес (выполняет указанную команду);
口 passthru (выполняет указанную команду и возвращает все результаты в браузер);
口 system (то же, что и passthru, только не обрабатывает двоичные данные);
О sheii exec (эквивалент оператора обратного штриха, который PHP выполняет в качестве команды оболочки);
П рореп (выполняет указанную команду);
phpinfo (позволяет узнать настройки н расположение сервера Apache и интерпретатора PHP).
Это вовсе не значит, что использование перечисленных здесь функций следует запретить. Продуманный с точки зрения безопасности код не позволит злоумышленнику воспользоваться гибкостью, предоставляемой данными функциями.
Параметр allow_url_fopen
При помощи этого параметра можно запретить открытие удаленных файлов, расположенных на других серверах. Если отсутствует жесткая необходимость в таком обращении, то значение этого параметра следует установить в off.
В ином случае атакующий может выполнить произвольный код, вызвав какой-либо из ваших скриптов со строкой запроса, к примеру, ?page=http: //www. myserver. com/haker. txt. Это очень частый ВИД атак, особенно для тех случаев, когда имена страниц сайта передаются через URL путем создания ссылок типа script.php?page=index. php* Поэтому крайне желательно также не передавать имена страниц через URL, а присвоить номер каждой странице и создать массив с номерами файлов страниц, где индексами массива являются номера страниц.
Параметр register_globals
Данная директива подробно обсуждалась в главе 7. Если эта директива установлена в значение on, то EGPCS-переменные регистрируются как глобальные, с чем связано несколько угроз. При такой установке нет надобности в проверке, откуда поступила переменная (от get, post или cookie), поэтому значения переменных легко могут оказаться поддельными.
Обычно приводят следующий классический пример. Из HTML-формы index.php в обработчик handler.php передаются два параметра user и password ——нмя и пароль, по которым посетителю предоставляется доступ к какой-либо странице, например, управлению его виртуальным счетом (листинг 5.19).
:Листинг 5.19. Файл hadler.php
<?php
// Файл handler.php, принимающий от формы index,php два параметра
// $изег и $password для имени пользователя и пароля соответственно if ($user == "userl** && $ pas sword == "passwordl**)
{
$access = 1;
}
if(S&ccess)
{
// Доступ к вирту аль ному счету
}
?>
В случае установки директивы register_giobais в файле php.ini в значение "on1 логика работы скрипта может быть нарушена. Так, если в строке запроса обратится по адресу handler^php?access= 1, авторизация произойдет, минуя
HTML-форму и части кода, ответственного за авторизацию. Разумеется, в данном случае защитить Web-приложение очень просто, но в больших Web- приложениях могут быть неочевидные ситуации. На фоне того, что злоумышленник может автоматически обращаться и анализировать ответы, или коды могут быть выложены лля всеобщего доступа, вероятность взлома возрастает значительно, так как отследить все такие ситуации сложно.
Для работы со строкой запроса, данными, возвращаемыми методом post, сессиями, cookie в PHP были введены так называемые суперглобальмые массивы — предопределенные массивы, где размешаются внешние переменные, которыми и необходимо пользоваться.
( Замечание )
Возможна безопасная работа и при включенной директиве register_globals, однако в этом случае необходима явная инициализация всех переменных.
Таким образом, следует помнить о том, что необходимо либо отключить директиву registar_globals, либо обеспечить явную инициализацию всех переменных. Это делает невозможным 95% всех возможных атак.
Безопасная установка MySQL
После установки сервера базы данных MySQL система имеет две базы данных:
口 mysqi — база данных, содержащая таблицы разрешений;
□ test — база данных, обычно используемая для тестирования.
По умолчанию привилегии в таблице разрешений базы данных mysqi устанавливаются таким образом, что подключиться к серверу может любой пользователь, при этом в качестве пароля используется пустая строка.
Изначально в системе заводятся две учетные записи: root — для администратора (имеющего привилегии суперпользователя) и учетная запись анонимного пользователя. Обе они имеют пустой пароль.
[ Замечание )
Вместо имени анонимного пользователя в СУБД MySQL используется пустая строка.
Под операционной системой Windows привилегии между этими двумя учетными записями распределяются согласно табл. 5.1.
Таблица 5.7. Привилегии пользователя под Windows
Хост
Пользователь
Пароль
Привилегии суперпользователя
localhost
root
Все
%
root
Все
localhost
Все
%
Отсутствуют
Как видно из табл. 5.1, изначально и суперпользователю (root), и анонимному пользователю назначены пустые пароли и доступ к системе как с локальной машины, так и с любого узла сети.
; Замечание )
Символ % в СУБД MySQL соответствует любому хосту сети. Это означает, что пользователь, для которого указан данный символ, может работать с любой машины в сети с использованием пароля, указанного в столбце "Пароль".
: Замечание }
Под операционной системой UNIX не используется предустановленная таблица разрешений, она создается после инсталляции MySQL при помощи сценария mysql_install_db.
При использовании СУБД MySQL на локальной машине выставленных по умолчанию разрешений вполне достаточно, однако при работе системы на реальном сервере необходимо защитить учетные записи паролем и ограничить доступ к базе данных только с проверенных сетевых узлов*
[Замечание )
Если СУБД MySQL установлена на той же самой машине, что и сервер Apache, имеет смысл оставить доступ только с узла localhost.
Существует несколько способов выставить пароль для учетной записи. Первый из них заключается в использовании уталнтн MySQLAdniin, входящей в состав дистрибутива MySQL (листинг 5.20).
^^^j^H^yj^^^^^^^^^j^^aty^^Mi^^ ^.jjv^ij^^jy *^^«« 4|**|ум,; • • “ :5君卄,:]…к,#??//}”**/" у .*;,,岭:工乜* "•••,.,”《*”、?♦饗;'
i Листинг 5.20. Назначение пароля пользователю root' J
年 mysqladmin -h localhost -u root password nnew_password"
# mysqladmin -h www. softtime. ru -u root password nnew_passwordT*
Приведенные выше две команды устанавливают для учетной записи root новый пароль» заданный в строке new__password.
Второй способ связан с возможностями диалекта языка запросов SQL, реализованного в СУБД MySQL. В нем для установки паролей используется оператор set password (листинг 5.21).
也装出 ^ЬУВ把A7;YA4?力У W ::.",:*':"..CfkH^JWIJMMMMUiilMiMiiillMMiMMMMAr^tflYjyjaw-W.Hm.WkAyHLy/Wy.^JV^^Ift1^
:Листинг 5.21. Установка пароля с помощью оператора set password
mysql> SET PASSWORD FOR 'root'@1localhost' = PASSWORD(1new_password *);
mysql> SET PASSWORD FOR 'root1@'电'=PASSWORD(1new__password');
Так как пароли и разрешения — это просто записи в таблицы user базы данных mysqi, то существует еще один способ смены пароля пользователя, основанный на применении оператора update (листинг 5.22).
^^^^^^^^^^^^^^^^^^^^燃置 и^*х網 ^^^и^ц^^ц^уу1 密 m^y^yfl^M^^^^^^^^^^^i^^i^iUi^yg^^^^y^^^^A%罡 j^i^yj*%^^y齡£^^^^^^
Листинг 5.22. Установка пароля с помощью оператора update
mysql> UPDATE user SET Password = PASSWORD(*new_password1) mysql> WHERE User='root *;
mysql> FLUSH PRIVILEGES;
Для того чтобы не перегружать сервер явным образом, в листинге 5.22 используется оператор flush privileges.
Для управления пользователями базы данных применяются два оператора SQL:
grant —— оператор, создающий пользователя MySQL и позволяющий настроить его привилегии;
revoke ——оператор, позволяющий удалить привилегии пользователя.
Создание нового пользователя в системе осуществляется с помощью оператора grant, который имеет следующий синтаксис:
GRANT privileges [ (columns) ) f [privileges [ (columns)]...]
ON table
TO user [IDENTIFIED BY [PASSWORD] password」;
Если учетно访 записи не существует, она создается, если же учетная запись, определяемая параметром user, уже имеется в системе — происходит ее модификация.
Оператор позволяет задать привилегии, определяемые элементом privileges для столбцов (columns) таблицы table, которые указывать необязательно. Элемент privileges является оператором (или списком операторов), разрешенным к использованию. В качестве таких операторов могут
выступать insert, select, update, alter и up. Если в качестве параметра privileges используется ключевое слово all, привилегии коснутся всех допустимых операторов.
( Замечание )
За списком всех допустимых операторов следует обращаться к документации MySQL.
[ Замечание )
Вместо имени таблицы можно использовать групповой символ * ——тогда изменения коснутся всех таблиц в базе данных. При этом можно уточнить имя базы данных, используя формат dbname. *. Если параметр table равен *. *, привилегии будут применяться ко всем таблицам базы данных.
Параметр user определяет учетную запись, которой присваиваются привилегии. Значение этого параметра состоит из имени пользователя и имени узла в формате 1 имя_пользователя1 @г узел *. Необязательный параметр password задает пароль для учетной записи.
Для удаления привилегий пользователей применяется оператор revoke, имеющий следующий синтаксис:
REVOKE privileges [ (columns) ], [privileges [ (columns)],..]
ON table
TO user;
Оператор revoke применяется для отмены привилегий, но не для удаления пользователей. В таблице user базы данных mysqi все равно остается запись для пользователя, даже если все привилегии для него отменены. Для полного удаления пользователя необходимо явно удалить его из таблицы user при помощи SQL-оператора delete.
ЧАСТЬ II
Разработка системыАДМИНИСТРИРОВАНИЯ САЙТА
Рис* 6.1. Современный сайт похож на айсберг: его подводная часть (система администрирования) значительно превосходит то, что видно пользователямРис* 6.1. Современный сайт похож на айсберг: его подводная часть (система администрирования) значительно превосходит то, что видно пользователям
Рис* 6.1. Современный сайт похож на айсберг: его подводная часть (система администрирования) значительно превосходит то, что видно пользователям
Рис* 6.1. Современный сайт похож на айсберг: его подводная часть (система администрирования) значительно превосходит то, что видно пользователям
Глава 6
Проектирование
В настоящее время любой корпоративный сайт можно представить в виде айсберга, надводная часть которого (содержимое сайта) видна пользователям, а огромная подводная часть (система администрирования)——скрыта от постороннего взора. Схематично это изображено на рис. 6丄
Современная хорошо спроектированная система администрирования значительно превосходит как по объему, так и по сложности разработки видимую часть ресурса: она позволяет редактировать информацию на сайте, выполнять различные задачи по администрированию, собирать и анализировать разнообразную статистическую информацию о посетителях сайта и т. д.
Для того чтобы обеспечить не только удобную навигацию администраторам н редакторам ресурса, но и простоту сопровождения ресурса, команде раз-
работников с самого начала необходимо спроектировать структуру расположения файлов и папок в директории DoocumentRoot.
Структура н организация разрабатываемого в этой книге сайта будет рассматриваться на примере корпоративного сайта, включающего следующие Web-приложения.
О Динамически формируемые статьи, снабженные системой администрирования, позволяющей создавать, удалять, осуществлять правку статей редактору, которому для этого необязательно знать язык разметки HTML.
口 Многоуровневый каталог продукции, снабженный системой администрирования, позволяющей изменять структуру каталога, добавлять, удалять и группировать разделы каталога, редактировать названия разделов и информацию 。продукции.
Система полнотекстового поиска по сайту, базирующаяся на Web-приложении "динa^fичecки формируемые статьи'1.
口 Динамически формируемый блок "Контакты", с системой администрирования, которая позволяет добавлять, удалять и редактировать контактную информацию.
口 Блок "Голосование** для проведения различных опросов среди посетителей сайта, позволяющий им принимать участие в опросе, просматривать результаты текущего и архивных опросов. Управление системой голосования также осуществляется через страницу администрирования.
П Блок 'Тостевая книгаf,, в котором посетители могут оставлять свои замечания, предложения и пожелания. На странице администрирования администратор может отвечать на сообщения посетителей, осушеств- лять модерирование путем редактирования, скрытия или удаления сообщений,
Блок "Фотогалерея*1, позволяющий посетителям просматривать фотографии, размещаемые администратором сайта при помощи страницы администрирования данного Web-приложения.
口 Система отправки сообщений с сайта, позволяющая посетителям отправлять сообщение на электронный адрес компании с предложениями и пожеланиями. В отличие от гостевой книги, сообщения в этом случае недоступны для публичного просмотра. В системе администрирования данного Web'npwjioxeHMH следует предусмотреть возможность не только смены почтового адреса, но и задания произвольного количества Web- адресов, на которые будет выслан дубликат письма от посетителя.
口 Web-приложение мМногофункциональный счетчик посетителей'*» позволяющее производить сбор и анализ статистики по посетителям сайта.
Подробно вопросы разработки перечисленных Web-приложений будут рассмотрены в последующих главах. Здесь же рассмотрим структуру директории DocumentRoot, содержащую все вышеперечисленные Web-приложения, Организацию папок для Web-приложений можно осуществлять двумя способами. Первый из них приведен в листинге 6.1 и заключается в том, что под каждое Web-пршюжение создается отдельная директория, включающая в свой состав поддиректорию admin страницы администрирования.
/ЯУ^НЧ?翡工:AV.V~^tS^W-::;4fM星観^5^*牡3七丁—," •.'.'>-.*.' "itl?*X;g 請 I;抬Д:毋^达抬:至繆■,::*? 3tM:j■小二:銀《:,&*#挚**Й电 1 空読竺Л*":::11*,,ул* t* jv,%f '■,4***Ч1*уй\*^й. V-*'!?,‘ i Листинг 6.1. Организация структуры корневой директории
■ ■H**ktti ■■>■■># ■ ■ЛТ* **п /. * » jj* i<Ar>»+*l * Х^Л 9^94 fi Л f^'^f&Vi ・;.*•:.♦, F;€ 工》^ ■^"Zf ■ ■ м й •・1 к ь Л^* •"*♦,. ;'■■ *%■*.*= ^ii".. в^ ^ * mV*V «V1V &1 л e'J^^^it^f^nfifti^^^f^ i ^V^V*** ■ Ч^ч^* 4 ^» ■ $*.*'『•,■****••;・';、、■ .Л^Л ; , i^?^^^「扌:
/ Do oumentRoot
У articles /admin /catalog /admin /contacts /admin /counter /admin
/mail /admin
/poll /admin
/guestbook /admin
/photos /admin
/utils
井 Динамически формируемые статьи
Адаинистрирование статей
Каталог продукции
# Администрирование каталога продукции
# Блок пКонтакты"
# Администрирование блока "Контакты"
Многофункдаональный счетчик посетителей
Система админи стриро в а ни я счетчика посетителей
Система отправки писем с сайта
Администрирование системы отправки писем с сайта
Блок мГолосоваииен
Администрирование блока пГолосованиеп
Гостевая книга
Система адаинистриров ания гостевой книги
Фотогалерея
Система администрирования фотогалереи
Общие для всех Web-приложений файлы
В корневой папке директорий, также
Document Root, помимо перечисленных в листинге 6.1 должны находиться файлы, объединяющие все Web-
приложения в единое целое н предоставляющие доступ посетителям сайта к информации, а администраторам —— к администрированию Web-приложений, т. е. к файлу index.php н сопутствующим файлам.
Конфигурационные файлы Web-приложений, файлы доступа к базе данных и файлы паролей рекомендуется размещать на один уровень выше корневой папки DocumentRoot* Это повышает защиту сайта от взлома, так как посетители не могут подняться выше директории Document Root и получить доступ к защищаемым файлам.
9 Зак. 1181
: Замечанье )
Файлы, общие для всех Web-приложений, например, верхняя часть дизайна сайта страницы (top.php) и нижняя часть дизайна сайта (bottom.php), удобно размещать в отдельной папке. В листинге 6.1 такая папка обозначена как utils.
Мьтернативным вариантом организации структуры сайта может служить схема, представленная в листинге 6*2. Для частей, посвященных администрированию сайта, выделяется отдельная директория. При таком способе организации директорий сайта проще осуществлять защиту информации: защищенной оказывается только папка admin. Все вложенные папки автоматически попадают под эту защиту.
4溜уи "XV』Л! ••■■.«:
/DocumentRoot /articles /catalog /contacts /counter /mail /poll /guestbook /photos /utils /admin/articles /catalog /contacts /counter /mail /poll /guestbook /photos /utils/DocumentRoot /articles /catalog /contacts /counter /mail /poll /guestbook /photos /utils /admin/articles /catalog /contacts /counter /mail /poll /guestbook /photos /utils
/DocumentRoot /articles /catalog /contacts /counter /mail /poll /guestbook /photos /utils /admin
/articles /catalog /contacts /counter /mail /poll /guestbook /photos /utils
/DocumentRoot /articles /catalog /contacts /counter /mail /poll /guestbook /photos /utils /admin
/articles /catalog /contacts /counter /mail /poll /guestbook /photos /utils
Динамические статьи
Каталог продукции
Блок ** Кон такты**
Сбор и анализ статистики по посетителям
Отправка писем с сайта
Блок ° Голосование11
Гостевая книга
Фотогалерея
Общие для всех ИеЬ^приложеиий файлы
Администрирование статей
Адаинистрирование каталога продукции
Админмстрирование блока иКонтакты1'
Система администрирования счетчика
Администрирование системы отправки писем с сайта
Администрирование блока "Голосование"
井 Система администрирования гостевой книги
Система администрирования фотогалереи
Общие для страниц администрирования файлы
Такая структура сайта не только более безопасна, но н позволяет организовать общие для системы адми н и стри рован и я файлы, разместив их в директории /admin/utils/. В папке admin обычно находится файл index.php, в котором размещено меню системы администрирования. Внешний вид этой системы приведен на рис 62
Рис. 6.2. Главная страница системы администрирования корпоративного сайта
Код, соответствующий странице, изображенной на рис. 6.2, приведен в листинге 6.3.
Листинг 6.3. Файл index.£•.% .'!W^X!?N ♦;”.:.:;—% ;君?S :м;:.;;……Листинг 6.3. Файл index.£•.% .'!W^X!?N ♦;”.:.:;—% ;君?S :м;:.;;……“?.^yyj^^^^^^/: .*》у",.?.**至 4j/f: 竺ЗУ,,\:““A,W"Y"!*,W”y:・"““,:
Листинг 6.3. Файл index.
£•.% .'!W^X!?N ♦;”.:.:;—% ;君?S :м;:.;;……
Листинг 6.3. Файл index.
£•.% .'!W^X!?N ♦;”.:.:;—% ;君?S :м;:.;;……
<?php
$title = 1 Администрирование сайта1;
$helppage = 1Здесь осуществляется администрирование сайта. Выбрав соответствующий раздел, можно добавить новость, внести изменения в каталог продукции, осуществлять модерирование гостевой книги и многое другое.1;
include "utils/topadmin.phpH;
?>
<table>
<tr><tdxp>
<a href='poll/index•php'>Голосование</а>
</p></tdx/tr>
<tr><td><p>
<а href= г guestbook/index・ php1>Гостевая книга</а>
</px/td></tr>
<tr><td><p>
<а liref=1 articles/index.php*>Динамические статьи</а>
</px/td></tr>
<tr><td><p>
<a href='catalog/admin/roain•php'>Каталог продукции</а> </px/td></tr> <trxtd><p>
<a href=1 guestbook/index. php ■ Жонтакты< /a>
</p></td></tr>
<tr><td><p>
<a href= * mail/index.php1>Отправка писем</а>
</px/tdx/tr>
<tr><td><p>
<a href=1counter/index.php * >Система сбора и анализа статистики</а> </px/td></tr> <trxtd><p>
<а href=1photos/index.php'>Фотогалерея<7а>
</p></td></tr>
</table>
<?php
include "utils/bottomadmin.php";
?>
Файл index.php включает файл, содержащий верхнюю часть д^rзaйнa страницы (topadmin.php), и файл с нижней частью дизайна страницы (bottomadmin.php)* Эти файлы расположены в общей для системы администрирования папке utils, что позволяет использовать общее для всех страниц администрирование оформления и систему ссылок.
Глава 7
Система
администрирования контента
Определим одно из важнейших качеств хороших сайтов. Это будет не дизайн, хотя н это важно, не наличие “крутых” программных блоков и даже не скорость загрузки по модему. Самое важное свойство сайта с точки зрения бизнеса ——актуальность: насколько правдива информация, насколько она актуальна на текущий момент, насколько точно она соответствует целям и задачам бизнеса.
Поэтому создание сайта — это только начало его жизненного цикла. Хороший сайт ——это не каменный неподвижный монумент, это живой организм, быстро реагирующий на любые изменения внешней среды. Любая информация на сайте должна меняться настолько быстро, насколько этого требуют реалии бизнеса.
Как же этого добиться? Лучшим выходом, с точки зрения оперативности, для компаний-владельцев сайтов будет наличие штатного Web-мастера. Только он сможет сделать все быстро и оперативно. Однако этот вариант далеко не самый лучший с точки зрения экономии, так как содержание Web-мастера только для поддержки сайта нерентабельно, если это. конечно, не очень крупный бизнес-портал или интернет-магазин.
Второй вариант ——при первой необходимости обращаться к сторонним Web-мастерам. Тоже не лучший вариант. Во-первых, требует составления подробного технического задания на вносимые изменения, а значит, требуется дополнительное время. Во'вторых, за услуги сторонних разработчиков нужно платить. Плата, как правило, берется за каждое обращение.
Третий вариант — договор на поддержку сайта. Этот вариант имеет свои плюсы и минусы, но изменения на сайте могут не потребоваться в течение долгого периода, в то время как деньги нужно платить постоянно.
Каков же выход? Организация системы администрирования содержимого сайта, позволяющая работать с ней людям, не знакомым ни с языком разметки гипертекста, ни с языком PHP. В этом случае можно самостоятельно
оперативно вносить изменения на сайт, либо возложить эти обязанности на секретаря.
На рис. 7.1 схематично изображены различные способы поддержки актуальности сайта.
Рассмотрим выгоды от использования систем администрирования контента, которые она дает другой стороне —— профессионалам сайтостроеиия — Web- мастерам. При обслуживании десятка сайтов вносить изменения в каждый из них в соответствии с дизайном этого сайта является непосильной задачей. Не говоря уже о том, что нужно помнить нюансы оформления каждого из них.
Система администрирования контента или проще CMS fContent Management Systems) позволяет решить эту проблему. Достаточно настроить ее один раз в соответствии с дизайном, и все изменения, вносимые в дальнейшем на сайт» будут автоматически следовать оформлению сайта. При этом редактирование HTML-кода потребуется только при наличии действительно сложных дизайнерских задач.
Таким образом, разработка системы администрирования контента сайта (CMS) принесет пользу всем:
разработчик повышает качество и скорость разработки своих приложений, а следовательно, и их финансовую значимость, Кроме того, предо-
ставляя заказчику возможность самому вносить изменения на сайт, он, тем самым, снимает эти задачи с себя, так как зачастую поддержка сайта дело не очень доходное, но довольно хлопотное;
заказчик получает удобный инструмент оперативного редактирования своего сайта и независимость от разработчика.
Описание системы CMS
В данном разделе будет описана система администрирования контента сайта (CMS), определены терминология и основные требования, а также описан интерфейс системы управления контентом (CMS). Разработка CMS, проектирование базы данных для нее и ключевые блоки кода системы будут описаны в следующем разделе книги.
( Замечание )
На прилагаемом к книге компакт-диске можно найти готовые к использованию коды приводимой в данной главе системы CMS, находящейся в эксплуатации на реально-действующих сайтах. Загрузить последнюю версию системы можно по адресу http://www.softtime.ru/info/downloads,php. Помощь по установке, настройке, а также модификации под свои задачи вы можете получить на форуме http://www.softtirne.ru/forum/.
Требования к системе администрирования контента (CMS)
Основные требования, предъявляемые к CMS, заключаются в следующем. Система должна позволять:
口 изменять и корректировать структуру сайта;
□ редактировать существующие страницы и создавать новые;
口 редактировать информационное наполнение всех страниц сайта.
Согласно этим требованиям, все страницы сайта должны создаваться средствами CMS. Исключение могут составлять дизайно-завимые блоки, например, главное меню сайта, ссылка на копирайт внизу страницы, изменения в которых могут сильно повлиять на дизайн. Такие блоки, как правило, мало подвержены изменениям и создаются в виде статичных HTML-блоков.
Терминология
Каждую ссылку с главной страницы сайта или нз главного меню можно представить в виде раздела сайта, содержащего другие страницы. Именно эти ссылки формируют структуру сайта. Например, главное меню сайта со-
держит разделы: О компании. Продукция. Список публикаций и т. д. Каждый подобный раздел может содержать как одну страницу, так и сразу несколько, например, раздел О компании может содержать страницы: Характеристика компании, Руководство, Контактная информация.
Структура сайта
Рассмотрим сайт, имеющий следующую структуру:
О О компании:
Характеристика компании
Руководство
Контактная информация
口 Продукция
□ Список публикаций:
Публикации в прессе
Публикации в Интернете
Зарубежные публикации
С точки зрения CMS, каждую информационную страницу будем именовать статьей. А раздел сайта будем называть разделом статей.
Таким образом, сайт будет представлять собой группу разделов, а каждый раздел содержит в себе одну или несколько статей.
Каждая статья содержит параграфы, которые могут состоять из текста, изображений, ссылок, таблиц и т. п.
С помощью CMS можно создавать и редактировать:
разделы статей, предназначенные для объединения статей в группы;
статьи — информационные страницы сайта;
параграфы ——составляющие элементы статьи.
Структура и элементы статьи
Если попытаться разбить информационное наполнение страниц на логические элементы, то каждая страница сайта может содержать:
口 заголовки;
口 абзацы (блоки текста);
口 изображения;
口 ссылки;
口 таблицы.
С точки зрения CMS, каждая статья содержит параграфы, которыми могут быть:
заголовки;
О блоки текста вместе с изображениями; изображения часто служат для иллюстрации текста и в то же время изображения обычно имеют подпись, поэтому блоки текста целесообразно объединить с изображениями;
口 ссылки, в том числе на другие статьи из CMS;
口 таблицы.
工 Примечание 二
В терминологии CMS все эти элементы называются параграфами, так как каждый из них представляет собой отдельный блок страницы. Список типов параграфов далеко не полон. Развивая CMSr можно добавлять новые типы параграфов. выделяя их из структуры страниц по каким-либо критериям.
Параграфы — это структурные элементы статей. Они не обладают оформлением и дизайном. Оформление присваивается параграфам в виде стилей. Каждый параграф в зависимости от его типа может иметь несколько стилей, один из которых должен быть установлен ему в системе администрирования.
Например, "Заголовок'1 может иметь стили:
Название страницы
口 Обычный заголовок
Важный блок страницы
口 Заголовок примечания
Абзацу могут быть присвоены следующие стили:
П Обычный текст
Текст с отступом
口 Подпись к картинке
口 Примечание
Стили содержат не только оформление, но иногда и логику поведения элементов, например, у элемента ,фСсылка" могут быть стили:
Открыть ссылку в новом окне
Открыть ссылку в текущем окне
Открыть ссылку во всплывающем окне
Для открытия во всплывающем окне используется JavaScript. Этот стиль рекомендуется использовать, если ссылка открывает изображение.
имечаниеимечание
имечание
имечание
Набор стилей для каждого элемента можно дополнять, развивая функциональность CMS.
Работа
с системой администрирования CMS
Система админис^мрования CMS позволяет выполнять следующие задачи.
О Управление разделами статей.
口 Управление метаданными статьи.
Метаданные для статьи включают в себя название, описание статьи (эти элементы выводятся на странице администрирования) м информацию для заголовка и метатегов страницы сайта: заголовок страницы (тег <Title>), описание страницы (метатег〈Description〉),ключевые слова (метатег <Keywords>).
Работа со статьей как со структурным элементом позволяет осуществлять над ней следующие действия:
создание;
редактирование (изменение метаданных статьи);
удаление;
скрытие н отображение.
Редактирование статей (работа с параграфами).
CMS должна обеспечивать следующие операции с параграфами:
создание;
редактирование;
удаление;
скрытие/отображение;
перемещение вверх/вниз.
Управление разделами статей
Раздел статей ——это группа статей, логически объединенных по какому-либо критерию. Создание нового раздела осуществляется через систему CMS. На главной странице администрироваиия, представленной на рис. 7*2, выводит
ся список существующих разделов.
Рис. 7.2. Главная страница администрирования CMS
Для создания нового раздела следует перейти по ссылке Добавить новый раздел. Открывшаяся страница создания нового раздела (рис. 7.3), будет содержать единственное текстовое поле Название. Заполнив его, следует нажать кнопку Добавить. После добавления будет осуществлен автоматический переход на главную страницу CMS.
Рис. 7.3* Добавление нового раздела
По адресу article/index.php доступен список разделов, список входящих в них статей и сами статьи, по сутн, здесь расположена своеобразная "карта" сайта.
Подключение разделов статей на сайте можно осуществлять в ручном режиме, расставляя ссылки на группы статей или отдельные статьи.
Может возникнуть вопрос: почему не сделать главное меню сайта автоматически генерируемым из списка разделов и этим полностью исключить ручной труд? Дело в том, что это можно сделать только для сайтов с очень простой структурой, когда навигация по сайту ограничена ссылками только из главного меню. В реальной ситуации дело значительно осложняется:
口 главное меню может быть оформлено таким образом, что сгенерировать его автоматически будет невозможно, либо трудозатраты на это будут выше, чем расстановка ссылок вручную. Например, главное меню разделено на 2 части, которые расположены в разных частях страницы. Либо оформление пунктов меню (цвет, пиктограмма) зависит от их названия;
□ не все разделы сайта можно перечислить в главном меню;
口 главного меню может попросту не быть на сайте.
Эти условия накладывают ограничения на автоматическую генерацию разделов статей и создание универсальной системы CMS. Для решения этой задачи пришлось бы вводить много новых сущностей в систему администрирования, например, "главное меню'*, "меню раздела”,"ссылка меню** и дополнительные параметры на эти сущности: "стиль”, *'оформление*\ "пиктограмма" и т. п. То есть средствами системы CMS нельзя решать вопросы дизайна: его алгоритмизация невозможна, поскольку он уникален для каждого сайта.
Поэтому зачастую необходимо ручное вмешательство для расстановки ссылок на разделы. Для того чтобы установить ссылку на разделы статей, нужно поставить вручную ссылку в HTML-код% указав в качестве URL путь к скрипту генерации статей и передав ему в качестве параметра идентификатор раздела статей.
URL должен быть следующего формата:
<а href=narticle/index.php?id_page=pH>Ссылка на раздел</а>
где article/index. php —— это путь к скрипту генерации статей» а передаваемый параметр р — это идентификатор раздела статей.
Ссылка на отдельную статью должна иметь вид:
<а href=Marticle/index.php?id_page=p&id_article=sart">CcbDiKa на статыо</а>
Здесь art является иденти фи катером статьи.
{ Замечание )
Более подробно работа с отдельными ссылками разобрана в разд. 7*3.
Таким образом, система администрирования контента сайта не зависит от его дизайна и Web-мастер может расставлять ссылки на основные разделы сайта так, как ему удобно, а не по строго заданной схеме.
Управление метаданными статьи
Для управления метаданными статей необходимо выбрать раздел статей. Для этого следует щелкнуть на его названии в списке разделов, в результате чего будет загружена С1раница, содержащая таблицу со статьями этого раздела, а также управляющая ссылка для добавления новых статей в выбранный раздел (рис. 7.4).
Рис. 7.4. Список статей в разделе
В разделе может содержаться неограниченное количество статей. При отображении их на сайте применяются следующие правила:
口 при наличии в разделе одной статьи при переходе к списку статей раздела статья сразу выводится в браузер;
口 если в разделе содержится больше одной статьи, то они выводятся на сайт в виде списка в алфавитном порядке.
У каждой статьи есть статус, отвечающий за ее отображение на сайте. Он может принимать одно из двух значений: статья скрыта от посетителей ресурса или доступна для просмотра ими.
( Замечание )
На отображение статей в режиме администрирования эти параметры не влияют*
Можно создать несколько статей в разделе, одна из которых (главная) будет доступна для просмотра, а все остальные скрыты. При этом из главной статьи можно расставить ссылки на скрытые статьи данного раздела. Тогда на сайте при просмотре раздела будет отображаться не список статей в разделе, а только одна главная статья. Таким образом, устанавливая в статьях ссылки друг на друга, можно создавать структуру сайта необходимой глубины.
Применяя эти правила и приемы, можно создавать развитую многуровневую структуру статей, которая может состоять из списков (разделов) статей, отдельных статей и ссылок статей друг на друга.
Редактирование существующих статей
Список статей в разделе представлен в виде таблицы, содержащей несколько колонок для метаданных и управляющие ссылки (см. рис. 7.4).
Первая колонка содержит название статьи. Переход по ссылкам в ней приводит к страницам редактирования информационного наполнения статей (редактирование параграфов).
Вторая колонка содержит краткое описание статьи. Оно выводится на сайте в том случае, если в разделе содержится несколько нескрытых статей, и они отображаются в виде списка.
Следующие три колонки содержат заголовок HTML-страницы со статьей (тег <Title>) И метатеги ДЛЯ нее (<Description> И <Keywords>).
Последняя колонка (Действие) содержит ссылки на операции редактирования:
口 Скрыть/Отобразить — управляет отображением статьи на сайте;
口 Редактировать — открывать страницу редактирования метаданных статьи (рис* 7.5): название, описание, тег <Titie>, метатег <Description>, метатег <Keywords>;
口 Удалить — удаляет статью.
Рис. 7.5. Страница создания статьи и редактирования метаданных статьи
Редактирование статьи
Для редактирования статьи необходимо выбрать раздел статей из списка разделов, а затем саму статью из списка статей, щелкнув на ее названии. Открывшаяся страница Web-редактора позволит редактировать содержимое статьи (рис. 7.6).
( Замечание )
Вновь созданная статья будет содержать только заголовок, совпадающий с названием статьи.
Создание параграфа
Перед тем как создать новый параграф, необходимо выбрать его тип с помощью переключателя из списка типов, расположенного в верхней и нижней части страницы (рис. 7.7).
Создание заголовка
Для создания нового заголовка следует выбрать при помощи переключателя тип параграфа заголовок и нажать кнопку Добавить. В результате будет открыта форма для создания заголовка, приведенная на рис. 7.8.
Рис. 7*6. Страница редактирования содержимого статьи
I 多 Редак г>фшш*№ г.ты* - *fKriMu№ kiteinH btfiarrt
№□1
姓叩亞―呼,WK gE
АЛ₽*Ч 1 蜜 lW:/MEfAimri/w№JM/*t.f9hp?id_«tlc1«—1 bidjMQC-1
■ j П«р«*ы; Сояпо» *
Редактирование гштънРедактирование гштънНед CTiHMicqiy巾|№Q w«ryt состоять w% ^И.ннлции 网聲叫jgwiK 0*вн« зячмвнта?, t4inin«m», тпкс1»рмг п«|рафлфы. ссыикИч тп@“*иы, сск.1Н№и нл cymrcniynaiNe ст«пгя Йхтлы,“,」, ИУ11«Ч5ТЬ ИЛобрлЖРЧНР >Ц> МС*НР fAfflltt >чм кХМИНИи ,, рвда»ТНро£аНИ1* ЗЛ#М*НТй,й|4П|1«ЦИ ппрйфьф Пия*г*^!*ичп 31№1>бмтпв в стрелу ре ltbt* 研 Ивытв^л*») “rmwo ййнлт^ «ноптми в&*р»/вн»п* — «г叩ом 匸№1收 i innfiqtf таМмцы
Редактирование гштън
Редактирование гштън
「мголон&к荷 текстовый параграф广 новая стапъя广 гэблиця HJ с$у*файл*「мголон&к荷 текстовый параграф广 новая стапъя广 гэблиця HJ с$у*файл*
「мголон&к
荷 текстовый параграф
广 новая стапъя
广 гэблиця HJ с$у*файл*
「мголон&к
荷 текстовый параграф
广 новая стапъя
广 гэблиця HJ с$у*файл*
Для Диб лишний н:загв 才《пинт птм #ыд«п»гй ну%мый тип « пеыент* * н**мит»
卜”》"野'Дф^яяить* ПО умалчани» алеменг дсЛввлд*тс = в ийнйц сШьи Нр в“ ии иет« ттв#^ ноахА ?лвменга пвбое "单е, «ыдяллс нршуto if7 1ицяга тфакчизиг*!лвм ^«ймкй левый стадЛеч! Ё 1ШМ,:“г,”。. HtHfu;i 1лемемг буд*г «-ставим хк*% ох лид >н оыдслрнмым
2啦苗9吁上]
Рис. 7.7. Выбор типа параграфа
Рис. 7.8. Страница создания запэловка
В данной форме следует заполнить поле Заголовок, выбрать стиль заголовка из выпадающего списка Стиль заголовка.
В системе CMS доступны следующие стили заголовков:
Обычный:
Название страницы;
Важный блок страницы;
Название листинга;
О Заголовок таблицы;
口 Заголовок примечания.
После заполнения полей формы и нажатия на кнопку Добавить в статью будет добавлен новый заголовок и произойдет автоматический переход к странице редактирования данной статьи.
Создание текстового блока (абзаца)
Для создания элемента статьи 'Текстовый блок** следует выбрать при помо щи переключателя тип параграфа ——текстовый параграф. После нажатия кнопки Добавить будет открыта страница создания нового абзаца (рис. 7.9).
В форме, приведенной на рис. 7.9, следует заполнить поле Содержание, выбрать стиль параграфа из выпадающего меню Стиль.
[Замечание 、
Обязательным к заполнению является только поле Содержание.
Рис. 7.9. Страница создания текстового параграфа
С помощью поля загрузки файлов Изображение можно добавить в параграф Пифическое изображение. Поле Увеличенное нзобр-е позволяет загрузить изображение большого формата. Это изображение не будет выводиться в параграф, его можно будет открыть по сгенерированной ссылке большое фото.
Удаление изображений
При редактировании параграфов с добавленными изображениями их можно удалить, поставив прочерк ("-") в соответствующих полях (Изображение и Увеличенное изобр-е).
После того как все необходимые поля заполнены, нажмите кнопку Добавить.
Доступные стили тестового параграфа представлены ниже:
Основной текст;
口 С отступом — текст с отступом от левого края;
Жирный;
Курсивный;
口 Ненумерованный список — текст выводится на страницу в виде ненумерованного списка. Элементы списка определяются по символу перевода строки;
Нумерованный список — текст выводится на страницу в виде нумерованного списка. Элементы списка определяются по символу перевода строки;
Код ——предназначен для вывода на страницу кода PHP. Оформляется моноширинным шрифтом и форматируется с помощью функции подсветки кода;
口 Примечание.
Создание ссылки
Для создания гиперссылки на ресурс сети выберите при помощи переключателя тип параграфа ссылка в списке типов н нажмите кнопку Добавить. В результате будет открыта страница, позволяющая создать ссылку (рис. 7.10).
Рис- 7,10. Страница создания ссылки
В форме, представленной на рис. 7.10, необходимо заполнить следующие поля:
Название — подсвеченный текст ссылки, который будет отображаться на странице;
口 Адрес (URL) — это URL, на который будет осуществлен переход при нажатии на ссылку.
Чтобы установить ссылку на существующую статью из CMS, необходимо записать URL в следующем формате:
art.php?id_article=id_article&id_page=Ld_page
где art.php — скрипт генерации статей, id_articie — идентификатор статьи, id_page ——идентификатор раздела статей;
П поле Выравнивание определяет положение ссылки на странице (выравнивание влево или вправо);
口 поле Открытие ссылки определяет способ открытия страницы по ссылке:
в текущем окне;
в новом окне;
во всплывающем окне;
口 поле Изображение позволяет загрузить изображение, которое будет открываться по ссылке.
После заполнения всех полей и нажатия на кнопку Добавить в статью будет добавлена новая ссылка, а снстеА【а CMS загрузит страницу редактирования текущей статьи.
Создание новой статьи и ссылки на нее
Для добавления ссылки на новую статью необходимо выбрать при помощи переключателя тип паршрафа — новая статья в списке типов и нажать кнопку Добавить. На открывшейся странице создания новой статьи (см. рис. 7.5) следует заполнить необходимые данные и нажать кнопку Добавить. Новая статья будет размещена в текущем разделе статей в скрытом режиме. То есть она не будет отображаться в списке статей на сайте (в режиме администрирования отображаются все статьи)* А ссылка на нее будет добавлена в редактируемую статью. Редактирование статьи и ссылки на нее осуществляется независимо* Удаление ссылки на статью не приводит к удалению самой статьи и наоборот — удаление статьи не удаляет ссылку на нее.
Создание таблицы из CSV-файла
Сформировав таблицу, например, MS Excel и сохранив ее в формате CSV, можно загрузить ее в статью в виде таблицы.
MS Excel позволяет сохранить в CSV-формате только текущий лист.
Для создания этого вида параграфа следует выбрать при помоши переключателя тип параграфа ——таблица нз csv-файла в списке типов и нажать кнопку Добавить. Откроется страница добавления таблицы (рис. 7.11).
Рис. 7.11, Страница добавления таблицы
Загрузите CSV-файл в поле файл. В поле разделитель занесите символ, который используется как разделитель в загруженном CSV-файле. Нажмите кнопку Добавить.
К Пр—меианмб ;
CSV-файл (Comma Separated Value)— это текстовый фа仿л, данные в котором отформатированы следующим образом: строки файла представляют собой строку таблицы, а столбцы таблицы разделены в файле заранее определен* ным символом-разделителем, например ";". Так как символ-разделитель может быть произвольным, то для правильного импорта CSV-файла символ-разделитель необходимо внести в соответствующее поле формы.
Работа с параграфами
Созданная статья в Web-редакторе представлена таблицей с набором параграфов и ссылками, позволяющими осуществлять редактирование, скры- тие/отображение, удаление и перемещение параграфов статьи (рнс. 7.12).
В первом столбце таблииы расположены переключатели для выбора позиции при добавлении нового параграфа. Если перед созданием нового параграфа выделить переключателем существующий параграф, то новь【員 будет вставлен в следующей за выделенным параграфом позиции. Таким образом, новые параграфы можно вставлять в произвольное место статьи.
〔 Замечание )
По умолчанию новые параграфы добавляются в конец статьи.
Изменять позицию уже созданных параграфов можно с помошью ссылок "вверх/вниз", оформленных в виде кнопок со стрелками и расположенных в третьем столбце таблицы. Нажатие на кнопку перемещает параграф на одну позицию вверх или вниз в зависимости от используемой кнопки.
Рис, 7.12. Статья в режиме администрирования
В последнем столбце таблицы расположены управляющие ссылки:
口 Исправить — открывает страницу редактирования параграфа;
口 Скрыть/Отобразить — позволяет временно скрыть параграф из текста статьи на сайте. В режиме администрирования он продолжает оставаться видимым;
口 Удалить — удаляет параграф.
Разработка системы
Для построения системы CMS необходимы три таблицы в базе данных MySQL:
artpages — таблица, предназначенная для хранения названий групп статей;
articles ——таблица, предназначенная для хранения названий статей н информации для ключевых слов и описания для HTML-страницы;
paragraphes — таблица, предназначенная для хранения отдельных параграфов статьи.
Таблица artpages включает 3 поля:
О id_page — уникальный индекс группы;
口 name — название труппы;
id_site ——зарезервированное поле на случай хранения в базе данных групп статей для нескольких сайтов.
Оператор create выполняющий создание данной таблицы, приведен в листинге 7.1.
CREATE TABLE artpage (
id_artpage MEDIUMINT NOT NULL AUTO_工NCREMENT,
name TINYTEXT NOT NULL,
id_site TINYINT,
PRIMARY KEY (id_artpage)
)TYPE=MyISAM;
Таблица articles включает 9 полей» назначение которых следующее:
口 id_article — уникальный индекс статьи;
паше — название статьи;
口 description — описание статьи;
口 htmi_titie — название для HTML-страницы. Это название размещается между тегами <titie> и </titie> и отображается в заголовке окна;
口 htmi__description — описание страницы для метатега <met& name=,,description,f content=, .,.*>;
口 html_keywords — ключевые слова ДЛЯ метатега vmeta name=Hkeywordsм content"*...*>;
口 pos — номер, определяющий позицию статьи относительно других статей. Сортировка по алфавиту или произвольный порядок следования статей не всегда допустимы, данное поле позволяет администратору задать произвольный порядок следования статей;
hide —— поле, определяющее доступность статьи для просмотра посетителями сайта. Если статья находится в процессе редакторской правки, она может быть скрыта от посетителей;
口 id_page ——индекс, определяющий принадлежность статьи к группе из таблицы art pages.
Оператор create, выполняюший создание таблицы articles, приведен в листинге 7.2.
^^^^Л^関^^闕:》燃.":竇,霹涉^^^5^^^^^^^ 抬 Н",¥ 2 * /','"^^^^y^^^^'^^',"f^^"" ^^,不.;"岁'',界霞?^鬱';:",^^****^:濁1”;号^^;|^^^**,",*岁:
:Листинг 7.2. Создание таблицы articles
s ¥,^Г^^^^^Г^^/^・;"3 ・・*,.:*;;';;'** *+:к,.* С;.;'*',;;;;,•♦.;;:;-;;».;и;'*'*:Л;.:*)*¥*Л^.. .,}•*?>■; ;K;v4;;、;.¥;:;";i^T^k :;*£&、.♦;臂:♦.**;: *;Jht*悠:科;::♦;*.**♦:】;::■*犯;:一/;:'41力-?;;・|?^||79;
CREATE TABLE articles (
id_ article 工NT NOT NULL AUTO_INCREMENT# name TINYTEXT NOT NULL,
description TINYTEXT NOT NULL, html^title TINYTEXT NOT NULL, html^description TINYTEXT NOT NULL, html keywords TINYTEXT NOT NULL, pos MEDIUMINT NOT NULL,
hide ENUM (' show — hide') DEFAULT ' show *, id_j1age MEDIUMINT NOT NULL, PRIMARY KEY (id_article), KEY idjpage (id_page}
)TYPE===MyISAM;
Таблица paragraphes включает 10 полей:
口 id_paragraph ——уникальный индекс параграфа;
П type ——тип параграфа: обычный абзац, заголовок, ссылка, таблица;
name — поле для текста параграфа;
url —— поле для гиперссылки;
口 picture_big — поле для хранения пути к увеличенному изображению, выводимому при щелчке по изображению;
picture small — поле для хранения пути к изображению, выводимому в параграфе;
place ——вспомогательное поле для определения позиции элемента, внешнего вида и т. д.;
О pos ——поле, определяющее порядок следования параграфов;
口 hide ——поле, определяющее доступность параграфа для просмотра посетителями сайта;
О id^article — индекс, определяющий принадлежность параграфа к статье. Оператор create, выполняющий создание таблицы paragraphes, приведен в листинге 7.3.
Е
#**»»^*',,1',* Л"Т■' j ■''■' S!''"'■'■■ ■■■■*- '"'VУ*:?…•:**;/ууУ.”,,"一.• u.-mmfjj#申”,十…,
Листинг 7.3. Создание таблицы paragraphes
;:*・;',*“;;>:■;""••***"",,.《♦,“+;■,■,*" + ■"*♦”■;“△•,***«■■<}*+*«*♦*“,
CREATE TABLE paragraphes ( id_paragraph INT(10} UNSIGNED NOT NULL ADTO_INCREMENT, type ENUM(1paragraph*,1 title *,1anchor1,'table')
NOT NULL DEFAULT paragraph1 r name TEXT NOT NULL, url TINYTEXT NOT NULL, picture_big TINYTEXT NOT NULL, picture_small TINYTEXT NOT NULL, place TINYINT(4) NOT NULL DEFAULT 'O', pos INT(ll) NOT NOLL DEFAULT '01, hide ЕЮ5 {' show1 J hide') NOT NULL DEFAULT ' show1, idarticle IRT{10) UNSIGNED NOT NULL DEFAULT 『。力 PRIMARY KEY (id_paragraph) r KEY id__article (id_article)
)TYPE=MyISAM;
Из листингов 7.2 н 73 видно, что поле hide, ответственное за скрытие н отображение статьи и параграфа, объявлено как enum и может принимать только два значения: *show* — статья или параграф доступны для просмотра н 'hide'——злемент скрыт, и доступен только администратору* С использованием перечисления enum также объявлено поле type в операторе create таблицы pa ragraphes (см. листинг 7.3). Поле type может принимать четыре значения: 'paragraph* —— для простого абзаца, ‘七土七1巳‘—— для заголовка, 'anchor'—— для ссылки и "table* —— для таблицы. Таким же образом можно объявить поле place, но так как назначение этого поля меняется при изменении значения поля type, это достаточно затруднительно. Поэтому далее полю place будут присваиваться числовые значения, смысл которых будет зависеть от значения поля type.
Разработку системы CMS начнем с системы администрирования, которая включает следующие файлы:
口 addpartform.php — HTML-форма для добавления раздела статей;
□ addpart-php — скрипт добавления раздела статей (обработчик формы addpartform.php);
口 addartfarm.php —— HTML-форма для добавления новой статьн в раздел;
О addart.php — скрипт добавления новой статьи в раздел (обработчик формы addartform.php);
addanchform.php — HTML-форма для добавления элемента "гиперссылка";
口 addanch. php — скрипт добавления элемента нгиперссылкаи (обработчик формы addanchform» php);
addanchartfот.php — HTML-форма для добавления элемента "ссылка на статью";
addanchart.php — скрипт добавления элемента "ссылка на статью1' (обработчик HTML-формы addanchart.php);
addpar form, php — HTML-форма для добавления элемента "текстовый блок”;
口 addpar.php ——скрипт добавления элемента "текстовый блок" (обработчик формы addpar form. php);
О add tabi е f о пл. php — HTML-форма для добавления элемента ^таблица1*;
口 addtable.php — скрипт добавления элемента "таблица** (обработчик формы addtableform.php);
addtit if о rm. php — HTML-форма для добавления элемента "заголовок";
addtiti.php — скрипт добавления элемента "заголовок” (обработчик формы addtitle form, php);
口 art.php — главная страница редактирования статьн, которая отображает элементы, включенные в статью, и предоставляет инструменты для добавления, удаления и редактирования элементов статьи;
choose,php — вспомогательный скрипт, производящий переключение на обработчики формы добавления нового элемента (расположен на странице art. php) в зависимости от выбранного типа элемента;
delart.php — скрипт удаления статьи;
口 deipar.php — скрипт удаления параграфа;
口 downpar.php — скрипт перемещения параграфа на одну позицию вниз;
口 editanchform.php — HTML-форма для исправления элемента “гиперссылка";
editanch.php — скрипт исправления элемента ф,гиперссылка*1 (обработчик формы editanchform.php);
editart form, php — HTML-форма для исправления метаданных статьи;
口 editart.php — скрипт исправления метаданных статьи (обработчик формы editartform.php);
editparform.php — HTML-форма для исправления элемента "текстовый блок";
口 editpar.php ——скрнпт исправления элемента "текстовый блок" (обработчик формы editparfom.php);
口 edittableform.php — HTML-форма для исправления элемента ^таблица";
О edittabie.php — скрипт исправления элемента "таблица" (обработчик формы edittableform.php);
□ edittitiform.php — HTML-форма для исправления элемента "заго- ловок”;
□ edittitl.php — скрипт исправления элемента "заголовок" (обработчик формы edittileform. php);
口 hideart .php — скрипт, помечающий статью как скрытую;
口 hidepar.php — скрпит, помечающий параграф как скрытый;
口 index.php — главная страница администрирования, отображающая спн- сок разделов;
口 showart* php — скрипт, помечающий статью как доступную для просмотра;
□ showpar. php —— скрипт, помечающий параграф как доступный для просмотра;
□ uppar.php — скрипт перемещения параграфа на одну позицию вверх.
Для работы системы администрирования требуется файл, устанавливающий соединение с базой данных — config.php, расположенный на один уровень выше, как это описано 8 главе 6. Все ссылки на него будут относительными. Они должны нметь вид: "../config.phpn. Это требуется для создания единого конфигурационного файла для всех Web-приложений, чтобы при смене паролей требовалось изменение настроек соединения лишь в одном файле (листинг 7.4).
? Листинг 7.4* Единый конфигурационный файл' *♦ : *,覇НИМ!I
/admin
/config.php
/Webl
/index.php(・./config.php)
/work.php(・・/config.php)
/Web2
/ index.php (../config.php)
/work.php(../config.php)
/Web3
/ index•php (*./config.php)
/work.php(* ・/config.php)
Содержимое файла config.php приведено в листинге 7.5.
п^нг7:5-ф^л^^^…,;ж
<?php
// Имя сервера базы данных, например,
// $dblocation = nmysql28.noweb.run
// сейчас выставлен сервер локальной машины
$dblocation = "localhost°;
// Имя базы данных на хостинге или локальной машине
$dbname - "articles'*;
// Имя пользователя базы данных
$dbuser = "root";
// и его пароль
$dbpasswd м "“;
// Устанавливаем соединение с сервером MySQL
$dbcnx = @mysql_connect($dblocation,$dbuser,$dbpasswd);
if (!$dbcnx)
{
echo(П<Р>В настоящий момент сервер базы данных не доступен, поэтому корректное отображение страницы невозможно*</Р>п);
exit();
}
// Выбираем базу данных
if (! @mysql_select__db ($dbname, $dbcnx))
(
echo (**<p>B настоящий момент база данных не доступна, поэтому корректное отображение страницы невозможно < </ Р> ");
exit();
}
// Небольшая вспомогательная функция, которая выводит сообщение
// об ошибке в случае ошибки запроса к базе данных function puterгor($теэзаде)
{
echo (H<p>$message</p>,r);
exit();
Скрипт в листинге 7.5 производит соединение с сервером базы данных и выбор базы данных системы CMS. В конце файла размещема небольшая
вспомогательная функция вывода ошибок puterrorо, принимающая единственный параметр с текстом, выводимым в окно браузера, и останавливающая выполнение Web-приложения.
Систему адм ин и стри рова н ия CMS условно можно поделить на три части: □ управление разделами;
口 управление статьями;
□ редактирование статей.
Рассмотрим блок управления разделами.
Содержимое файла index.php, ответственного за отображение главной страницы адм и н л стри рования приведено в листинге 7.6.
i^"»#4"^' "*■学?箒”产■■• "■冲:产;界*"吓鼻"关"'*可e.?学;律:[Т弊7.[".?智 ? 虫出.:,空N; ii^^^^'i^'iX'妙¥警獰暫止.^^驾:,::.・#V翼三
i Листинг 7.6. Файл lnd@x.php-丨
<?php
// Соеданяемся с базой данных requireonce(л,./config.phpn);
?>
<а hre f^ naddpartfoxm * php'^Добавить новый раздел</а> <р>Список страниц</р>
<ul>
<?php
// Вывзжм список разделов $ query = n SELECT * FROM artpage*'; if($pgs = myslq_query($query)) {
while (list ($id_page, $part) =mysql_fetch_array ($pgs, MYSQL_NUM)) {
echo "<lixa Ьге^1пс1ех.рЬр?1с1_5)аде=$1б_раде>$ра^</а></11>";
} else puterror(пОшибка np城 обращении к блоку статей"); ?>
</ul>
<а h ге f=11 adda гt fо rm. php ?id_pa ge=< ? echo $_GET['id_page']; ?>"> Добавить новую статью на выбранную страницу</а>
// Если параметр id_page не передан странице, устана влив а ем епо
// значение, равное 1
if (! isset ($_GET [1 id_j)agef ])) $_GET [1 id^page1 ] = 1;
// Формируем SQL-запрос на выборку статей в выбранном разделе
$query = "SELECT * FROM articles
WHERE idjpage»". $_GET「id_j>age,] J ORDEH BY name'1;
// Выполняем SQL-запрос
$art = mysql_query ($query);
// Останавливаем выполнение скрипта, если происходит ошибка при
// выполнении SQL-запроса
if(!$art) puterror("Ошибка при обращении к блоку статей**);
// Если раздел содержит статьи, выводим их в таблице
if (mysql_nuin_rows ($art) >0) (
?>
<br>
<table>
<tr>
<td>Ha3saMMe(страница}</td>
<td>OnMcaHMe(страница)</td>
<td>Ha3BaHMe(title)</td>
<td>OnMcaHne(description)</td>
<td>KjBO4eBHe слова (keywords) < /td>
<td со!зрап^З>Действие</1с1>
</tr>
<?
while ($articles = mysql_fetch_array ($art))
(
〃 Ссылки на скрытие/отображение
if($articles[^ide1] ~ 'hide') $strhide 二"<a href=showart*php? id_article«M. $articles [1 id_article * ] •11 & idjpage=M»$_GET[1 id_page'J."> Отобразить</а>";
else $strhide = M<a href=hideart.php?
id_article=".particles['id^article1]•"& id_page="・ $_GET [1 id jage,], ,,>Скрыть</а>'1;
echo "O
<td>
<a href=art-php?id_article=,t.$articles[1id_article *]."& id_page=n,$_GET[* idjpage']•" >"・ $articles['name']«M</a> </td>
vtd>".$articles[1 description *]."</td>
<td>". $ articles [ html_title' ]. **</td>
<td>". particles [ ,html_desc ript ion1 ] . H</t<i>
<td>"•$articles[rhtml_keywords,]・"</td>
<td>$strhide</td>
<td><a href=editartform*php?
id_article=". $ articles [1 id__article' ]. ”&
id_page=". $_GET [,id_page,]. н>Редактировать</а> </td>
<td<a href=delart.php?
土d_artiule=". particles [' id__article' ] • "& id」)age=" • $_GET「id_j>ageT ]・ и>Удaлить</a></td> </tr>";
echo H</table>H;
}
?>*
В начале скрипта, приведенного в листинге 7.6, происходит включение файла config.php, код которого приведен в листинге 75 Это обеспечивает выполнение SQL-запросов, использующихся в файле indax.php.
После ссылки на добавление нового раздела (файл addpartfbrm.php, рассматриваемый ниже) при помощи SQL-запроса "select * from artpage" происходит выборка разделов, которые выводятся в виде ненумерованного списка, каждый элемент которого является ссылкой вида index - php? id_page=$ id_page, где $id_page — Первичный КЛЮЧ таблицы artpage.
просмотра (hidef shok), и Отобразить, если статья скрыта от просмотра посетителями (hide=,hide,)- Ссылки передают скриптам showart.php и hideart.php значение первичного ключа статьи, которую следует отобразить или скрыть соответственно.
[ Замечание )
Статус скрытой статьи означает, что статья не отображается на страницах сай* та, но по-прежнему доступна для редакции со страницы администрирования.
Ссылки Редактировать и Удалить ссылаются на скрипты editanfbrni.php и delart.php, которые будут рассмотрвнм ниже.
Переход по ссылке Добавить новый раздел приводит к форме addpart form.php, содержимое которой приведено в листинге 7.7.
;*)丝г浴军:世災>”?.."*:焚Л 竺*.-"…二* W"*",:y%Ж£М." T„L**t野:吋f ”у … 7:.产寸С к亨廿稣*^^冲 1七季:通谜爆1tM塁学"■ ;Листинг. 7.7. Файл addpartform.php
<a href^'1 javascript: history.back() ">Назад</а>
cfcrm action=addpart,php method-post>
Название :
< input class=input type=Htext" name=',nameN ><br>
<input class^button type= "sukmit ** va 1 ие=Добавить>
</fonn>
Форма, приведенная в листинге 7,7, имеет единственное текстовое поле name для названия раздела н кнопку, отправляющую содержимое поля скрипту addpart.php. Файл addpart.php приведен в листинге 7,8.
:::产,/.♦"含良,磔у『”¥:у*Ш,:.А,啓附::PF“A*;+d,H1!”,”¥,:F 工产七 *?:#:+?”>л …,OY 2产口,_,¥*::*£5 中 WT,±,.*W:j 却.?:,,〉:《,.产”;炉兰*K:L".*r*:L,.^^" 1?
Листинг. 7.8. Файл addpart.php:
IН.:: ■;■*.:廿二工'〒二Ш、 fX;:『;;■*‘.;. 党£'*落Vn К, ...岳:打分工也隔十書謡便碱爵M伊翩■♦.£№;*疝礪学戈窑.??;『君齡2霖喑存t.vi-芸亥品,.玄::.,:.广『*壮话M:
<?php
// Соединяемся с базой данных require once(и../config*phpп); // Проверяем, заполнено ли поле паше if(empty($_POST[rname」))links С^Введате название раздела."); // Заменяем одинарные кавычки на обратные
帛name 二 str__replace (” ' \ "、",$_POST[ 'name' ]};
// Формируем SQL-запрос на добавление нового раздела
$query = "INSERT INTO artpage VALUES (
NULL,
Yname 3
0)";
if (mysql_query ($query))
//В случае успешного добавления переходам на главную страницу // системы адаинистрирова ни я
echo M<HTML><HEAD>
<МЕТА HTTP-EQUIV='Re fresh1 CONTEKT=,Q; URl>index.phpr> </HEAD></HTML>»;
J
else puterror < "Ошибка при обращении к блоку статей11);
// Функция вывода ошибки в окно браузера function links($msg)
{
echo ”vp>" .$msg J</p>“;
echo **<p><a href=# onClick=1 history.back 0 *>
Вернуться к правке раздела</ах/р>11;
echo "<p><a href=index.рИр>Апминистрирование статей</а></р> и; exit () г
)
?>
После соединения с базой данных проверяем содержимое поля паше, расположенное в суперглобальном массиве $_post. Если в результате проверки обнаруживается, что значение этого элемента пусто — вызывается функция links о» которая выводит предупреждение, ссылки для возврата к правке раздела и останавливает работу скрипта. Функция принимает единственный параметр $msg, содержащий текст, выводимый в браузер.
[ Замечание )
Функция выводит две ссылки: Администрирование статей, переход по которой возвращает редактора на главную страницу системы администрирования, и Вернуться к правке раздела. Последняя ссылка реализована при помощи иачаЗспрЬфункции history.ЬаскО, позволяющей сохранить значения, введенные в поля формы, в отличие от прямого перехода по ссылке <а href=addpart.php>.
Далее посредством функции str_repiace<) все одинарные кавычки ('), содержащиеся в переданном параметре name, преобразуются в обратные кавычки ('). Это необходимо дяя того, чтобы избежать конфликта при формировании SQL-запроса на добавление раздела в таблицу artpage, так как все текстовые поля в нем должны обрамляться одинарными кавычками.
ЮЗак. 1181
После формирования и успешного выполнения SQL-запроса происходит автоматический возврат на главную страницу администрирования —— index.php через передачу браузеру соответствующего заголовка HRefresh**.
Далее будет рассмотрена система управления статьями, включающая добавление и удаление статьи, редактирование метаданных, скрытие и отображение.
Переход по ссылке Добавить новую статью в выбранный раздел приводит администратора к форме addartfbrm.php, содержимое которой приведено в листинге 7.9. Внешний вид формы приведен на рис. 7.5.
j Листинг 7.9. Файл addartform<php
<?
// Устанавливаем соединение с базой данных
require_once ("一 /config.php*1);
// Устанавливаем значения переменных по умолчанию
if (1 isset (faction)) $ act ion = 11 addart .php";
if(!isset(^button)) $button - "Добавить";
if(!isset($title)) $title = "Добавление новой статьи";
// Если значение $tmp не установлено^ помечаем вновь добавляемую статью // как доступную для просмотра (не скрытую}.
if (! isset ($1лф)) $trap = пcheckedи;
if (! isset ($」ЖТ [1 idjpage * ]))
(
echo ''Страница не выбрана";
exit (};
}
?>
<title><?= $title ?></title>
<a href="javascript: history* back()” >Назад</а>
<form action=<?php echo $action; ?> method=post>
Название : <input type=,,text,* name="nanie" value=lf<?= $name ?>Hxbr>
Описание : <textarea naiYie=Hdescription" cq1s="60" rows=n2M>
<?= ^description ?>
</textarea><br>
Название(titled :
<textarea name^"html_title1, cols="60H rows="2"> v?= $html_title ?>
</textarea ><br>
Описание(description):
<textarea najne=°html description" cols-"60" rows=M2H>
V?二 $html_de s с г ipt ion ?>
< / texta rea><br>
Ключевые слова (keywords > :
<textarea naine=Hhtrol_keywords '* cols="60" rows=rt2°>
<?= echo Shtmlkeywords; ?>
</textarea>
Отображать : <input type=Hcheckbox** name=Hhide" <?= $tmp ?»
<input class=,,buttonH typE="submit" valuerr,<?= $button ?>H>
<input type=nhiddenH name=Mid_pageH value=n<?= $_GET[rid_page1] ?>*'>
<input type=**hidden" name=1,id_article,' valus"<?= $id_article ?>”>
</form>
В начале скрипта происходит проверка трех переменных $action, $title и $ button, хранящих имя файл а-обработч ика формы, название формы и кнопки соответственно. Если значения переменных не установлены, им присваиваются значения по умолчанию. После приводится код формы» содержащей пять полей: для названия и описания статьи, поля, необходимые для формирования динамической страницы: название, которое будет отображаться в заголовке окна браузера, текст для метатегов <description> и <keywords>. Кроме этого, форма содержит два скрытых поля для параметров id_paga И id_article.
Каждое поле формы снабжено атрибутом value, принимающим значение по умолчанию. Такое построение формы необходимо для ее повторного но пользования при редактировании метаданных статьм, что будет рассмотрено ниже. Файл addartfbrm.php можно включить в другой файл — editartform.php, в котором метаданные предварительно извлекается из базы данных и помещаются в соответствующие переменные.
Содержимое полей формы addartform.php передается скрипту addart.php. который осуществляет добавление статьи в раздел (листинг 7.10).
? Листинг 7Л0. Файл addart.php
<?php
// Устанавливаем соединение с базой данных
require_once(r,. ./config»php11);
// Проверяем, заполнено ли поле name формы
if (empty ($_POST [' name1 ])) links {$_POST [ * id_page1 ],
**Введате название статьи.");
// Определяем, скрыта статья или нет
if ($ POST ('hide* ] = "on") $ showhide = "show11;
else $showhide = "hide";
// Заменяем одинарные кавычки обратными
$name = str_replace(”〔”「、",$_POST [ *name']);
$description = str_replace (n,,t,, $_POST [ description * ]);
$html_title = st jreplace(”'”「、”,$_POSTUhtml_t:itle']};
$html_description = str__replace ("''',”、", $_POST [ * html_description']);
$html_keywords = str_replace ("''',”'",帛_POST [' html__keywords,]);
// Формируем и осуществляем SQL-запрос на добавление статьи
$query = "INSERT INTO articles VALUES (
NULL, ,$name■,
'$description', '$html_titlel
'$html_de script ion *,
'$html_keywords I " • 0,
$showhide1,"・
$_POST [1 idqage 1 ”),
if(mysql_query($query))
{
// Выясняем первичный ключ id_article, с ген ериров анный для статьи
/ / механизмом AUTO_ICREMENT, чтобы добавить заголовок в таблицу // paragraphes
$id_article = mysql_insert_id ();
/ / Автоматически добавляем в статью заголовок,
// совпадакяций с названием статьи
$query = "INSERT INTO paragraphes
VALUESVALUES(NULL,(NULL,
VALUES
VALUES
(NULL,
(NULL,
title I$name1,
echo "<HTML><HEAD>
<МЕТА HTTP-EQUIV=1Refresh1
CONTENT=,0; URSindac.php?id_j)age^'\$_POST「id_page' ] *"*> </HEADX/HTML>";
} else puterror(нОшибка при обращении к блоку статей”);
}
else puterror(^Ошибка при обращении к блоку статейм);
// ①ункци月 вывода ошибки и ссылок возврата в окно браузера function links($id_page,$msg)
{
echo H<p>n. $msg. ,,</p>";
echo и<р><а href^# onClick=fhistory.back()'>
Вернуться к правке параграфа</a></p>n;
echo n<pXa href^index. php?id_page=$idjpage>
Администрирование статей</ах/р>м;
exit ();
} ?>
Форма имеет единственное обязательное для заполнения поле ——название статьи, поэтому проверка на пустое значение проводится только для параметра паше.
( Замечание )
Здесь функция links (). в отличие от аналогичной функции в листинге 7.8, принимает два параметра: $id_page ——первичный ключ раздела и $msg ——сообщение, выводимое в браузер.
Если флаг Отобразить в форме addartfbrm.php установлен, то возвращаемое в поле hide значение равно Ъп. Это означает, что редактор добавляет статью, которая сразу будет доступна для просмотра со страниц сайта. В этом случае временной переменной $ showhide, используемой далее для формирования SQL-запроса, присваивается значение 'show'. Если флаг будет сброшен, переменная $ showhide получит значение 'hide:
Следующим этапом является замена во всех текстовых переменных, переданных из формы addartform.php, одинарных кавычек (') на обратные ('), после чего происходит формирование и выполнение SQL-запроса добавления новой статьи в таблицу articles.
По умолчанию в статью добавляется заголовок, совпадающий с названием статьи. Для этого в таблицу paragraphes добавляется параграф со значе-
нием поля type ^ “title". Сгенерированное при помощи механизма AUTO_工ncrement значение первичного ключа новой статьи, необходимое для добавления заголовка, возвращается при помощи функции mysql_insert_id().
:Замечание )
В данном варианте системы CMS предполагается, что вероятность одновременного добавления двух новых статей равна нулю. Поэтому выбрана таблица типа MylSAM, как более быстрая, но не поддерживающая механизма транзакций.
После успешного добавления обеих записей в таблицы articles и paragraphes происходит автоматический возврат на страницу управления статьями.
Теперь рассмотрим редактирование метаданных статей. За это ответствен файл editartform.php, который использует ранее рассмотренный файл addartfbrm,php (листинг. 7.11).
Листинг 7Л1.Файп editartform,php
<?php
// Устанавливаем соединение с базой данных require_once (°. /config.php);
// Проверяем, установлены ли значения параметров
if(!isset($_GET[1id__article1]) && !isset($_GET[1id^page1])) {
echo "He выбрана статья для исправления1*; exit ();
I else {
// Формируем SQL-запрос на выборку метаданных статьи
$query 匸"SELECT * FROM articles
WHERE id_article='\ $_GET[1id_article『];
// Выполняем SQL^sanpoc $prg = mysql_query ($query); if($prg) $par = mysql_fetch_array($prg); else puterror (,гОшибка при обращении к блоку статей11); // Выясняем, скрыта статья или доступна if ($par [ *hide* J = "hide") $Ьф -"”; else $5р = "checked”;
/ / Выбираем данные из базы данных
=$par「name,];
= $par[*description,];
=$par [1 html_title1 ];
l = $par[1html_description*];
= $par [' html__keywords * ] ?
=$par[1id_article1];
к $par[*pos1];
переменные формы addartform* php
■ иРедактирование статьи**;
="Исправить*1;
="editart.php";
$name $description $html_title $html_description $ html_keywo rds $id__article $_GET [ * pos1)// Устанавливаем i $title$button $action$name $description $html_title $html_description $ html_keywo rds $id__article $_GET [ * pos1)// Устанавливаем i $title$button $actioninclude "addartfот.phpM;
$name $description $html_title $html_description $ html_keywo rds $id__article $_GET [ * pos1)
// Устанавливаем i $title
$button $action
$name $description $html_title $html_description $ html_keywo rds $id__article $_GET [ * pos1)
// Устанавливаем i $title
$button $action
*憑囑:.?吧藝更噸.1®*憑囑:.?吧藝更噸.1®Так как форма для управления метаданными статьи уже была разработана в листинге 7.9, в ней необходимо лишь изменить значения переменных по умолчанию, настроенных на добавление статьи. Для этого после проверки на допустимое значение параметров id_article и 士d_page производится формирование и выполнение SQL-запроса на выборку из таблицы articles записи, соответствующей исправляемой статье. После этого происходит установка значений полей формы addartfbrm.php. Следует отметить, что переменной $action устанавливается значение editart. php, что позволяет переопределить обработчик формы. В результате этого переменные из формы будут передаваться не в файл addart.php, а в editart.php, код которого приведен в листинге 7.12.
*憑囑:.?吧藝更噸.1®
*憑囑:.?吧藝更噸.1®
<?php
// Устанавливаем соединение с базой данных
require_once ("一 /config. php");
// Проверяем, заполнены ли поля формы
if (empty ($_POST [ 'name * J)) links ($_POST [1 id_page,],
"Введите название статьи.");
if(empty($_POST[*pos1])) links($_POST('id_page'],
**Введите позицию параграфа * ");
// Опре дел яем, скрыта статья или нет
if ($ POST [* hide1] = "on") $ showhide = 11 show";
else $showhide = "hide";
/ / Заменяем одинарные кавычки обратными
$name = str_replace (11 * ", "'*\ $_FOST[Fame']);
$description = str_replace(",","、”,$_POST[description‘]); $html_title = str_replace (°1",11' *\$_POST[ ^tml^title11); $html_description = str_replace(”・”,"、”,$__POST[fhtml_description1)); $html_keywords = str replace(”■”「、”,$_POST [1 html_keywords1 ]); / / Формируем и осуществляем SQL-запрос на исправление метаданных // статьи
$query = "UPDATE articles SET
name=1 $name *,
description=,$description1, html__title=,$htnil_title*, html_descript ion= ‘ $ html__des cr ipt ion ‘ / html_keywords^ * $html_keywo rds *, pos=r,. $_POST [,pos "J,
hide=1 $ showhide‘
WHERE id_article=rt. $_POST [1 id_article "; if (mysql_query($query)) (
// В случае успеха осуществляем автоматический переход
// на странтду управления статьями echo n<HTML><HEAD>
<МЕТА HTTP-EQUIV=*Refresh1
CONTENT^10; URD=index.php?id_page=n.$_POST[r id_page 4•"1> </HEADX/HTML>n;
}
else puterror{^Ошибка при обращении к блоку статей");
function links($id_page,$msg) {
echo n<p>", $msg* H</p>**;
echo ”vp>" href=# onClick=1history.back()‘> Вернуться к правке параграфа</а></р>**;
echo "vpXa href = index * php?id_page^=$id_page> Администрирование статей</а></р>";
exit();
Файл editart.plip из листинга 7.12 походит по структуре на файл addart.php (см. листинг 7.10), только вместо SQL-оператора insert используется update, позволяющий обновить поля записи.
( Замечание
Заголовок статьи, добавленный в addart.php в таблицу paragraphes, преобразованиям не подвергается, так как он может быть удален или перемещен таким образом, что на первой позиции будет текстовый блок или другой элемент статьи. В этом случае изменение названия может привести к потере информации в статье и нарушению логики ее построения.
Скрытие статьи не требует отдельной формы н осуществляется при помощи скрипта, расположенного в файле hideart.php (листинг 7.13).
:Листинг 7ЛЗ. Файл hideart.php;
<?php
// Соединяемся с базой данных
require__once (n ../config .phpH);
// Формируем SQL-запрос
$ query = "UPDATE articles SET hid”1 hide'
WHERE id_article=".$_GET['id^article•];
// Выполняем запрос if (mysql_query($query))
// В случае успеха возвращаемся к списку статей
echo (,<HTML><HEAD>
<МЕТА HTTP-EQUIV= * Refresh1
CONTENTS 0; URL=index. php? idjag@="・ $_GET [1 id_page ']*”‘>
</headx/h™l>m;
} else put er rot ("Ошибка при обращении к блоку статей0);
?>
Файл hideait.php записывает при помощи SQL-оператора update в поле hide значение 'hide'. Файл showart.plip, выполняющий обратную задачу — отображение статьи, отличается лишь тем, что полю hide в операторе update присваивается значение 'show'.
Изменять статус статьи (скрытая/нескрытая) можно не только через гиперссылки Скрыть и Отобразить на главной странице администрирования, но и через форму исправления метаданных статьи, устанавливая или снимая флажок Отобразить.
Последняя функция блока управления статьями заключается в удалении статей. Она осуществляется при помощи скрипта, расположенного в файле delart.php, содержимое которого приведено в листинге 7.14.
»^^'а^^»^^'а^^
»^^'а^^
»^^'а^^
<?php
// Соединяемся с базой данных require_once(” ・・/config.php”);
// Проверяем, нет ли изображений. Если есть — удаляем
$query = "SELECT * FRCM paragraphes
WHERE id_article=**. $_GET [' id_article,] . " AND 'picture_sruall!e' -' AND ' picture_small!='…; $par = mysqlquery < $query);
if ($par)
while($row = mysql_fetch_array($par)) {
if (is_file {"*./". $row [1 picture_small * ]))
unlink ("・./". $mw [ * picture_small1 ]};
if (is__file(,\./H. $row[,picture_big •])) unlink (tr.. / " .$row[ fpicture__bigf ]);
} else puterror(^Ошибка запроса к блоку статей0);
// Выполняем запрос на уничтожение параграфов и метаданных статьи $дивгу』аг = ° DELETE FROM paragraphes
WHERE id_article=r,. $_GET「idarticle'];
$query_art = "DELETE FROM articles
WHERE id_article=H.$_GET[1id^article * 1;
if (mysql_query ($query_par) && mysql_query ($query__art) }
{
// Осуществляем автоматический переход на главную страницу // администрирования статей echo M<HTMLXHEAD>
<МЕТА HTTP-EQUIV=1 Refresh1
CONTENT-10; DRSindex・php?id_page«M.$_GET[1 id^page']."‘>
</HEADX/HTML>°;
} else puterror {нОшибка при обращении к блоку статей'*);
Удаление записи, соответствующей статье из таблицы articles, усложняется тем, что в таблице paragraphes имеются записи, ссылающиеся на запись из таблицы articles. Поэтому необходимо также удалить все параграфы, принадлежащие данной статье. Это влечет за собой необходимость удаления изображений, принадлежащих этим параграфам. Поэтому в файле delart.php (см. листинг 7.14) осуществляется запрос на выборку всех параграфов, принадлежащих статье (id_article«$_GET [ * id_article1 ]) И содержащих ИЗО* бражение. Все обнаруженные изображения уничтожаются.
: Замечание )
Поле, соответствующее увеличенному изображению, можно не проверять SQL- запросом, так как увеличенное изображение добавляется только при наличии обычного изображения.
После этого формируются и осуществляются SQL-запросы на удаление записей из таблиц articles и paragraphes, относящихся к статье с первич- ньш ключом, переданным скрипту deiart.php в качестве параметра id_article.
На этом разработка блока управления метаданными статьи закончена. Далее будет рассмотрен непосредственно Web-редактор статей.
Ключевым файлом, осуществляющим отображение структуры статьи, является art.php, код которого приведен в листинге 7.15. Окно Web-редактора показано на рис. 7.12.
1Л^с™н^^151*а.^..а^Р:Й^(®1ж;.;Ь'сА^
<?php
// Осуще с ТВляем соединение с базой данных reqaire_once(n../config.phpH);
echo и<а href=index•php?id_page^".$_GET【'id_page']." >
Список статей</а>°;
if(!isset($_GET[*id_article'])) {
echo ℃татья не выбрана";
exit();
// Ниже выводится структура статьи и форма для добавления новых
// элементов
?>
<form action=choose * php method=post>
<input type^hidden nanie=id_article value=<?= $_GET [ * id_article1) ?»
break;case 2: // $strnamebreak;case 3: // $stmamebreak;case 5: // $strnamebreak;case 4: // $strnamebreak;case 2: // $strnamebreak;case 3: // $stmamebreak;case 5: // $strnamebreak;case 4: // $strname
break;
case 2: // $strname
break;
case 3: // $stmame
break;
case 5: // $strname
break;
case 4: // $strname
break;
case 2: // $strname
break;
case 3: // $stmame
break;
case 5: // $strname
break;
case 4: // $strname
// Рекурсивная функция по выводу параграфа function putarticle($pos,$id_article) {
$par = mysql_query (^SELECT * FROM paragraphes
WHERE id_article=$id__article AND pos=$posH); if ($par && mysql_num_rows ($par) >0) {
$paragraphes = mysql_fetch_array ($par); switch($paragraphes[1type1])
(
case 1 title1: // Заголовок
$type = "Заголовок";
$stredit = "<a href=edittitlfom.php?
id__article=s$id_article&
id_paragraph=^n.$paragraphes [1 id_j)aragraph* ].">
Исправить</а>и;
switch($paragraphes(,place1])
case 1: 〃 Название страницы
$strname = "<hl cl a s s=a rtnamepa ge>
.$paragraphes[1name1]."</hl>";
Важный блок страницы
="<hl class^artblockpage>
"・ $ paragraphes [Fame' J. "</hl>";
Заголовок обычный
="<hl class=artzag>
n.$paragraphes fname1] /*</hl>n;
Заголовок таблицы
= "<hl class=artnametable>
"・ $paragraphes(*name1]•"</hl>";
Заголовок листинга
="<hl class=artnamelisting>
".$pa rag raphes [Fame' ] • "</hl>";
break;
case 6: // Заголовок примечания $strname = "<hl class=artnamenote>
".$paragraphes['name *]・"</hl>"; break;
}
break;
case * paragraph1: // Абзац
$type = "Абзац";.
$stredit = ”<自 href«editparform.php?
id_article=$id__article&
id_paragraph=". $paragraphes [1 id_paragraph * ]."> Исправить </а>";
switch($paragraphes[1place1])
{
case 0: // Обычный параграф
$strname = "<p class=arttext>n.$paragraphes[1name']."</p>"; break;
case 1: // Жирный
$stmame = ”《p class-arttext>
<b>" . $paragraphes [Fame,] »,l</b></p>H; break;
case 2: // Курсивный
$strname = "<p class=arttext>
<i>n.$paragraphes [ ^ame1 ] * H</ix/p>";
break;
case 3: // Ненумерованный список $strname = M<ul><li>*4
strreplace(”\n", "<li〉",$paragraphes[Tname,]) .a\n</ul>";
break;
case 4: 〃 Нумерованный список $ strname = ,r<ol><li>r,.
str_replace(n\n", H<li>H,$paragraphes[1name1]) .M\n</ol>n;
break;
case 5: // Подсвечиваем код
$strname = high lightest ring ($paragraphes [1 name * ], true); break;
case 6: // Примечание
$strname = 11 <p class=artmtE>"・
$раragraphes「name' ] J</p>";
break;
case 7: // Параграф с отступом
$5trname = "<р class=arttextindent>n. $paragraphes [Fame,J ・"</p>";
break;•
}
break;
case 1anchor *: // Ссылка
$type =• "Ссылка";
$stredit = "va href^ditanchform.php?
id_artxcle=$id_article5
id_paragraph=". $paragraphes [' id paragraph1 ]."> Исгфавить</а>п;
$strname = n<a href="•$paragraphes['url'J.">". $paragraphes[1name1]
•”</a>";
break;
case 'table *:
$type = "Таблица”;
$stredit = "<a href=edittableform.php? id_article=$ id__article&
id^paragraph=n.$paragraphes[^d^paragraph1]* M> Заменить </a>“;
$strnaine = $paragraphes [ * name1 ];
break;
}
// Ссыпки на скрытие/отображение параграфа
i f《$paragraphes Г hide '] = ' hide') $strhide = "<a href=showpar,php?
id_article^$id_article&
id-paragraph=n. $paragraphes [' idparagraph1 ]."> Отобразить </a>n;
else
$strhide = "<a href=hidepar,php?
id_article=$id^article&
id_paragraph=n. $paragraphes [1 id_paragraph1 ]. **> Скрыть</a>";
// Ссылки на изображения
$small ="”;
if ($paragraphes[rpicture_small1] != ”" && $paragraphes['picture_small'] !="-") (
if(is_file C' *•/H * $paragraphes[1picture_small*]))
if <filesize("../H.$paragraphes[,picture_small*]) != 0)
$ small = и<а target:」>lank
h.ref=, * /". $paragraphes [ 'picture_sniall']."> Изображение</а>r*;
}
$big ="";
if ($paragraphes[Tpicturebig1] != "” && $paragraphes [* picture _big‘] 1= **-**} {
if (is_file ("••/”. $paragraphes [1 picture__small * ]))
if(filesize("../M,$paragraphes(1picture_big1]) != 0 }
$big 二 и<а target=_b1ank
href=../".$paragraphes[,picture_big‘]・"> Изображение по ссылке</а>м;
)
// Вывод параграфа
echo ”<tr>
<tdxinput typLHadio name=pos checked
valuer"・ $paragraphes [1 id^aragraph1) .M>
</td>
<td>
$strname
$small $big
</td>
<tdxa hre f=uppar. php?
id_article=$id_article&
id_j)aragraph=" , $paragraphes【1 id paragraph1 ]. **&
pC5=”.$paragraphes 【'pos1].”'Переместить вверх</а> <br>
<a href=downpar.php?
id_article^$id_article&
id_paragrap^" . $paragraphes (* id^paragraph1) . "& pos=H.$paragraphes[fpos,]•”〉Переместить вниз</а>
</td>
<td>$stredit<br>
$strhide<br>
<а href=delpar.php?
id_articie^$id_article&
id_paragraph=,t. $ pa ragraphes [' id__paragraph1). ”>
Удалить</a>
</td>
</tr>rt;
// Рекурсивно вгызьшаем функцию для отображения других
// параграфов статьи
putarticle($paragraphes['idparagraph *],$id_article);
}
?>
Файл ait.php начинается с формы, позволяющей добавлять новые элементы статьи, передавая первичный ключ запнсн таблицы articles соответствующей редактируемой статье и позицию параграфа ——скрипту choose.php, который производит переключение на соответствующий обработчик. Элементы управления формы дублированы н выводятся в начале и конце страницы. Между элементами управления формы выводится таблица, содержащая па- ратрафы, каждому из которых соответствует переключатель, позволяющий задать позицию вставляемого элемента статьи. По умолчанию выделен по^ следний элемент статьи, н новые элементы добавляются в конец.
За вывод строк таблицы с элементами статьи несет ответственность рекурсивная функция putarticle ($pos, $id_article), принимающая два параметра. Первый параметр, $pos, передает позицию выводимого элемента, а второй, $id_articie, передает функции значение первичного ключа статьи.
Позиция элемента статьи определяется следующим образом. Первый элемент статьи имеет значение поля роз, равное 0. При добавлении новой статьи в качестве первого элемента в статью автоматически помещается заголовок, значение pos которого и приравнивается 0. Поле роз следующего за ним элемента равно первичному ключу первого элемента id_paragraph, точно так же определяется поле pos для второго по порядку элемента статьи (рнс. 7.13).
Альтернативным способом определения позиции парапэафов является нумерация их по порядку, как это продемонстрировано на рис. 7.14. Среди достоинств такого способа нумерации ——простота восприятия, возможность обойтись без рекурсивного спуска, использование одного SQL-запроса на
выборку. К недостаткам относится сложность реализации перемешения параграфов вверх и вниз, так как столбец роз не является уникальным.
id_paragraph
рО8
14
0
45
14
142
45
8
142
Рис. 7.13. Определение позиции параграфа
Рис. 7.14. Альтернативный способ определений позиции параграфа
Функция putarticie()выводит в окно браузера параграф, позиция которого роз совпадает со значением второго параметра функции и ссылки на скрытие/отображение, редактирование, удаление и перемещение параграфа.
После SQL-запроса на выборку выводимого параграфа следует конструкция switch, которая в зависимости от содержимого поля type записи таблицы paragraphes формирует гиперссылку Исправить, уникальную для каждого из типов, а также временную переменную $strname, хранящую содержимое параграфа.
Гиперссылки Отобразить, Скрыть, Удалить и гиперссылки для перемещения вверх и вниз (кнопки в виде стрелок) одинаковы для всех типов элементов, поэтому формируются вне управляющей конструкции switch.
В случае если поле type принимает значения 'title' или paragraph'» применяется дополнительный переключатель switch. Он анализирует поле place для определения дополнительного форматирования заголовка и текстового абзаца.
[ Замечание )
В листинге 7.15 для оформления различных типов заголовков используются стилевые таблицы. Здесь также могут применяться различные уровни заголовков, задаваемые HTML-тегами от <Н1> до <Нб>.
Для элемента типа 'paragraph' наиболее интересны значения поля place от 3 до 5. Цифры 3 и 4 соответствуют ненумерованному и нумерованному спискам. Каждая подстрока, заканчивающаяся признаком конца строки "\п", соответствует элементу списка, при этом исходная строка разбивается на элементы при помощи функции str_repiace(), заменяющей символы “\п" на тег <ii>, соответствующий началу элемента списка.
Таким образом, элементом статьи может быть как весь список, так и каждая позиция списка. В последнем случае можно менять порядок следования в списке.
( Замечание )
При работе с параграфами принят другой подход. Далее будет показано, что при добавлении параграфа, содержащего один или несколько символов конца строки **\п", текст автоматически разбивается на отдельные параграфы, которыми далее можно управлять независимо друг от друга.
При значении поля place, равном 5, текст абзаца подсвечивается при помощи функции highlight_string () согласно синтаксису PHP.
Далее будут рассмотрены скрипты, обеспечивающие операции редактирования параграфов. Первым будет рассмотрен скрипт, расположенный в файле choose.php. Этот скрипт является обработчиком формы, расположенной в файле art.php (листинг 7.15), Содержимое файла choose.php приведено в листинге 7.16.
;Листинг 7.15. Файл choose.php
<?php
faction = 11 index, phpr,;
switch ($_POST [ * typepar,])
(
case 1: // Заголовок
$action = "addtitlform. php11; break;
case 2: // Абзац
faction = "addparfoinn.php"; break;
case 3: ,// Ссылка
$action = ^addanchform, php*1; break;
case 4: // Новая статья faction = Haddanchartforni. php"; break;
case 5: // Таблица из сз¥-файла $action = "addtableform.phpM; break;
}
echo '*<HTMLXHEAD>
<META HTTP-EQUIV=1 Refresh * 8NTENT='O; URL^$action? id_artiC=”.$_POST「id一己工ticl修]."& pos=" • $_POST [^s1 ] .*4
id3age=". $_POST [1 id__page1 ] . "1 > </HEADX/HTML>M;
Файл choose.php может принимать четыре параметра: typepar, id article, pcs и id_page. В зависимости от значения параметра typepar при помоши конструкции switch происходит переключение на обработчики, расположенные в файлах addtitl form.php, addparforni.php, addaiichfbnn.php, addanchartfbnn.php и addiabletbrm^php, для добавления заголовка, абзаца, ссылки, ссылки на новую статью и таблицы соответственно. Оставшиеся три параметра передаются обработчикам в качестве параметров.
Рассмотрим перечисленные выше обработчики более подробно. Файл addlitlform.php, код которого приведен в листинге 7J6, содержит форму для добавления нового заголовка. Внешний вид формы, создаваемой файлом addtitlefbrm.php, приведен на рис. 76
:" ;*У ",^^^安产,ул■聚 \厘 嘴£4"工z辞嘅'¥'=产"1" "", 'V ? кй^‘安2 **学找¥^?;:;*Y …J'
:Листинг 7.16. Файл addtltiform.php
;•. •»<»»Л Л» ЛSr«vA4 < п <»>:“工 лл • • А. ь ■ > лтй i iV.?A#S?SH^EKi?ffi ЛтК ,
<?php
// Осуществлнем соединение с базой данных require once(” ・./config.php");
// Проверяем, передан ли параметр idarticle if(!isset($_GET[1id_article1]))
<
echo nСтатья не выбрана";
exit ();
}
// Если позиция заголовка не передана, выставляем ему позицию, // соответствующую последнему параграфу
.if(!isset($_GET[rpos1J))
{
$query = "SELECT MAX(id_paragraph) AS pos
FROM paragraphes
WHERE id_article^H»$_GET[1id^article,J;
$cnt = mysql_query {$query); if($cnt)
$count ^ mysql_fetch_array(Sent);
if($count[fposf] = "”) $_GET[rpos'] = 1;
else $_GET「p3,] = $count [ *pos1};
} else put error (11 Ошибка при обращении к блоку статей'1);
}
// Выставляем переменные по умолчанию
if (!isset (^button)) ^button = "Добавить";
if (! isset ($tmp)) $tmp = "checked";
if (! isset ($title)) $title = 11 Добавить заголовок";
if(!isset{$action)) $action = "addtitl*phpn;
<a href="javascript: history.back()">Назад</а>
<form action=<?php echo faction; ?> method=post>
Заголовок : <input type«text name=name
value=1<?php echo htmlspecialchars($name); ?> * xbr> Стиль заголовка :
<select t^e^text name=h>
<option value=3<?phpif<$h==3)echo"selected";?>>Обычный
<option value=l<?phpif《$h==l)ecbo"selected11;?»Названиестраницы
<option value=2 <?php if ($h=2) echo *'selected*1; ?»Важный блок страницы
<option value=4<?phpif ($h=4)echo"selected11;?»Названиелистинга
< opt ion value=5<?phpif ($h—5)echo"selected11;?»3аголовок таблицы
< opt ion value=6 <?php if ($h=6) echo "'selected”; ?»3a головок примечания </selectxbr>
Отображать : <input type=checkbox name=hide <?php echo $tmp; ?»<br> < input type^submit value=v?php echo $ but ton; ?»
<input type=hidden name=pos value=<?php echo $_GET [1 pos * ]; ?»
<input type=hidden name=id_article value=<? echo $_GET[1id_article1]; ?»
<input type=hidden name=id_paragraph
value=<?php echo $_GET[1id_paragraph']; ?»
</form>
После установки соединения с базой данных происходит проверка значения параметра id_article: если значение параметра не установлено, работа скрипта приостанавливается, так как в этом случае невозможно установить, в какую из статей происходит добавление заголовка. В отличие от параметра id_articie, для параметра pos допускается неустановленное значение. В этом случае при помощи SQL-зап роса выясняется максимальное значение первичного ключа id__paragraph для параграфов, принадлежащих редактируемой статье. Именно это значение и передается далее в качестве параметра роз. Такой подход позволяет в случае, если параметр pos не передан, размещать новый заголовок в конце статьи. Далее, как и для файла addart form, php (см. листинг 7.9), устанавливаются значения переменных, допускающих перенастройку формы для последующего использования ее при редактировании заголовка.
Форма состоит нз текстового поля для заголовка, выпадающего списка для задания типа заголовка, флага скрытия/отображения и кнопки, позволяющей отправить содержимое формы обработчику, которым по умолчанию является скрипт, расположенный в файле addtitl.php, содержимое которого приведено в листинге 7.17.
<?php
// Осуществляем соединение с базой данных require_once (,1 *. /config.php");
// Проверяем, заполнены ли обязательные поля формы
if (empty ($_POST [Fame Ч)) 1 inks ($_POST [fid_article' J,
''Введите заголовок.°);
if(en^pty($_POST「pos,J)) links($_POST[1id_article1],
"He указан предьщущий параграф*");
// Определяем, скрыт параграф или нет
if($_POST[,hide,] = ”cn") $showhide = "show"; else $showhide = "hide";
// Заменяем одинарные кавычки обратными
$_POST [Fame ■ ] = str_replace ^^"f»'^t $_post [Fame 4);
// Формируй и осуществляем SQL-запрос на добавление параграфа
// типа ^Заголовок1*
$query = "INSERT INTO paragraphes VALUES (NULL, titles '”.$_POST['namb ]・”'
”.$_POST[,hH
"•$_POST「pos,]・”,
1 $showhide',
”.$_POST[ Td_article*] J);"; if (mysql^query ($query))
// Выясняем id_paragraph только что добавленной записи
$_POST ['idbparagraph* ] = mysql_insert_id();
// У параграфа, который ссылался на предыдущий параграф,
// изменяем значение поля роз
$query = "UPDATE paragraphes
SET pos=H. $_POST [,idjaragraph1 ]. ”
WHERE id_article="• $_POST[*id_article'] J
AND pos=” ・ $_POST [1 pos」."
AND id_paragraph!=M.$_POST[1idjparagraph1];
if(!mysql_query($query))
puterror (*гОшибка при обращении к блоку статей**);
/ / Автоматический переход на страницу ре да ктиров ания статьи echo ”<HTMLXHEAD>
<МЕТА HTTP-EQUIV= * Refresh1 CONTENT=rO; URSart.php?
ic^_article=,,. $_POST [1 id__article ']・"'>
</HEADX/HTML>";
} else puterror("Ошибка при обращении к блоку статей");
function links ($id__article, $msg)
{
echo r,<p>n. $msg ・"</p>";
echo n<pxa href=# onClick= * history. back () *>
Вернуться к правке параграфа</а></р>Г|;
echo и<р><а href=art < php?id_article^$ici_article>
Администрирование статей</а></р>п;
exit{);
}
?>
В листинге 7.17 после проверки обязательных полей, замены в тексте заголовка одинарных кавычек (г) обратными ( ) формируется и выполняется SQL-запрос на добавление нового заголовка в таблицу paragraphes. Так как заголовок может быть вставлен в любом месте статьи, то на этом этапе с угадывается ситуация, при которой два парафафа имеют одинаковое поле роя, как это показано на рис. 7.15, где после парафафа с id_paragraph = 45 вставляется новый параграф. Для того чтобы исправить ситуацию, необходимо параграфу с первичным ключом id__paragraph = 142 поменять значение поля роз с 45 на 214.
Для этого сразу после выполнения оператора insert вызывается функция mysql_insert_id (), возвращающая первичный КЛЮЧ id_paragraph ТОЛЬКО что добавленного абзаца, н при помощи оператора update изменяется поле роз старого параграфа.
Рис, 7.15. Вставка нового параграфа со значением id_paragraph = 214
Файл addpailbrm.php (листинг 7.18) представляет собой форму для добавления нового текстового блока. Окно формы, созданной при помощи файла addparfbrnLphp, показано на рис. 79
// Осуществляем соединение с базой данных require_once (”…/config. php");
if (! isset ($_GET [fid_article']))
echo "Статья не выбранаn; exit О ;
// Если позиция абзаца не лереданаг выставляем ему последнюю позицию if(!isset($_GET{'$pos*]))
$ query ^ "SELECT №X (id_paragraph) AS pos
FROM paragraphes
WHERE id_article=" . $_GET [' id^article*);
$cnt 二 n^sql_query ($query);
if(Sent)
4
$ count = mysql_fetch_array ($cnt);
if($count[Tpos4 == "") $_GET[lpos,] = 1;
else $_GET[•pos口 = $count[*pos *];
} else puterror ("Ошибка при обращении к блоку статей0);
// Выставляем переменные по умолчанию
if (! isset (^button)) $ button = "Добавить**;
if{!isset($tmp)) $tmp = иcheckedH;
if (Jisset ($title)) $title = "Добавить параграф1*;
if (! isset ($action)) $ action = ** addpar, php";
?>
<a class=menu href= njavascript: history.back()н>Назад</а>
<form enctype='multipart/form-data, action=1<?php echo $action; ?>' method=post>
Содержание : <textarea name=naine cols=75 rows₽14><?= $name; ?></textarea> Стиль : <select type=text name=place>
<option valugO<option valugO<?php if($place == 0)<?php if($place == 0)тексттекст<option value=7 <?php<option value=7 <?phpif($place g 7)if($place g 7)<option value=l <?php if ($place = 1)<option ualue=2 <?php if($place = 2)<option value=l <?php if ($place = 1)<option ualue=2 <?php if($place = 2)<opt ion valu.e^3<opt ion valu.e^3<?php if(帛place — 3)<?php if(帛place — 3)echo ^selected'1; ?» Основнойecho selected"; ?»C отступом echo ^selected11; ?»Жирный echo "selected"; ?»Курсивный echo "selected"; ?»echo ^selected'1; ?» Основнойecho selected"; ?»C отступом echo ^selected11; ?»Жирный echo "selected"; ?»Курсивный echo "selected"; ?»<option value=4 <?php<option value=4 <?phpif($place = 4)if($place = 4)<option value=5 <?php if £$place = 5)<option vakn6 <?php if ($place == 6)<option value=5 <?php if £$place = 5)<option vakn6 <?php if ($place == 6)Ненумерованный список echo "selected**; ?»Нумере в анный список echo "selected**; ?»Код echo "selected”; ?»ПримечаниеНенумерованный список echo "selected**; ?»Нумере в анный список echo "selected**; ?»Код echo "selected”; ?»Примечание
<option valugO
<option valugO
<?php if($place == 0)
<?php if($place == 0)
текст
текст
<option value=7 <?php
<option value=7 <?php
if($place g 7)
if($place g 7)
<option value=l <?php if ($place = 1)
<option ualue=2 <?php if($place = 2)
<option value=l <?php if ($place = 1)
<option ualue=2 <?php if($place = 2)
<opt ion valu.e^3
<opt ion valu.e^3
<?php if(帛place — 3)
<?php if(帛place — 3)
echo ^selected'1; ?» Основной
echo selected"; ?»C отступом echo ^selected11; ?»Жирный echo "selected"; ?»Курсивный echo "selected"; ?»
echo ^selected'1; ?» Основной
echo selected"; ?»C отступом echo ^selected11; ?»Жирный echo "selected"; ?»Курсивный echo "selected"; ?»
<option value=4 <?php
<option value=4 <?php
if($place = 4)
if($place = 4)
<option value=5 <?php if £$place = 5)
<option vakn6 <?php if ($place == 6)
<option value=5 <?php if £$place = 5)
<option vakn6 <?php if ($place == 6)
Ненумерованный список echo "selected**; ?»
Нумере в анный список echo "selected**; ?»Код echo "selected”; ?»Примечание
Ненумерованный список echo "selected**; ?»
Нумере в анный список echo "selected**; ?»Код echo "selected”; ?»Примечание
</select><br>
Изображение : <input type^file name^icture_sma 11 ><br>
Увеличенное изобр-е : <input class-input type=file narne=picture_big><br>
Отображать : <input type=checkbox name=hide <?php echo $tn^>; ?»<br> cinput type=submit value=<?php echo $button; ?»<br>
<input type=hidden naiue=pos vaiue=<?php echo $_GET [ * pos1); ?»
<input type^hidden name=id__art icle value«<?php echo $_GET(1id_article *];
?»一 -
<input type^hidden name=id_paragraph value=<?php echo
$_GET [ fid_paragraphf ] ; ?»'
</form>
Структура файла addparfbrm.php из листинга 7J8 сходна со структурой фай* ла addtitlefbrni.php (см. листинг 7.16). Здесь точно также происходит анализ содержимого параметра роз и, если данный параметр не передан форме, устанавливается позиция, соответствующая последнему параграфу в статье. Сама форма состоит из текстовой области для ввода содержимого текстового блока, выпадающего списка для выбора различных стилей» двух полей типа file для загрузки изображения и его увеличенной версии, флажка для скрытия/отображения параграфа и кнопки, отправляющей переменные из формы обработчику,
( Зам/ание )
Для успешной передачи файлов от клиента серверу тег формы <form> должен содержать параметр enctype= ^ultipart/form^ciata r.
Обработчик формы расположен в файле addpar.php, содержимое которого приведено в листинге 7.19.
ГЛистинг 7Л9» Файл addpar.php
<?php
// Осуществляем соединение с базой данных require_once ("•・ /config.php11);
$strtir^ = array ();
// Проверяем поля формы на соответсвие
if (einpty($_POST [Fame Ч)) links ($_POST [1 id_article1 ],
11 Введите содержание параграфа.11);
if (empty ($_POST [1 pos * ])) 1 inks ($_POST [1 id_article'],
"He указан предадущий параграф.11);
// Определяем, скрыт параграф или нет
if($_?08T[^1661] = "on")$showhide = "show"; else $showhide = "hida";
// Заменяем одинарные кавычки обратными
$ POST[* name1] = str replace{"'"#"、",$ POST[1name1));
$picture_smair = ”七 Spicture一big ="叮 // Если поле выбора малого изображения не пустое, // копируем его из временного каталога в каталог /files
if ($__FILES[,picture_small* 1 [ tmp_name1 ] ! = "” && $_FILES [ ^icture^small * ] [ *tmp_name* ] != ”-”) {
$path == .. /files/s^.datef^YmdHis11, time ()).
strrchr ($_FILES I' picture_small * ] [1 name1),"・”)『
if (copy ($_FILES [ *picture_small」「tmp_name」,"・./". $path)) $picture_small = $path;
// Если поле выбора малого изображения не пустое, // копируем его иэ временного каталога в каталог /files
i f ($_FILES [* pic tore一big' ]「tmp_name' ] !=”"&& $__FILES [ *picture_bigr ] [ * tmp_name1) !="-") (
$path = H- ,/files/bj\date (HYmdHisMr timed ).
strrchr ($_FILES [ 'picture^big* ] Uname,),"・");
if (copy($_FILES[1 picture^big1 ] [1 tmp_name1 ],"・./”・Spath)) $picture_big = $path;
} )
// Разбиваем полученный блок текста по отдельным абзацам:
// каждая подстрока, заканчивающаяся \п, будет новым абзацем
$tok = strtok($_POST['name1],п\пп); while ($tok) {
$query = °INSERT INTO paragraphes VALUES (NULL, ■paragraph I
'S8k・,
// Картинки достаются только первому параграфу
$picture_big =
$pi сture_sma11 ="”;
if (!mysql_query ($querу))
puterror {"Ошибка при обращении к блоку статей1*);
// Выясняем id_paгаgraph только что добавленной записи
$_POST['id_paragraph1 ] = mysql_insert_id();
// Блок исправления позиции параграфа
// Для параграфа, который ссьщался на предыдущий параграф,
// изменяем значение поля роз
$ query = ,rQPDATE pa ragraphes
SET pos=r,. $_POST L id__paragraph']・”
WHERE id_article=n . $_P05T I' id_article']."
AND p8="・ $_POST - pcs」J
AND id_j)aragraph! =" . $_POST [1 id_paragraph'];
if (lmysql_query ($query))
puterror (нОшибка при обращении к блоку статей**);
$_POST [ * pcs' ] - $_POST [,id_pa га graph1 ];
$tok = —”%
}
echo "<HTMLXHEAD>
<№TA HTTP-EQUIV= • Refresh1 CONTENT=,D; URL^art.php?
id_article=**. $_POST [' id_article']."‘>
</HEADX/HTML>H ;
?>
В листинге 7.19 после установки соединения с базой данных, проверки допустимости значений полей формы и замены одинарных кавычек (‘)обрат- ыыми ( ) для параметра name происходит обработка переданных из формы изображений.
(Замечание )
В листинге 7.19 не приводится функция link (), которая полностью аналогична одноименной функции из листинга 7.17,
Перед обработкой изображений, переданных из формы, проверяется элемент массива $_files [1 picture_smali • 1 [1 tmp_name1 ], содержащий путь к сохраненному во временном каталоге сервера файлу.
Помимо пути к загруженному во временный каталог файлу, суперглобальный массив $_f工les позволяет определить другие характеристики файлов, например:
О имя 1^ $_FILES Lpicturjsmall1 ] [ *name1 ];
О размер ——$_FILES[*picture_small* 1 [ ^ize1 ];
□ тип — $_EHLES [ 'picture^small' ] [ЧуреЧ И Т. Д.
В случае если значение этого элемента не пустое, посредством функции сору () файл перемещается в директорию files Web-гфиложения CMS, а путь к файлу сохраняется в переменной $picture_smaii> которая далее используется для формирования оператора insert для добавления нового абзаца.
То же самое происходит и по отношению к параметру picture_big, ответственному за передачу увеличенного изображения.
( Замечание )
Если скрипту передается только увеличенное изображение, оно будет проигнорировано. так как для его обработки необходимо, чтобы предварительно было получено обычное изображение.
Текст абзаца перед добавлением в таблицы paragraphes разбивается на части при помощи функции StrtokO .
〔 Замечание )
Функция strtok() первый раз принимает два параметра—— разбиваемую строку и разделитель, по которому разбивается строка, все последующие разы функция вызывается с единственным параметром (разделителем), и обычно с использованием цикла while, как это продемонстрировано в листинге 7.19.
Каждая подстрока, заканчивающаяся признаком конца строки "\п* добавляется как независимый абзац. При этом если происходит передача изображения, оно разметается только в первом абзаце.
( Замечание )
При добавлении абзаца в статью можно не разбивать строку на отдельные итекстовые блоки**, а создавать абзацы в пределах одного текстового блока при формировании статьи, обрамляя каждую из подстрок в теги <р> и </р>. Недостатком такого подхода является невозможность изменения порядка следования абзацев.
В конце скрипта происходит автоматический возврат к странице редактирования статьи.
В листинге 7.20 приведено содержимое файла addanchform,php представляющего собой форму для добавления элемента статьи нссылка". Окно формы, созданной при помощи файла addahchfbrni.php, показано на рис. 7.10.
^ингЫ'Файл^ингЫ'Файл
^ингЫ'Файл
^ингЫ'Файл
<?php
〃 Устанавливаем соединение с базой данных
require^once("../config.phpM);
if (! isset ($_GET「id^article ЧН (
echo "Стать月 не выбрана";
exit();
}
// ... Код анализа переменной $_GET['роз1],
// аналогичный коду в листинге 7.18
// Выставляем переменные по умолчанию
if(!isset($button)) $button = "Добавить”;
if (! isset ($tmp)) $5p = **checked";
if (! isset ($title)) $title = "Добавить ссыпку'1;
if(!isset($action)) faction = "addanch.php"; ?>
<a href<=njavascript: history*back()н>Назад</а>
<form enctype=*multipart/form-data1 action=<?php echo faction; ?> method=post>
Название : <input type=text name-name valuer'<?php echo $name; ?>1><br> Адрес (URL) : <input type=text name=url value=r<?php echo $url; ?>* ><br> Изображение : <input type=file narne=picture_small><br> Выравнивание : <select type=text name=placegor>
<option value=0
<?php
i£( {$place=0) or (§p工ас*®].) or ($place=2)) echo "selected"; ?»Ссыпка справа
<option value 1
<?php
if (($place=3) or ($place=4) or ($place=5)) echo “selected"; ?»Ссылка слева
</select><br>
Открытие ссылки : <select type=text name=placetarget>
<option value=0
<?php
if (($place~0) or ($р1асе=3)) echo "selected";
?»0ткрыть н текущем окне
<option value=l
<?php
if (($place—1) or ($place=4)) echo "selected";
?»0ткрыть в новом окне
<option value«2
<?php
if (($place—2) or ($place=5)) echo **selected;
?»Всплывающее окно
</select>
Отображать : <input type=checkbox name=hide <?php echo $tmp; ?»<br>
<input class=button type=subnit value-<?php echo $button; ?»
<input type=hidden name=pos value=<?php echo $_GET【'pcs']; ?»
<input type^hidden name=id_article value=<? echo $_GET[* id_article1]; ?» <input type=hidden name=id_paragraph value=<?= $_GET[1id_paragraph1]; ?» </fonti>
В листинге 7.20, как и в листингах, управляющих добавлением других элементов статьи, сперва проверяется, передано ли значение первичного ключа статьи (ld_article)1 а затем проверяется параметр роз* Если значение этого параметра не установлено, оно выставляется соответствующим последнему параграфу в статье. Форма содержит текстовые поля для имени ссылки, ее адреса, поля типа file для передачи изображения, два выпадающих списка для определения положения ссылки и способа ее открытия (текущее, новое или всплывающее окно), а также флаг для скрытия или отображения параграфа.
Как и предыдущие формы, форма в листинге 7.20 настроена на повторное использование при редактировании ссылки, при этом выпадающие списки для определения местоположения ссылки и способа ее открытия кодируются одним полем place в таблице paragraph (рис. 7.16).
В листинге 7,20 показано, как происходит декодирование значения поля place. В обработчике addanch.php, код которого приведен в листинге 7.21, решается обратная задача: значения, полученные с помощью элементов управления piacegor м placetarget, кодируются одним значением place.
0
3
1
4
2
5
Открыть в текущем окне
Открыть в новом окне
Открыть во всплывающем окне
Ссылка справа Ссылка слева
Рис. 7.16. Значение поля placeв таблице paragraphes для элемента *'ссылкаи
避‘""J:-?""
:Листинг 7,21. Файл addanch.php
<?php
// Осуществляем соединение с базой данных require_once(".. /config.php'*);
// Проверяем поля формы на соответсвие
if(empty($_POST[1name1])) links($_POST[1id_article1], ^Введите ссылку・");
if( (empty($_POST[1url1]H &&
($_FILES [ * picture_small1 ] [ * tmp^name * ] — ,M, I I
$_FILES [ ^icture^small * ] [ * trnpjname • ] ^ ”-"))
links ($_POST [,id_article,],"Введите адрес (URL) или закачайте изображениеn;
// Опре дел яем, скрыт параграф или нет
if($_POST[* hide1] — "on") $showhide ^ "show”; else $showhide = "hide”;
//... Код проверки значения $_POST[1pos *], аналогичный листингу 7.19 $picture_small = n";
//.・. Код загрузки изображения, аналогичный коду из листинга 7.19
// Заменяем оданарные кавычки обратными
$_POST [ Fame1 ] = str__replace(”,”」、", $_POST [ ■ name ■]);
§_POST furl1] H str_replace(”-)“、”,$_POST [^rl1 ]);
// Кодируем составной стиль одним значением
switch ($_POST[* placetarget1]) {
case 0:
// в текущем окне
if ($_POST['placegor'] ===0) $place=0;
else $place=3;
break;
// в новом окне
if ($_POST [1 placegor ■ ] =0) $place=l;
else $place=4;
break;
/ / всплывающее окно
if ($_POST ['placegor * ] =0) $place=2;
else $place=5;
break;
default: $place^l;
)
// Формируем и осуществляем SQL-запрос
$query = "INSERT INTO paragraphes VALUES {NULL,
'anchor',
,n.$_POST[tnamet].n,r
,”.$_POST [Frl 1 'L
$place, n.$_POSTUpos" J, 1 $showhide
M.$_POST[* id^article'],");”; if (mysql_quEiry ($quai:y)) {
//,.• Код обновления позиции, аналогичный коду из листинга 7.19 echo ”<HTML>VHEAD>
<МЕТА HTTP-EQUIV^1 Refresh1 CONTENT—。; URAart.php? id_article=n. $_POST [' id__article' ] J, </HEADX/HTML>";
} else puterror ("Ошибка при обращении к блоку статей'1);
?>
Листинг 7.21 в。многом повторяет предыдущие листинги. Основным отличием здесь является кодирование двух значений от выпадающих списков формы в одно значение $place, используемое далее для формирования SQL-запроса.
И Зак 1181
В листинге 7.22 приведено содержимое файла addaiichartfbrm.php* ответственного за вывод формы добавления ссылки на новую статью.
:,"因[屮J^^y^j^^l^1 您必学Щ"1七:菖■" * .*,]:““**”「” > v.« ±,у ”] № :; :: :'j^y j^^^j**' ” ;',',*i* * V ^^^f^1^-,t^'ЛT,'т,'*,■,^^V^Л"^'*“馳•";,",,^^SS^^^y^
:Листинг 7.22. Файл addanchartform.php
工!爾•>“;;&>士士%..;•**;;、.,“;.."工;,.;;「??Z;W:JL4 ・*“!;^jt"*K •:/••:“•',:?> *讨丄7,“* ^^"Uf,・“ e**T>;'",:&^i. $li・N,X ;;;・;“;; ・;*'**/:,““!1胃*к «У.」;,*£”£:
<?php
if (!isset £$titlepage)) $titlepage:='Добавление новой статьи*;
if (!isset($helppage)) $helppage=1*;
include 11../uti 1/topadmin.php";
/ / Осуществляем соединение с базой данных
require__once(н. >/config.php");
if (! isset ($_GET [ Td^p&ge1 ]) J
{
echo "Страница не выбрана";
exit О ;
}
// * *. Код проверки значения $_GET['pcs'】,аналогичный листингу 7.18
// Выставляем значения по умолчанию
if(!isset($button)) $button = "Добавить”;
if (! isset ($title)) $ title = 0 Добавление новой статьи'1;
if (! isset ($tn¥>)) $tirp = "checked”;
if(!isset($action)) $action = '^ddanchart.php"; ?>
<a href23"javascript: history.back()п>Назад</а>
<foun action=<?php echo $action; ?> methoci=post>
Название : <input type=ntextn naxne='•name*^ value=H<?php echo $nama; ?>**>
Описание : <textarea name="descriptionn cq1s="60" rows=M2M>
<?php echo $description; ?></textarea><br>
Название(title) : <textarea name='1 html_ti11e1' cols=,l60H row岩="2”>
<?php echo $html_title; ?></textarea><br>
Описание (description) : <textarea name=,,html_descriptiontl cg1s="60" г о ws=11211 >< ?php echo $html_ciescription; ?></textarea><br>
Ключевые слова(keywords) : <textarea name=nhtnl_keywordsи cq1s="60" rows=”2"><?php echo $html_keywords; ?></textarea><br>
Отображать : <input type=checkbox name=hide <?php echo $trrp; ?»<br>
<input class=button type=,rsutmitrt value=<?php echo $button; ?»
<input type=hidden name-pos value=<?php echo $_GET [ rpos1 ]; ?»
<input type₽hidden name=posart value=<?php echo $jSET[1posart1]; ?»
<input type=hidden name^idjage value=<?php echo $_GET [' id_page' ]; ?»
<input type^hidden name^id^article
value«<?php echo $_GET['id_article1]; ?>>
</form>
Файл addatichartfbrm.php из листинга 7.22 очень похож на файл addartfbrm.php (см. листинг 7.9). Поля формы позволяют задать метаданные статьи, при этом не требуется данных для ссылки, так как URL статьи определяется однозначно, а название совпадает с названием статьи.
Значения полей передаются обработчику, расположенному в файле addanchart.php, содержимое которого приведено в листинге 7.23.
ШМ»Ы^Л1^ЫД;*; <•« V y^^・^j$M ■•■■■■*•••■ *占^^司曾工*:・《* ЯЛ*'*»*" ■ ■?■• •■■ "Л^#]*:‘ f V/** ' >'*У *' *ЧДУ ;芷資 ** *3 * * * * j >»JJ»4;•“*■•* ««у ■■■■■■■■■■■ i"#.^ >■ ■■■• ■ ■ ■•«
1нг 7.23. Файл addanchart.php
<?php
// Осуществляем соединение с базой данных require^once(и../config.php");
// Проверяем поля формы на соответсвие
if(empty($_POST[1 namef])) links($_POST[»id_page [,
^Введите название статьи.");
if(empty($_POST[fposart'])) links($_POST[1idjpager],
"Bb巳дитв позицию параграфа.");
// Заменяем одинарные кавычки обратными
$_POST ['пале' ] = stt^eplace〈…,…,$_POST [rname 4);
$_POST[description1] = str__replace("|И, "',\$_POST[description1]);
$_POST「html_title' ] = str_replace(“『“「、”,$_POST['html^title」); $_POST [' html_desc ript ion * ]=
str_replace(…,…,$_POST[1 html_description']); $_POST [f html_keywords『]=str_replace( “『”「、“,$_POST [『htmlkeywords1 ]); // Добавляем новую статью
$ query = 11 INSERT INTO articles VALUES (
NULL,
l,,.$_POST[,namel]
,”.$_POST [ •description -JI ,” • $_POST [ rhtml_title ']・”', ,"・ $_POST [1 html_description ']."*, '”・ $_POST「html_keywords ']・"', l\$_POST[,posart,].", 'hide,,
u.$_POST[,idjpage,] .°)";
if (J mysql_que ry ($ query)) puterror("Ошибка при обращении к блоку статей");
// Выясняем id_article только что добавленной статьи
$id_article ^ $_POST [ * id_article1 ];
S_POST [' id_article' ] = mysql_insert_id ();
// В новую статью добавляем заголовок, совпадающий с названием статьи
$ query = 11 INSERT INTO parag raphes VALUES (
NULL,
•title1,
'”・5_FOST「name']「I
0,
,show*,
'\ $_POST [,id^article」・")”; if(!mysql_query($query))
puterror ("Ошибка при обращении к блоку статей1*);
// Формируем URL для ссылки
$url = 1,art.php?id__article=".$__POST[ *id_article' ] .n& id_page=11. $_POST [1 id^page1 ];
// Нормируем и осуществляем SQL-запрос для добавления ссылки на
// новую статью
$query = "INSERT INTO paragraphes VALUES (
NULL, ranchor1,
,".$_POSTrname,] • ”I '$urU,
0,
,,.$_POST[,post]・", 1 $showhide'# Sid_artic工e};"; if(!mysql_query($query))
puterror(^Ошибка прм обращении к блоку статей");
//... Код обновления позиции, аналогичный коду листинга 7.19
/ / Осуществляем автоматический переход на страницу редакторования // статьи.
echo **<HTML><HEAD>
<МЕТА HTTP-EQUIV= * Refresh * CONTENT^^O; ORL^art.php? idggei • $_POST {♦ id^page' ] . "& id_article^$idL_article' >
</HEAD></HTML>fl;
Добавление ссылки на статью сводится к выполнению трех операторов insert. Первый нз них добаачяет новую статью в таблицу articles, поле hide которой имеет значение hide', что исключает появление прямой ссылки на статью с сайта.
Второй оператор insert присваивает вновь добавленной статье заголовок, совпадающий с названием статьи.
Третий оператор insert добавляет в редактируемую статью ссылку, формируемую перед этим во временной переменной $ar匕
Замечание )
Ссылка имеет аид "art. php?id_article=num&id_page-pag". В системе администрирования данная ссылка приводит к Web-редактору статьи. При отображении статей на сайте файл art.php будет ответствен за вывод статьи в браузер. Одинаковое название для файлов Web-редактора и просмотра статьи позволяет использовать одну и ту же ссылку и в системе администрирования, и при динамическом формировании ссылок на сайте.
В листинге 7.24 приведена форма (addtablefornLphp) для добавления последнего элемента статьи —— таблицы. Окно формы, созданной при помощи файла addtableform.php, показано на рис. 7.11.
*:#. .0 …у :* 寧 “ 1 ”*t畢产■ 4尸"上::,.“『у .*_.”■宀』..• • nt.-Tti»».^ . .. v . , . . 4VH«!f.» »Л ♦ **» • «.у* * * ».?» » »t •卄?” *空工2:・“”吃:二к* ••:•."::,,"::,¥?,「:**》■$ ?>子丁,讶警 1[,:£::Ш •&*: Листинг 7.24. Файл addtableform.php麦盡廳■
<?php
if(!isset(^button)) ^button = "Добавить";
if(!isset{$separator)) ^separator =";”;
if(!isset($action)) $action = Kaddtable.php";
?>
<form enctype=* multipart/form-data1
aution=У?php echo $action; ?>, method='post'>
файл : <input type=lfile1 name=1 filename1><br>
разделитель : <input type='text1 name^fseparatorf valuer*<?php echo $separator; ?>1><br>
<input type=1 submit * value=,<?php echo $button; ?>‘>
<input type=hidden name=pos value-<?php echo $_GET[1pos1]; ?»
<input type=hidden naine=id_article value«<?php echo $_GET [1 id__article1 ]; ?»
<input type=hidden name=id_paragraph value=<?php echo
$_GET [1 id__paragraph,]; ?>>~
</form>
Форма содержит поле типа file для отправки CSV-файла и текстовое поле для разделителя, в качестве которого по умолчанию выступает точка с запятой. Содержимое полей передается обработчику, расположенному в файле addtable.php, код которого приведен в листинге 7.25.
嶼*мкмшлд^曜出曙型等,中脸畴喫明Й鲤嘤燜4?адйа^聞里岁疆些喉眸维那戦學ам弊睡嶼wiiiiMiw :Листинг 7.25 Файл addtable.php
<?php
// Осуществляем соединение с базой данных
require_once《"•・ /config.php11);
// Проверяем поля форьаы на соответсвие
if (empty ($_FILES [1 filename 寸「tirp_name」))links ($_POST [1 id__article1 ] f
"Введите путь к файлу");
if (erqpty($_POST [ 'pos' ])) links ($_POST fid^article'】,
nHe указан предыдущий параграф”);
〃 Открываем переданный файл
if (! ($fp n fopen ($_FILES「filename' ] [' tmp_name,]「rb”)) J
{
echo n<р>Невозможно открыть файл.</р>'1;
exit ();
}
// Читаем содержимое файла в промежуточный буфер - $buf£er
$buffer = fread($fp, filesize ($_FILES [ filename* ] [ г1дт^_па1пе* ])};
// Закрываем файл
fclose($fp);
// Если имеются пустые позиции, заменяем их прочерком
$buffer = str_replace{$_POST['separator1].$_POST[1 separator1],
$_POST[1separator1].”-"・ $_POST[* separator1],
$buffer);
^buffer = str_replace (и\пи. $_POST [' separator* ], "\口-"・$_POST['separatorf],
$buffer);
// Заменяем прямые кавычки обратными
^buffer = str_replace("*"r ”'",$buffer);
// Разбиваем файл по строкам, каждую строку файла помещаем в отдельный
// элемент промежуточного массива $stet叩
$tok = strtok($buffer, и\п'Ч;
$strtn^[] = $tok;
while ($tok)
{
$tok = strtok(u\n");
$зЪг1дф[ ] = $tok;
}
// Начинаем формирование таблицы
Stable ="《table〉";
// Разбиваем строку по отдельньал словам, используя
// разделитель $__POST [1 separator * ]
foreach (Sstrtn^) as $value)
{
// Если строка пустая, выходим из цикла
if(trim ($value)=="")break;
// Начинаем формирование строки таблицы
$table ,= "<tr>";
$strtr(^ = strtok ($value, $_POST [' separator1 ]);
$table •= 11 <td>$ strtmp</td>u;
while ($strtmp = strtok ($_POST [ * separator * ]))
{
// Помещаем значение в ячейку
$table .= u<td>$strtmp</td>H;
}
// Завершаем формирование строки таблицу
$table .= ,,</tr>,f;
)
// Заканчиваем формирование таблицы
$table .= "</table>M;
// Вставляем б таблицу paragraphes новый элемент - таблицу
$query = nINSERT INTO paragraphes VALUES {
NULL,
table *, $ tables
О,
M.$_POST[,pos,]・%
show I
”・ $_POST [f id_article '】・”);";
if (mysql_query($query))
<
//.・. Код обновления позиции, аналогичный коду в листинге 7.19
/ / Осуществляем автоматический переход на страницу редактирования // статьи.
echo H<HTML><HEAD>
<МЕТА HTTP-EQUIV=»Refresh1 8NTENT='O; URL=art.php? id_article=".$_POST['id_article']・”,
</HEAD></HTML>";
}
else puterror(nОшибка при обращении к блоку статей");
После проверки допустимости введенных в форме значений, скрипт открывает временный файл, имя которого определяется элементом суперглобального массива $_files['filename'][『tmp_name*]» и осушествляет чтение содержимого файла в строковый буфер $buffer, после чего файл закрывается. Строки таблицы в формате CSV заканчиваются символом окончания строки "\п”, а элементы строки ——разделителем.
Замечание )
Обычно разделителем является символ табуляции, но так как этот символ часто встречается в тексте, используются различные его заместители: запятая, точка с запятой, двоеточие и т. д.
После этого при помощи функции str_repiace() все пустые элементы заменяются символом прочерка (-).
Замечание
Признаком пустого элемента таблицы являются два символа разделителя, следующие друг за другом (;;)* или разделитель в конце (; \п) или начале строки (\п;).
При помощи функции strtok() содержимое файла разбивается на строки, которые помещаются во временный массив $strtmp. Далее в цикле foreach происходит формирование строк таблицы, которая заносится в промежуточную переменную $ table: каждый элемент таблицы обрамляется HTML* тегами <td> и </td>, а сама строка HTML-тегами <tr> и </tr>. Строку $table начинает HTML-тег <tabie>, а заканчивает — </table>.
После этого осуществляется вставка новой записи в таблицу paragraphes со значением поля type, равного 'table1.
На этом рассмотрение инструментов для добавления параграфов статьи завершено. Далее будет рассмотрена система редактирования уже сушествую- ших элементов статьи. Их редактирование сводится к передаче в рассмотренные ранее HTML-формы значений из базы данных, с последующей передачей исправленных значений обработчикам edittitl.php, editanch.php, editpar.php и edittable.php, для исправления заголовка, ссылки, текстового блока и таблицы соответственно. Структура этих файлов аналогична ранее рассмотренным файлам addtitl.php, addanch.php, addpar.php и addtable.php, только оператор INSERT заменяется оператором UPDATE, кроме того, отсутствует потребность в изменении позиции параграфов, как это имело место при добавлении нового элемента статьи. Поэтому здесь будут рассмотрены только файлы edittitlfbrm.php — форма для исправления заголовка и edittitLphp ——обработчик данной формы. Код остальных файлов можно найти на прилагаемом к книге компакт-диске, а также по адресу http:// www.softtime.ru/info/downloads.plip.
Код файла ediiiiillbrn^php приведен в листинге 7.26.
渺—萨■題蹴m:—ш^к步:摯总5 .卄 吋芬? •审*茅飴靠寧号咻,,,^r^'т:^^^•,•■¥" ・,. /"吋卡岂飞竺甥约:
;Листинг 7*26. Файл ediWtlform.php以發齡总
<?php
// Осуществляем соединение с базой данных require_once("•./config.php");
if(empty($_GET[1id__paragraph1]) |I en^ty{$_GET[1id_mrticle *])) {
echo "He выбран заголовок для исправления"; exit {);
}
// Извлекаем содержимое параграфа из базы данных
$query = "SELECT * FROM paragraphes
WHERE id_paragraph=M.$_GET[1id_paragraph'];
$prg = mysql__query [$query);
if ($prg) $par = mysql_fetch_array ($prg);
else put error (11 Ошибка при обращении к блоку статей");
// Присваиваем значения переменным
$name 5s $par「name'];
$h = $par[*place*];
$po5 = $ран['ро5」;
if ($parChide1] = "hide”)$tmp 匸"”;
else 5tlф = "uheuked";
// Настраиваем форму для исправления параграфа
$title = "Исправление заголовка11;
$button = ^Исправить";
$action = Medittitl*phpH;
// Включаем форму в файл
include Haddtitlform*phpn;
?>
После соединения с базой данных, а также проверки корректности параметров id_paragraph н id_article в листинге 7.26 осуществляется извлечение записи из таблицы paragraphes для исправляемого заголовка. После настройки параметров формы в тело скрипта включается файл addtitlfornrphp, содержащий форму для редактирования заголовка. В качестве обработчика заголовка назначается файл edittitl.php, имя которого присваивается переменной faction* Содержимое данного файла приведено в листинге 7.27.
<?php
// Осуществляем соединение с базой данных require_once("*./config*phpM);
// Проверяем поля формы на соответсвие
if (empty ($_POST [' name1 ])) links ($_POST [1 id__article1 ], "Введите заголовок1*);
// Определяем, скрыт параграф или нет
if ($_Р03Т[*Ыае*] ^ ”8”)Ssho沛ide 工"show”; else $showhide = "hide";
// Заменяем одинарные кавычки обратными
$_POST [' name,] = str__replace《"*“「、",$_PQST [Fame1 ]);
// Осуществляем запрос
$query = "UPDATE paragraphes
SET named ". $_POST [Fame ♦】J 3
hide= * $showhide * #
place=,l.$_POST[,hf]."
WHERE id_paragraph=H * $_POST [' id_paragraph,];
if (mys ql__que г у ($ que г у)}
{
// Осуществляем автоматический переход на страницу редактирования
// статьи,
echo И<НТМЬ><НЕМ>
<МЕТА HTTP»EQUIV= * Refresh1 CONTENT=*0; URL=art.php? id_article=n. $__POST [ * id__article * ] . " f>
</HEAD></HTML>M;
} else puterror(^Ошибка при обращении к блоку статей");
function links($id_article,$msg)
{
echo "<p>".$msg,"</p>";
echo n<p><a href=# onClick^history.back()*>
Вернуться к правке параграфа</ах/р>";
echo "<р><& href=index.php?id_article=$id_article>
Администрирование статей</а></р>11;
exit ();
}
?>
В листинге 7.28 приведено содержимое файла delpar.php, осуществляющего удаление параграфа с первичным ключом id_paragraph, передаваемым файлу в качестве параметра.
»• •tiv*?**•*4VХ*^*'***^,"挥 1ЧУ'1" * *'
;Листинг 7.28. Файл delpar.php
<?php
// Соеданяемся с базой данных require_once(".・/config.php");
// Выясняем, чьим потомком является удаляемый параграф. Значение поля
// удаляемого параграфа заносим в переменную $роз
$query = "SELECT роз FROM paragraphes
WHERE id_paragraph=M.$_GET[1id_j)aragraph'];
$cur = mysql_querу ($query);
if ($cur) $pos = mysql_result ($cur);
else puterror(nОшибка запроса к таблице новостей.・*");
// Изменяем значение поля роз потомка удаляемого параграфа на
// значение, сохраненное в переменной $pos
$query = "UPDATE paragraph's
SET pos=H .$row [1 pos 111
WHERE id_article=,r.$_GET[1 id^article']・”
AND pcs=".$_GET「id^paragraph,];
if(!mysql_query($query))
put error (° Ошибка запроса к блоку статей11);
// Проверяем, нет ли изображений» Если есть — удаляем
$query = "SELECT * FROM paragraphes
WHERE id_j)aragraph=M.$_GET[1id_paragraph1];
$par = mysql_query ($query);
if($par)
{
$row - mysql_fetch_array ($par);
if (is_file ('\ ./".$row[ ,picture_small1 ]))
unlink(и.*/M#$row['picture^small1));
if(is_file(n../".$row[^icture^big1]))
unlinkf11. ./*\$row[Tpicture_bigI]);
J else puterror("Ошибка запроса к блоку статей");
// Выполняем уничтожение параграфа
$query = nDELETE FROM paragraphes
WHERE id_paragraph=n. $_GET [ ^djaragraph'];
if (mysql_C[Uery ($query))
// Осуществляем автоматический переход на страницу редактирования 〃 статьи.
echo "<HTMLXHEAD>
<МЕТА HTTP-EQUIV=1Refreshf CONTENT=»0; URL=art.php? id_article=n . $__GET [1 id__article」J。
</HEAD></HTML>M;
} else puterror("Ошибка при обращении к блоку статей"); ?>
Удаляя параграф из статьи, требуется сохранять ссылочную целостность параграфов в статье (рис. 7.17). После удаления параграфа с первичным ключом id_paragraph = 214 поле pos параграфа с первичным ключом id^paragraph = 142 следует исправить с 214 на 45.
После изменения поля pos у ссылающейся на удаляемый параграф записи таблицы paragraphes удаляются изображения, прикрепленные к параграфу.
Затем выполняется оператор delete, удаляющий запись с первичным ключом, передаваемым через параметр id_paragraph ИЗ таблицы paragraphes.
АА
А
А
Рассмотрим скрипты перемещения параграфа вверх и вниз. В листинге 7.29 приведено содержимое файла downpar.php, осуществля ю те го перемещение параграфа с первичным ключом, передаваемым через параметр id_paragraph на одну ПОЗИЦИЮ ВНИЗ.
*;寸 ^^ 'ЬК^УУ^^^^У^^^Ж^УМ” ■•■ у **現* "产产 W 屮"УТМ-И^ЧУ,1^^^^^^^^^^д^^^у^工必,e.yxRY^^^^^j^^^yya.^j^^gHtHya’w'y^yfjwg^jj^^H,■ •
;Листинг 7.29. Файл downpar.php
^1JT^^ .#:■£;.二 *”:*.» %*:;#士 .:?.二.*::.*..;;. “ 1*4 * 'НЮ1V + 4^7 t^Tt«. +4+* wV«C^ ^rn^WiVifi'in «^п¥/г/^rhuTi!*~ ATbY ЛГа ■ я I j!« «а *7 «'■'■'■'а а ^ в Л ■ > * * ^V« « Jo “;、“ .. .*;;・♦;.;>,. t ь ■ J
<?php
// Осуществляем соединение с базой данных require_once (n../config.php*');
// Формируем SQL-запрос на получение информации о нижнем параграфе
$ query = r, SELECT * FROM paragraphes
WHERE id_nrticle=” • $_GET J id^article' ] J AND pos=".$_GET[* id_paragraph1];
$par = mysql_query($query);
// Если на параграф с первичным ключом id_paragraph ссыпается другой // параграф, меняем их местами
if ($par && mysql_num_rows ($раг) >0)
$down =$ query 二$down =$ query 二$queryl$querylmysql_fetch_array($раг);
$down =
$ query 二
$down =
$ query 二
$queryl
$queryl
:"UPDATE paragraphes
SET id_paragraph=O
WHERE id_paragraph=" . $_GET [1 id^aragraph * ];
="UPDATE paragraphes
SET idjparagraph=n.$_GET[1id_paragraph']•“,
pos^,,.$_GET[ rposf ] .н
WHERE id_paragraph= и. $dcwn「idjparagraph1 ];
$query2 = "UPDATE paragraphes
SET idjparagraph^".$down['idjparagraph' ] .nf pos=*1. $dcwn (1 pos']・"
WHERE id_j>aragraph«On;
if (!mysql__query ($query))
puterror {г,Ошибка при обращении к блоку статей0);
else if (!mysql_query ($queryl))
puterror (^Ошибка при обращении к блоку статей1*);
else if(!mysql_query($query2)) puterror("Ошибка при обращении к блоку статейп);
)
// Осуществляем автоматический переход на страницу редактирования // статьи.
echo n<HTMLXHEAD>
<МЕТА HTTP-EQUIV='Refresh1 CONTENT=T0; URSart.php? id_article«M * $_GET [»id^article,]・'”>
</HEAD></HTML>";
В листинге 7.29 перемещение параграфов осуществляется посредством трех операторов update. При этом изменил затрагивают только первичные ключи и ссылающиеся на них поля pos (рис. 7.18). Реального перемещения текста параграфа не происходит, вместо этого нижнему параграфу присваиваются значения полей idjparagraph н pos верхнего параграфа, а верхнему параграфу —эти же значения полей нижнего параграфа. Так как значение индекса idjparagraph уникально и не допускается присвоение разным записям двух одинаковых значений, при помощи SQL-запроса, размещаемого в строке $query, происходит присвоение индексу idparagraph перемещаемого параграфа нулевого значения. После этого для записи, соответствующей нижнему параграфу, при помощи SQL-запроса в строке $queryl присваиваются сохраненные значения ld_paragraph и роз верхнего параграфа, а верхнему параграфу (SQL-запрос $ query 2)соответвенно — нижнего.
(Заменан.е )
Так, нумерация индекса ведется с единицы; присвоение нулевого значения индексу допустимо.
Результат такой замены предста&лен на рис. 7.19.
Глава 7. Система администрирования контента
323
ld_paragraph
pos
14
0
45
14
214
45
142
214
8
142
Рис. 7.18. Перемещение параграфа вниз
id_paragraph
pos
14
0
45
14
142
214
214
45
8
142
Рис. 7.19. Результат перемещения параграфов
( Замечание )
Такой способ перемещения параграфа не является единственным. Так же можно осуществить изменение трех полей роз. Так. на рис. 7.18 в этом случае можно изменить поля pos у параграфов с первичными ключами 214, 142 и 8. не изменяя при этом значений id_paragraph.
Файл uppar.php во всем аналогичен файлу downpar.php, приведенному в листинге 7.29, за исключением первого запроса select, в котором извлекается информация не о нижнем, а о верхнем параграфе. Запрос имеет следующий вид:
SELECT * FROM paragraphes
WHERE id_article="・$_GET['id_article']•"
AND id_paragraph="•$_GET['pos'];
В листинге 7.30 приведено содержимое файла hidepar.php, ответственного за сокрытие параграфа с первичным ключом id__paragraph, передаваемым ему в качестве параметра.
,^^Й?避變賢^、箏墜祖冷W.滥%?1卢"'学吋炉^磔1*唯酱ti管押送”*:"汽绊哲亨„".咨笠隔W空".兜■可颦■^^^,
! Листинг 7.30. Файл hidepar.php
<?php
// Соединяемся с базой данных
require_once./config.php");
// Формируем SQL*'запрос на сокрытие параграфа
$query = "UPDATE paragraphes SET hide=thide,
WHERE id_paiagraph=rr. $_GET [1 id_paragraph t ];
// Выполняем SQL-запрос
if(mysql_query{卒query))
{
// В случае успеха возвращаемся к правке статьи
echo "VHTMIXHEAD〉
<МЕТА HTTP-EQUIV=1Refresh• 8NTENT=,0; URL=art.php?
id_article=".$_GET[* id_article']."‘>
</HEADX/HTML>";
} else puterror (11 Ошибка при обращении к блоку статей");
Файл showpar.php аналогичен по содержанию файлу hidepar.php, за исключением оператора update, который принимает следующий вид:
UPDATE paragraphes SET hide=‘shQW‘
WHERE id_pa工agraph="•$_GET[1id_paragraph ‘];
На этом разработка панели администрирования системы CMS закончена. Далее будет рассмотрен движок отображения статей на сайте. Он состоит всего из двух файлов, расположенных в подкаталоге article:
口 index.php — обеспечивает навигацию по статьям;
□ art.php — обеспечивает отображение статьи на сайте.
Файл index.php, выводящий в окно браузера список групп статей н список статей, принадлежащих отдельной группе, представлен в листинге 7.31.
,■: •・.、;:““ .V” •・“•“ “丁・ ”•;“”“'. 1*■“普71"“ ♦、、”,■: •・.、;:““ .V” •・“•“ “丁・ ”•;“”“'. 1*■“普71"“ ♦、、”а*»* «jMA^fi >,2£,. >, .^.-«^«1,,».,, р »“.*“”:.,: у,”,,;** t,l咨晔喇jgj卓时戰а姿jg电要д:,.” 4"v ••• "У:二"NT ?u ,:WW7*"¥ **«;f й^4£ 什炉:”:•竇丐幫暁‘;"2科S
,■: •・.、;:““ .V” •・“•“ “丁・ ”•;“”“'. 1*■“普71"“ ♦、、”
,■: •・.、;:““ .V” •・“•“ “丁・ ”•;“”“'. 1*■“普71"“ ♦、、”
Листинг 7.31. Файл index.php
<?php
// Соединяемся с базой данных
require_once ("・./config. php11);
// Если не указана группа статей, путем передачи
// номера группы через параметр id3age выводим список групп
if{!isset($_GET[т id_pager]))
// Производим выборку всех групп статей
$query = мSELECT * FROM artpage ORDER BY name";
$pgs = mysql_query($pgs);
// Если запрос выполнен успешно и число
// групп больше 0, выводим список групп
if ($pgs && @mysql_nuii^_row ($pgs) > 0) (
echo "<q1>";
while($page = mysql_fetch_array($pgs)}
echo "<Li>va href=http: //11. $ SERVER [nSERVER NAMEn J * $PHP SELF. n? id_page^n, $page [' id_artpage1 ] •">
.$page[Fame" *”</a>";
}
echo "</0工>";
}:
}
// Если номер группы передан через параметр id_page, работаем
// с данной группой
else
{
// Если не указана статьяf путем передачи
// номера статьи через параметр id__page выводим список статей
// текущей группы
if (! isset £$_GET [ ^d article* ]))
// Выбираем статьи этой группы с порядковым номером id_page
$query = "SELECT + FROM articles
WHERE idjags”・$_GET[fid^page」J
AND hide=1 show1
ORDER BY name";
$art = mysql_query($query);
if (! $art) put error ("Ошибка при обращении к блоку статей1');
// Смотрим# сколько статей на странице
// Если одна, просто выводим статью
i f (mysql_nuni_rows ($акt) =^= 1)
{
$articles = mysql__fetch_array ($art);
$_GET[1id_article'] = $articles['id_article1];
include "art.php";
}
// Если больше одной - выводим список статей
else
(
echo "(01>";
while particles = mysql_fetch__array ($art)) {
echo "CiX£ href-http: 〃”. $_SERVER[nSERVER^NAMEH] .$PHP_SELF. ”? id__article=H. $articles [ *id_article * J ・”>
"・ $articles['name']."</a><br>".
$articles['description*];
}
echo "v/ol>";
}
}
// если id_article статьи указан# то выводим ее на странице
else
(
include **art. php° ;
В листинге 7.31 после соединения с базой данных происходит проверка переданного странице параметра 土djage, содержащего первичный ключ группы статей. В том случае, если данный параметр не установлен, производится вывод списка доступных групп статей. Имя каждой группы является ссылкой, передающей параметр id_j>age.
Отдельного рассмотрения заслуживает адрес ссылки. Файл index.php предназначен как для непосредственной ссылки по адресу http://www.site.ru/ article/index.php, так и для включения в другие файлы, как это продемонстрировано в листинге 7.32.
Листинг 7.32. Включение файла Index.php в другие файлы
<?php
// Верхнее оформление страницы include narticle/index*php11;
〃 Нижнее оформление страницы
?>
При таком подходе имена файлов, отображающие статьи, будут разными. В листинге 7Л эта проблема решается путем формирования URL из имени сервера $__server[^server^ame"] и текущей страницы §php_self. Таким образом, URL ссылок динамически изменяются при смене имени файла. Автоматическое определение имени сервера при помощи элемента супер* глобального массива $_server [ •'server_nameп ] позволяет не заботиться об именах ссылок при отладке Web-приложения на локальном хосте и при его работе на реальном сайте.
При переходе посетителя по одной из ссылок в списке групп статей файл index.php получает установленный параметр id_page, равный первичному ключу группы статей. Это заставляет программу следовать в первый блок else, где производится проверка параметра id_articie, если данный параметр не установлен — выводится список статей текущей группы. Если в группе находится только одна статья, список не выводится, а выводится сама статья путем включения файла art.php, ответственного за вывод статьи в окно браузера. То же самое происходит при передаче скрипту файла index.php параметра id_articie с первичным ключом конкретной статьи. В листинге 7.33 приведено содержимое файла art.php, ответственного за отображение статьи.
1стинг 7.33. Файл1стинг 7.33. Файл
1стинг 7.33. Файл
1стинг 7.33. Файл
<?php
// Соединяемся с базой данных require_once("../config.php");
// Вызываем функцию оторбажения статьи в окно браузера
putarticle($_GET[*id_article1], 0, 0,"");
// Рекурсивная функция putarticle() выводит статью в окно браузера
// Параметры:
// $pos - позиция выводамого параграфа, при первом вызове
// всегда следует указывать 0.
// $id_article - первичный ключ записи таблицы articles,
// соответствующей выводимой статье.
// $listbegin - вспомогательный параметр, дозволяющий отслеживать вывод // списков, равенство флага 1 или 2 означает, что следующий выводимый // параграф является элементом списка» При первом вызове следует // всегда указывать 0.
// $begin_list - вспомогательный параметр, передающий строку с тегом
// начала списка * При первом вызове следует всегда указывать // пустую строку.
function putarticle($id_article, Spos, $listbeginr $begin_list)
$query - "SELECT * FROM paragraphes
WHERE id_article=$id__article AND pos=$pos;H;
$par ^ mysqlquery($query);
if($par && mysql_num rows($par)>0)
^paragraph = mysql_fetch_array($par);
// Закрытие списка
$end_list = ””;
if($listbegin == 1)
{
if ($paragraph [' type1 ] —1 | [
($paragraph['place*] != 3 && $paragraph['type *]=^0})
{
Send list ^ n</ul>H;
$listbegin = 0;
}
if($listbegin — 2)
{
if ($paragraph [1 type' ] ~1 | |
($paragraph[* place1] != 4 && $paragraph[* type1]=0))
$end__list ^ "</o」>”;
^listbegin = 0;
}
// Определяем тип элемента (заголовок, параграф или ссылка) switch(^paragraph「type1])
case i:
// Заголовок;
// Назначаем тегам hl, h2, h3 и т.п. класс стилевой таблицы
if ($paragraph [1 place1 ] = 1) if($paragraph「place'] = 2) if($paragraph L place11 一 3) i f($parag raph[* place1) = 4)
$cls = нс1азз=аг^ап1ерадеп;
$cls = ,rclass=artblQckpagen;
$cls = tfclass=artzagri;
$cls ^ Mclas3=artnamelisting,1;
if(^paragraph[*place1] == 5) $cls = "class=artnametable";
if($paragraph(1place *]工= 6) $cls = ”cl&33=&rtnRnenQte";
$strname = "$end_list<h,f.$paragraph['place']・"$cls>H.
$paragraph[1name,]
."</h".$paragraph[1place1].">";
break;
case 0:
// Абзац;
// Изображение
$ image -"";
if ($paragraph(*picture_small'] != ”” && ^paragraph[1picture_sroall1] != "-" && filesize ($paragraph [ Tpicture_srnall']))
$ image = "<img src=u.^paragraph[*рхсЪиге_зта11*].
” align=right class=img horder=0>M;
if (^paragraph[^icture^big*] 1= "" &&
^paragraph [ ^icture^big1) != "-” && filesize ($paragraph[ ^icturejoig1 ])) {
$ image = ”<inig 5гс=п. $paragraph. (* picture_small * ] - *' border=0>
<a class=link href=\"javascript: VQid(0);\"
OnClick=\Hshowpicture(*"<$paragraph[^icture^big1]."1, * 1)\"> Большое фото</а>и;
}
// Тип абзаца
switch£$paragraph「place,])
case 0: // Обычный
$strname = H$end_list<p class=arttext>
$ image*'. ^paragraph [f name1 ] . "v/p>\n"; break;
case 7: // с отступом
$stimame = "$end_list<p class=arttextindent>
$ image". ^paragraph [ ^aine1 ] . n</p>\nM;
break;
case 1: 〃 Жирный
$strname = H$end_list<p class=arttext>
$image<b>".$paragraph['name*).H</b></p>\nH; break;
сазе 2: 〃 Курсивный
$strname = ,t$end_list<p class=arttext>
$image<i>n*$paragraph[ ^name1 ]. 1г</1х/р>\пг,;
break;
// Ненумерованный список
$begin_list 匸"”;
if($listbegin == 0)
(
$begin_list = "<ul>";
$listbegin = 1;
・}
$strname = H$end_list $begin_list<li><p class=litext>M.
str_replace (M\nn, n<li><p class=litext>'1, $paragraph [ ^ame1 ])
・”</p>\n";
break;
〃 Нумерованный список
$begin_list ="”;
// Открытие списка
if($listbegin = 0)
{
$begin_list = "<◎工〉,
$listbegin = 2;
}
$strname = 4$end_list $begin^list<1i><p class=litext>".
str_replace{"\n", "<li><p class=litext>u,$paragraph[Fame*]) /•</p>\nM;
break;
// Подсвечиваем код
$strname = и<р class=artcode>n.
highlight_string(^paragraph[*name'], true)."</p>"; break;
// Примечание
$strname = n$end_list<p class=artnote>$imageH <
$paragraph['name'].”</p>\n”;
break;
break;
// Ссылка;
if($listbegin) $str_list = "<li>";
else $str_list * "”; // Расставляем стили
switch($paragraph[1place,])
case 0; 〃 Правая ссылка, всплывающее окно $pstyle«nclass=artlink_rN;
$ target*и href=". $paragraph [1 url1 ] •ri "; break;
// Правая ссыпка, новое окно $pstуle^nclas s=a rt1ink_r M;
$target=" target^_blank href=H.^paragraph【'url*].11 "; break;
// Правая ссылка, всплывающее окно $pstyle^,1class=artlink_r,,;
^target-** href=\Mjavascript: void(0) ;\"
OnClick=\ Hshowpicture Г ". $ para graph [' url1 ] > м 4 * *) \ и r,; break;
// Левая ссьспка, текущее окно $pstyle^**claяз^artlink_^l,^;
$target=n href=и. $ paragraph ['url * ].M 11;
break;
〃 Левая ссылка, новое окно $pstyle="class=artlink_lH;
$target=" target=_blank href₽M * $paragraph[*url']." ”; break;
// Левая ссылка, всплывающее окно $pstylee,,class=artlink_ln;
$ target-11 href=\M javascript: void(0) ;\n
OnClick«\u3howpicture(,H.$paragraph[»url4 -H,r 1 *)\IM,; break;
}
$ strname = ,,$str_list<p $pstyle >
<a class=link ^target href®'1. $paragraph [f url1 ]. H> ".$paragraph[ 'name' ].,1</a></p>11;
break;
// Таблица;
$strname = M$end_list"* $paragraph[T name *]・”《br>";
break;
// Вывод параграфа
if ($paragraph[•hide*]==0) echo $strname;
// Рекурсивно вызываем функцию putarticle() putarticle($id_article,
$paragraph[1id_paragraph,], $listbegin# $begin_list);
Листинг 7.33 начинается с вызова рекурсивной функции putarticle О, выводящей статью в окно браузера. Оставшуюся часть файла занимает сама функция, которая принимает 4 параметра:
口 $id__articie —— первичный ключ записи таблицы articles, соответствующей выводимой статье;
$pos — позиция выводимого параграфа, при первом вызове всегда следует указывать 0;
$iistbegin ——вспомогательный параметр, позволяющий отслеживать вывод списоков, если равенство флага 1 или 2 означает, что следующий выводимый параграф является элементом списка. При первом вызове следует всегда указывать 0;
$begin_iist — вспомогательный параметр, передающий строку с тегом начала списка. При первом вызове следует всегда указывать пустую строку.
При первом вызове функции только первый параметр является обязательным, остальные параметры принимают нулевые занчения, их задачей является передача различной вспомогательной информации во время рекурсивного спуска.
В функции putarticle о происходит формирование одного параграфа статьи ——аналогично файлу art.php (см. листинг 7.15)—— путем анализа информации, полученной из таблицы paragraphes, во вложенной конструкции switch, с последующим рекурсивным вызовом в конце функции для вывода оставшихся параграфов.
〔 Замечание )
Для более эффективной работы Web-приложения имеет смысл после редакции статьи сохранять ее в статическом HTML-файле. Это позволит избежать лишней нагрузки на базу данных и повысит надежность ресурса, так как он не будет зависеть от работоспособности сервера MySQL.
Универсальный каталог продукции
Каталогом продукции будем называть многоуровневый каталог продукции или услуг. Продукцию или услуги мы будем называть товарными позициями. Характер продукции не имеет значения. Она описывается набором полей в базе данных, в которые будет заноситься формализованная информация о продукте.
Например, для каталога автомобилей это могут быть поля:
Марка автомобиля;
口 Модель;
口 Тип кузова;
口 Грузоподъемность;
口 Цена.
Для риэлтерских услуг набор полей базы данных может быть следующим:
Количество комнат;
Район;
口 Адрес;
Площадь;
口 Этаж.
Характер продукции определяется только набором полей в базе данных, а логика работы каталога не зависит от типа продукции. Потому данный каталог можно назвать универсальным. Для перевода каталога с одного типа продукции на другой необходима лишь корректировка, связанная с количе* ством н типами обрабатываемых полей, н возможно — корректировка дизайна.
Товарные позиции объединены в группы, которые мы будем называть под- катЕшогами, которые, в свою очередь, могут содержать другие подкаталоги. Таким образом, структура каталога может быть многоуровневой, а количест-
во уровней программно не ограничено. Структура каталога представляет собой дерево, узлами которого являются группы продукции.
Но как бы ни был привлекателен многоуровневый каталог продукции, его неограниченная расширяемость имеет свои минусы. При разработке Web- приложений очень часто искусственно ограничивают количество уровней каталога. Это связано со следующими прнчннамн*
Одним из требований при разработке Web-интерфейсов является ограничение глубины ссылок на сайте 3 уровнями. То есть чтобы попасть в любое место сайта, пользователь должен совершить максимум 3 перехода по ссылкам. Если это требование применять к каталогу, то количество уро в- ней каталога сократится до 2, так как первым уровнем обычно является главная страница сайта.
口 Ограничение количества уровней делает каталог более предсказуемым и потому более легким для восприятия. В любом случае фиксированное максимальное количество уровней устраняет неоднозначность. Знание максимального количества уровней помогает пользователю предсказать объем каталога и не потеряться в нем, на любом его уровне.
Очень редко встречаются задачи создания Web-приложений, когда необходим действительно многоуровневый универсальный каталог. Каталог специально адаптированный под конкретный вид продукции, зачастую выглядит гораздо выигрышнее универсального.
Однако это не значит, что многоуровневый каталог не нужен. К проектированию каждого каталога следует подходить индивидуально, исходя из предполагаемого объема информации в каталоге и задач» которые на него возложены.
Проектируя каталог, помните также о максимальном количестве элементов, воспринимаемых человеком. Считается, что оно равно 7—9 элементам. Это значит, что нужно так спроектировать каталог, чтобы на одном уровне располагалось не более 7—9 групп продукции (рис. 8.1).
Груп пе^АВТОМОБИЛИ-
ВАЗ \
КЗ 1
ПАЗ 〔
УАЗ\
лжз А 7—9 элементов
МАЗ /
КАМАЗ I
L3Mn
Рис. 8.1. Рекомендуемое количество элементов в группе
Работа с каталогом в режиме пользователя
Рассмотрим внешний вид каталога на примере риэлтерского каталога квартир» позволяющего посетителю ресурса просматривать список предлагаемых квартир.
Внешний вид каталога представлен на рнс. 8.2. В левой части расположено навигационное меню каталога, а правую часть занимает основной информационный блок, где будет отображаться список продукции, в данном случае список квартир.
Рис. 82 Каталог продукции
Навигация по каталогу осуществляется с помощью ссылок в навигационном меню* Переход к подкаталогу приводит к отображению его содержимого в правой части страницы, и если в нем существуют подкаталоги, то они выводятся под названием выбранной группы в виде списка.
Описание квартир из выбранной группы выводится в виде таблицы в правой части страницы. Столбцы таблицы представляют собой поля базы данных, описывающие квартиру:
口 Кол. комн. — количество комнат;
口 Район — район;
口 Адрес — адрес;
口 Площадь (О)—— плошадь общая;
口 Площадь (Ж) — площадь жилая;
口 Площадь (К) — площадь кухни;
口 Эт. — этаж, на котором расположена квартира;
Эт. дома — этажность дома;
口 Хар. — материал, из которого построен дом (кирпичный, панельный и т. п.);
口 С/У — наличие санузла;
Л/Б — наличие лоджии или балкона;
口 Цена, м. кн. — цена за квадратный метр;
口 Цена, общ. — общая цена квартиры;
Валюта — валюта, в которой указана цена;
口 Прим. — примечание.
Содержимое этой таблицы можно сортировать по содержимому столбцов. Название столбца, где возможна сортировка, оформлено в виде ссылки. Переход по ней приводит к сортировке таблицы квартир в выбранном подкаталоге. Первый щелчок на названии столбца сортирует таблицу по убыванию в данном столбце. Повторный щелчок на этом же столбце сортирует таблицу по возрастанию данных столбца.
В этой главе рассмотрен реально действу щи й каталог риэлтерских услуг. Поскольку авторы проживают в Нижнем Новгороде, в каталоге приведены названия улиц и районов этого города. Для адаптации каталога для другого города нпн района части кода нужно изменить в соответствии с необходимыми названиями.
Работа с каталогом
в режиме администратора
Определим требования к каталогу, которые должны быть реализованы в системе адм и н истр и ро ва н ия. Так как мы рассматриваем каталог риэлтерских услуг, то подкаталог товарных позиций будем называть подкаталогом квартир, а саму товарную позицию —— квартирой. Каталог риэлтерских услуг позволяет:
口 создавать группы квартир;
口 удалять группы квартир и квартиры, которые входят в удаляемую группу из каталога;
добавлять квартиру в каталог с помощью формы добавления продукции (ручной режим);
口 добавлять квартиру в каталог путем импорта данных из CSV-файла (автоматический режим);
редактировать информацию о квартире;
удалять квартиру из каталога;
口 временно скрывать или отображать подкаталог квартир;
О временно скрывать или отображать квартиру.
Рассмотрим, работу с каталогом в режиме администратора. Главная страница адми н ИСТ11И ро ван и я каталога содержит таблицу с подкаталогами верхнего уровня, их описанием и ссылками для редактирования (рис. 8.3).
Рис. 8.3* Главная страница администрирования каталога
Рассмотрим содержание таблицы:
口 в первом столбце находится название подкаталога в виде ссылки, переход по которой приводит на нижележаший уровень каталога;
второй столбец содержит описание подкаталога;
третий столбец содержит позицию подкаталога относительно других, с ее помощью можно определить порядок вывода подкаталогов на сайте. Подкаталоги квартир выводятся по возрастанию значения этого паля;
в четвертом столбце содержится список действий над группой:
Скрыть/Отобразигь — временно скрывает млн отображает данный подкаталог на сайте;
Редактировать — открывает страницу редактирования названия подкаталога квартнр и его описания (рис. 8.4);
Удалить — удаляет подкаталог и квартиры, которые в него входят.
Рис. 8.4* Страница редактирования названия группы квартир и ее описание
Чтобы добавить новую группу квартнр в базу данных, следует перейти по ссылке Добавить подкаталог (см. рис. 8.3). Открывшаяся страница редактирования полностью аналогична странице, приведенной на рис. 8.4. Изменив необходимые поля и нажав кнопку Добавить, можно добавить новый подкаталог.
Для того чтобы добавить подкаталог или товарную позицию в другом подкаталоге, следует щелкнуть по названию группы, оформленной в виде ссылки, в первом столбце таблицы. Описание квартир выбранного каталога будет выведено в виде таблицы, расположенной ниже таблицы подкаталогов (рис. 8.5).
Рис. 8,5. Квартиры выбранной группы
Квартиры, отображаемые на странице, относятся именно к текущему подкаталогу "Квартнрн". Подкаталоги пЭлитные квартиры*' и ^Обычные квартиры" могут иметь свои собственные наборы кнартнр, которые можно редактировать после смены текущего каталога на один из них.
Поля таблицы квартир (см. рис. 8.5) представляют собой формализованное описание квартир. Последний столбец содержит ссылки для их редактирования:
口 Скрыть/Отобразить ——управляет выводом квартиры на сайте;
□□Исправить — открывает страницу редактирования информации на квар тиру (см. рис. 8.8);
□
□
Удалить — удаляет квартиру из базы.
Вв口HYfbtM НН ;疝Вв口HYfbtM НН ;疝I Добовлсже кшртмры ■ Mkrowjft Intone! Expioffr一 ВИВI Добовлсже кшртмры ■ Mkrowjft Intone! Expioffr一 ВИВ型*" 3»е @ид 唾бражоь С^мие £npawe型*" 3»е @ид 唾бражоь С^мие £npaweДобавление квартирыДобавление квартирырлнвнI Квнавйнсккй _▼]лдрес|М, Янскаяпп(3(74nn(MQ|35пп09[10~КОЛ ВО КОМН4ТЕ"三IрлнвнI Квнавйнсккй _▼]лдрес|М, Янскаяпп(3(74nn(MQ|35пп09[10~КОЛ ВО КОМН4ТЕ"三I)ыжн.домл|12мятериап пома |Км1рпичнь1й д] ели, уз@п(совмещенный .^JbakciiyI Валком 司цена|1200000мена м.кн.卜州叫8апин “叵丽三]прныечдцнеП01ЦЦ11Я|1Огобрлжлть 审Исправить I)ыжн.домл|12мятериап пома |Км1рпичнь1й д] ели, уз@п(совмещенный .^JbakciiyI Валком 司цена|1200000мена м.кн.卜州叫8апин “叵丽三]прныечдцнеП01ЦЦ11Я|1Огобрлжлть 审Исправить I
Вв口HYfbtM НН ;疝
Вв口HYfbtM НН ;疝
I Добовлсже кшртмры ■ Mkrowjft Intone! Expioffr一 ВИВ
I Добовлсже кшртмры ■ Mkrowjft Intone! Expioffr一 ВИВ
型*" 3»е @ид 唾бражоь С^мие £npawe
型*" 3»е @ид 唾бражоь С^мие £npawe
Добавление квартиры
Добавление квартиры
рлнвнI Квнавйнсккй _▼]
лдрес|М, Янская
пп(3(74
nn(MQ|35
пп09[10~
КОЛ ВО КОМН4ТЕ"三I
рлнвнI Квнавйнсккй _▼]
лдрес|М, Янская
пп(3(74
nn(MQ|35
пп09[10~
КОЛ ВО КОМН4ТЕ"三I
)ыжн.домл|12
мятериап пома |Км1рпичнь1й д] ели, уз@п(совмещенный .^J
bakciiyI Валком 司
цена|1200000
мена м.кн.卜州叫
8апин “叵丽三]
прныечдцне
П01ЦЦ11Я|1
Огобрлжлть 审
Исправить I
)ыжн.домл|12
мятериап пома |Км1рпичнь1й д] ели, уз@п(совмещенный .^J
bakciiyI Валком 司
цена|1200000
мена м.кн.卜州叫
8апин “叵丽三]
прныечдцне
П01ЦЦ11Я|1
Огобрлжлть 审
Исправить I
Выше таблицы квартир (рис. 8.5) расположены ссылки для добавления новых квартир в подкаталог.
Ссылка Добавить квартиру открывает страницу добавления отдельной квартиры. Она аналогична странице редактирования существующей квартиры (рис. 8.6), но все поля формы добавления пусты и требуют заполнения.
После заполнения необходимых полей и нажатия кнопки Добавить информация о квартире добавляется в выбранный подкаталог.
Разработка каталога "Риэлторские услуги"
Для построения каталога услуг необходимы две таблицы в базе данных MySQL
П catalog ——таблица, предназначенная для хранения структуры подкаталогов;
П product ——таблица для хранения товарных позиций, в случае каталога "Риэлторские услуги" это будут квартиры, выставленные на продажу.
Таблица catalog включает 6 полей:
口 id_catalog ——первичный ключ таблицы;
□ name — название подкаталога;
口 descrition — описание подкаталога;
口 роз ——поле, определяющее позицию каталога относительно других при выводе списка каталогов в окне браузера;
口 hide ——поле типа enum, принимающее два значения *show* и 'hide' н определяющее доступность каталога для просмотра посетителями. По умолчанию поле принимает значение 'sho", что соответствует состоянию доступности каталога для просмотра посетителями;
id_parent — поле, принимающее значение первичного ключа другого каталога, который является родительским для данного подкаталога.
Поле id parent позволяет организовывать многоуровневое вложение каталогов. Значение данного поля равно 0 для каталогов, расположенных в корневом каталоге, л равно значению первичного ключа каталога, по отношению к которому они являются подкаталогами. Оператор create, выполняющий создание данной таблицы, приведен в листинге 8」.
]Листинг 8.1. Создание таблицы catalog
・・•••♦♦'♦♦♦••♦•♦•♦♦♦♦♦,••♦♦♦♦♦•••♦、•・♦」,;•?• ;S;・Y・ i?・%;^ft,・V・・•.禽■汗・■、:':“.“ +
CREATE TABLE catalog {
id catalog INT(8) NOT NULL AUTO_INCREMENT,
12 Зак. 1181
name TINYTEXT NOT NULL,
description TINYTEXT NOT NULL, pos SMALL工NT⑶ NOT NULL default *口\ hide ENUMCshow1, ^ide') NOT NULL DEFAULT 'show', id^parent 工NT(8) NOT NULL DEFAULT 『0', PRIMARY KEY (id_catalog), KEY id_parent (id^parent) )TYPE=MyISAM;
Таблица product состоит нз 19 полей:
id_product — первичный ключ таблицы;
note — краткое замечание;
district ——поле для хранения информации о районе, где расположена квартира; имеет тип enum и принимает одно из 8 значений, по числу районов города;
口 address — поле для хранения адреса;
square_o — общая площадь квартиры;
squarej —— жилая площадь квартиры;
口 square_k — площадь всех комнат;
rooms — количество комнат;
floor — этаж;
口 floorhouse — этажность дома;
material ——материал дома, поле имеет тип enum и принимает 3 значения: 'brick', 'concrete', 'reconcrete* ДЛЯ КИрПИЧНЫХ, панельных И МОНОЛИТНЫХ домов соответственно;
su — поле, характеризующее санузел квартиры; имеет тип enum и принимает 2 значения: 'separate' и 'combined^ для раздельного и совмещенного санузла соответственно;
balcony — поле, характеризующее тип балкона; имеет тип enum и принимает 2 значения: 'balcony' и 'loggia' для балкона и лоджии соответственно;
price ——целочисленное поле для обшей цены квартиры;
口 pricemeter — целочисленное поле для цены за квадратный метр;
currency — поле ДЛЯ валюты, используемой В полях price И pricemeter; имеет тип enum и принимает 2 значения: 'rur' и 'usd' для рублей н долларов соответственно;
口 hide — поле типа enum, принимающее 2 значения show' и 'hide' и определяющее доступность товарной позиции для просмотра посетителями. По умолчанию поле принимает значение 'show', что соответствует состоянию доступности товарной позиции для просмотра посетителями;
О роз — поле, определяющее положение товарной позиции в списке при его выводе в окно браузера;
口 id_cataiog ——поле, определяющее принадлежность товарной позиции к каталогу.
Оператор create, выполняющий создание данной таблицы, приведен в листинге 8.2.
1^^tЙИД^^Е^^Й^%^*й%^宦肥翌辦^¥?f举骷1炉"彥t"喀图图■'昔脸***'*^卿醐舒干册^"整吨货"1'*'"
.;;*附;> ■ ■ ■ >^« ■ ■ ■?il\t>"f>» Л* I『ih AhtL,I.;;*附;> ■ ■ ■ >^« ■ ■ ■?il\t>"f>» Л* I『ih AhtL,IЛистинг В,2・ Создание таблицы product
.;;*附;> ■ ■ ■ >^« ■ ■ ■?il\t>"f>» Л* I『ih AhtL,I
.;;*附;> ■ ■ ■ >^« ■ ■ ■?il\t>"f>» Л* I『ih AhtL,I
>i<>ava'ilia ■ a rt^f н < rt»
CREATE TABLE product (
id_product INT(ll) NOT NULL AUTO一工NCREMENT,
note TINYTEXT NOT NULL,
district ENUM(1kanavinskii *,
*nizhegorodskii I 1sovetskii', 'priokskii', ,moskovskii I ,avtozavodskii1,
'leninskii1,
^omovskii1) NOT NULL default 1 kanavinskii1 r
address TINYTEXT NOT square^ SMALL工NT ⑹ squarej SMALLINT(6) square_k SMALL工NT ⑹ rooms TINYINT(4) NOT floor TINYINT(4) NOTaddress TINYTEXT NOT square^ SMALL工NT ⑹ squarej SMALLINT(6) square_k SMALL工NT ⑹ rooms TINYINT(4) NOT floor TINYINT(4) NOTNULL, NOT NULLNULL, NOT NULLNOT NULLNOT NULLNOT NULLNOT NULLNULLNULLNULLNULLfloorhouse TINYINT(4) NOT material ENUM('brick1,'concrete1,floorhouse TINYINT(4) NOT material ENUM('brick1,'concrete1,DEFAULTDEFAULTDEFAULTDEFAULTDEFAULTDEFAULT'01 'O', ‘01'01 'O', ‘01DEFAULT O, DEFAULT ‘03 NULL DEFAULT 'O',DEFAULT O, DEFAULT ‘03 NULL DEFAULT 'O',
address TINYTEXT NOT square^ SMALL工NT ⑹ squarej SMALLINT(6) square_k SMALL工NT ⑹ rooms TINYINT(4) NOT floor TINYINT(4) NOT
address TINYTEXT NOT square^ SMALL工NT ⑹ squarej SMALLINT(6) square_k SMALL工NT ⑹ rooms TINYINT(4) NOT floor TINYINT(4) NOT
NULL, NOT NULL
NULL, NOT NULL
NOT NULL
NOT NULL
NOT NULL
NOT NULL
NULL
NULL
NULL
NULL
floorhouse TINYINT(4) NOT material ENUM('brick1,
'concrete1,
floorhouse TINYINT(4) NOT material ENUM('brick1,
'concrete1,
DEFAULT
DEFAULT
DEFAULT
DEFAULT
DEFAULT
DEFAULT
'01 'O', ‘01
'01 'O', ‘01
DEFAULT O, DEFAULT ‘03 NULL DEFAULT 'O',
DEFAULT O, DEFAULT ‘03 NULL DEFAULT 'O',
^econcrete1) NOT NULL DEFAULT 1brick', su ENUM(Separate *,1combined *) NOT NULL DEFAULT 'separate', balcony ENUM(1balcony'#1loggia') NOT NULL DEFAULT 'balcony I price INT(10) NOT NULL DEFAULT 'O', pricemeter INT(10) NOT NULL DEFAULT *0', currency ENUM(fRUR','USD') NOT NULL DEFAULT 'RUR',
hide ENUM(『show'Jhide*) NOT NULL DEFAULT ^how*, pos INT(ll) NOT NULL DEFAULT 'O’, id^catalog INT(8) NOT NULL DEFAULT 'O', PRIMARY KEY (id^product), KEY id_catalog (id_catalog)
)TYPE=MyISAM;
Разработка каталога, так же как и системы CMS, будет начата с системы админинистрирования, которая включает следующие файлы:
addcatfbrm.php — HTML-форма для добавления нового подкаталога;
addcal.php — скрипт, позволяющий добавить новый подкаталог (обработчик формы addcatfbrm.pbp);
口 delcai.php — скрипт, ответственный за удаление каталога из системы;
口 hidecat.php — скрипт, ответственный за скрытие каталога;
showcat.php — скрипт, ответственный за отображение каталога;
口 editcatfbrm.php ——HTML-форма для редактирования параметров каталога;
口 editcat,php ——скрипт, ответственный за редактирование параметров каталога (обработчик формы editcatform.php);
addprodfbrnrphp ——HTML-форма для добавления новой товарной позиции;
addprod.php —— скрипт, позволяющий добавить новую товарную позицию (обработчик формы addprodlbmi.php);
口 delprod.php — скрипт, ответственный за удаление товарной позиции из системы;
editprodform.php — HTML-форма для редактирования параметров товарной позиции;
口 edilprod.php ——скрипт, ответственный за редактирование параметров товарной позиции (обработчик формы editcatform.php);
hideprod.php — скрипт, ответственный за сокрытие товарной позиции;
О index.php — главная страница системы адм и н исти рования;
П product.php — скрипт, ответственный за отображение таблицы товарных позиций текущего каталога;
口 showprod.php ——скрипт, ответственный за отображение товарной позиции.
Для работы данного Web-приложения также понадобится файл config.php, в котором расположен скрипт, устанавливающий соединение с базой данных (см. листинг 7.5).
[ Замечание )
Далее в листингах для удобства восприятия будет опущен несущественный HTML-код. связанный в первую очередь со стилевым оформлением Web- приложения. Полная версия этого Web-приложения находится на прилагаемом к книге компакт-диске, а также доступна по адресу http://www.softtime.ru/ tnfo/downloads.php.
Главная страница системы администрирования расположена в файле index.php, содержимое которого приведено в листинге 8.3.
:Листинг 83 Файл index.php
:$*『;:;:;备,**+3、;厶;:,-^«:丢,?;;玄需;*С;;;. ;;;;;;“*** 各:;1*";"*:;***;*. Z; :*"、:::.;:*;; j* ♦*,****;* jfi;.:; .*h*,*^T,J::;爾.4"*,;,"才储讣容苗・?.; c„ ■
<?php
// Осуществляем соединение с базой данных require_once(”/config.php");
// Если параметр id_parent не установлен, присваиваем ему значение О if (! isset ($_GET ['idjiarent1 ])) $_GET[1 idjarent11 = 0;
// Формируем и выполняем SQL-запрос на извлечение информации
// о текущем каталоге.
$ query = rt SELECT * FROM catalog
WHERE id__catalog='r * $_GET [1 id^parent1 ];
$ctgc = mysql_query ($query);
// Если запрос выполнен удачно и текущий каталог не является корневым, // осуществляем вывод названия каталога — ссылки на уровень выше
if ($ctgc and $_GET[fid_parent*] != 0)
{
$catc = mysql_fetch_array($ctgc);
〃 Выводрш название каталога
echo **<Ь1Жаталог: ".$catc[ Fame,] Jv/hlxbr>";
// Возврат на уровень выше
echo ”〈& href=index.php?id_j>arent=*' • $catc [1 id_j>arent']・”>
Верхний уровень
</a><br>M;
// Выводим ссылку дин добавления нового подкаталога
if($_GET「id_parent1]!=0) echo "&пЬзр; <a hrеf=addcatfот<php?
id catalQg=,,. $_GET [1 id_parent' ]. ” & id_j)arent«". $_GET [1 id_parent']."> Добавить подкаталог< Za>”;
// Выводим таблицу подкаталогов текущего каталога
if (isset ($J3ET [1 id_parent ЧП {
// Формируем и выполняем SQL-запрос на вывод содержимого каталогов // данного уровня
$query н "SELECT * FROM catalog WHERE id_parent=n.$_GET[fid_parent」・” ORDER BY pos";
$ctg = mysql_query($query);
i£(!$ctg) puterror (нОшибка при обращении к каталогу**);
// Выводим заголовок таблицы каталогов echo n<table><tr>
<td>Ha3BaHwe</td>
<td>OnncaHne</td>
<td>no3.</td>
<td^effCTBn H</td>
</tr>";
// В цикле выводам подкаталоги текущего каталога while ($cat = mysql__fetch_array ($ctg)) {
// Выясняем, скрыт каталог или нет, и согласно этому выводим
// соответствующую управляющую ссылку
if($cat[fhide *] == 'hide') {
$strhide = "va href=showcat.php?
id_catalog=" * $cat [1 id__catalog * ] ”"& idjarentH” ・ $_GET [1 id_parent1 ]・"> Отобразить
</a>";
} else (
$strhide = M<a href=hidecat.php?
id_catalog=M.$cat[•id_catalog■].” & id_parent=" . $_GET [ • id_parent * ]«rt> Скрыть
</a>n;
// Выводим список каталогов echo r,<tr>
<td><a href=index.php?
id_parent=H.$cat[1id_catalog']."> "・$cat[Fame" J </ax/td>
<td>"・ $cat['description']."</td>
<td>°.$cat[fpos,].M</td>
<td>$strhide<br>
<a href=editeatform.php?
id_catalog=r,. $cat [ * id_catalog1 ]. "&
id_j>arent=H . $_GET [1 id_parent1 ] . 4 >Исправить</a><br>
<a href=delcat.php?
id_cata工。g="•$cat[1id_catalog']* ”&
id_parent«n.$_GET[,idjparent,]・">Удалить</а></{с1> <八工>";
}
}
echo ”</table>";
// Здесь заканчивается вывод подкаталогов текущего каталога.
// Выводим товарные позиции (услуги), подключая файл product .php include "product.php";
?>
Основная задача файла index.php заключается в выводе таблицы подкаталогов текущего каталога, помимо названия подкаталога (являющегося ссылкой на управление содержимым этого подкаталога), таблица содержит описание, позицию подкаталога относительно других подкаталогов, а также три ссылки Отобразить/Скрыть, Исправить и Удалить.
Файл index.php через строку запроса может принимать параметр idjparent — первичный ключ каталога, содержимое которого выводится на странице администирования, например:
http: //www. site. ru/admin/index. php?id_parent=12
Если параметр idjparent не передается, то элемент суперглобального массива $_get [1 idjparent1 ] получает значение 0, что соответствует корневому каталогу.
Как видно из листинга 8*3, после соединения с базой данных выводится название текущего каталога н ссылка на предыдущий уровень.
Это достигается осуществлением SQL-запроса:
SELECT * FROM catalog
WHERE id_catalog=$_GET [^d^parent1 ]
Данный запрос возвращает одну запись, соответствующую текущему каталогу.
После этого происходит формирование таблицы с подкаталогами текущего каталога. Для этого выполняется SQL-запрос:
SELECT * FROM catalog
WHERE id_parent=$_GET['id_parent']
ORDER BY pos
Запрос возвращает записи, соответствующие подкаталогам текущего каталога, отсортированные по полю роз.
Если у текущего каталога отсутствуют подкаталоги, предыдущий SQL-запрос вернет пустое значение, и вывод подкаталогов осуществляться не будет.
После формирования шапки таблицы в цикле while происходит формирование строк таблицы. В конце файла index.php посредством функции include происходит вк-чючение файла product.php, ответственного за вывод таблицы с товарными позициями текущего каталога. Содержимое файла product.php приведено в листинге 84
:Листинг 84 Файл product.php:Листинг 84 Файл product.php! !£■.' МЛ: V-^^-^1^'^^"^^^*-! !£■.' МЛ: V-^^-^1^'^^"^^^*-
:Листинг 84 Файл product.php
:Листинг 84 Файл product.php
! !£■.' МЛ: V-^^-^1^'^^"^^^*-
! !£■.' МЛ: V-^^-^1^'^^"^^^*-
<?php
// Если это не корневой каталог - выводим таблицу с товарными позициями if($_GET[* id_parent1] != 0) {
?>
<Ь1>Продукция в каталоге</hl><Ъг>
// Ссылка на добавление новой товарной позиции в каталог
<а href-addprodform.php?id_parent=<? echo $_GET['id_parent'] ?» Добавить квартиру</а>
《?php
// Выбираем из базы product товарные позиции, относящиеся к
// текущему каталогу
$query = nSELECT * FROM product
WHERE id_catalog=n.$_GET[*id_parent*]•” ORDER BY pos";
$prt = mysql_query($query);
if($prt)
// Выводим заголовок таблицы
echo H<table><tr class= * tableheader * >
<td>pai%OH</td>
<td>a^pec</td>
<td colspan=3><p class=help>rtn (О/Ж/К) </td>
<td>KOMH. </td>
<td>3Ta3K</td>
<td>3Ta^ * дoмa</td>
<td>MaTepwал</td>
<td>cany3an</td>
<td>6ajiKOH</ td>
<td>uewa</td>
<td>ueHa м•kb ・ </td>
<td>Bajwra</td>
<td>npMMe4aHne</td>
<td>fleiicTBMH< / td>
</tr>H;
while ($par = mysql_fetch__array ($prt))
{
// Выясняем, скрыта ли товарная позиция и выводим
// управляющие ссылки
if ($par [ *hidef ] = *Ь1с1ет) $ showhide = г|<а hre f=showprod, php ?
id3roduct=". $par [' id_product']・ ”& idqrenB".$_GET[• id_parent,] •”& id__catalog=H. $catc [1 id_catalog * ]."> Отобразить</a>°;
else $showhide = ”<a href=hideprod.php?
id_product=". $par [1 id__product,] . " & id_parent=". $_GET「id^parent『]・"& id_catalog=".$catc[* id_catalog1]•"> Скрыть</а>п;
// Определяем район
switch ($par [ * district1 ])
{
case 1kanavinskii1:
$distr = "Kaнавинекий"; break;
case 'nizhegorQdskii': $d±str = "Нижегородский1'; break;
case 1sovetskii *:
$distr = "Советский";
break;
case •priokskii*:
$distr = "Приокский";
break;
case ^oskovskii1:
$distr к "Московский";
break;
case 'avtozavodskii*:
$distr = "Автозаводскийи; break;
case 1leninskii•:
$distr = "Ленинский";
break;
case 'sormovskii*:
$distr = "Сормовский"; break;
}
// Определяем материал дома switch ($par[Material*]) {
case 1brick1:
$material = "кирпичный"; break;
case •concrete•:
$material = "панельный"; break;
case 'reconcrete *:
$material = "монолитный1*;
break;
)
// Определяем тип санузла switch (Spar['su■])
case 'separate':
$su = н со вмещенный0; break;
case 1combined1:
$su = "раздельный11; break;
}
// Определяем наличие балкона switch ($раг[1balcony1]) {
case *balcony1:
$balcony = "балкон11; break;
case * loggia *:
$ balcony = 11 лоджия"; break;
)
// Выводим таблицу с товарными позициями (квартиры) echo "<tr>
<td>$distr</td>
<td>". $par [,address」J</td>
<td>M. $par [1 square^o1 ] . "</td>
<td>'l $par f square__j ,:]・ ”</td>
<td>". $par (* square__k* ] . M</td>
<td>” ・ $par [ * rooms,] * **</td>
<td> и. $par [1 f loor1).n < /td>
<td>".$par[* floorhouse」.H</td> <td>$material</td> <td>$su</td> <td>$balcony</td>
vtd>".$par[1price1]・"</td>
<td>M.$par[1pricemeter1]* M</td>
<td>".$pa工[r currency*].и</td>
<td>H.$par[* note,].n</td>
<td>
<p>$showhide<br>
<a href=editprodfom* php?
id_product=M.$par(1 id_product1 ] *"& ici_parent^,'. $_GET [ * id_j)arent' ] •"> Исправить < /а><Ьг>
<а href=delprod«php?
idj>roduct-",$par[* id_product'].”& id_parent=°.$_GET[1id_parent']."> Удалить</a></td>
</tr>°;
}
?>
</table>
<?php
else echo °Это самый верхний каталог - здесь можно вывести описание.н;
В листинге 8.4 после вывода ссылки на добавление новой товарной позиции Добавить квартиру формируется и выполняется SQL-запрос на извлечение из таблицы product товарных позиций, относящихся к текущему каталогу* После вывода в окно браузера шапки таблицы в цикле while происходит формирование и вывод строк таблицы. Поля таблицы, имеющие тип enum, расшифровываются прм помощи управляющих конструкций switch.
Далее будут рассмотрены скрипты и формы для управления каталогами.
Добавление каталога осуществляется при помоши формы, расположенной в файле addcatfbrm.php, содержимое которого представлено в листинге 85
微浦尚*К^ЙМКi^^Вйй^険0йЗ^З靈期留&聲需修广 曙产,抬紂础产.":'…微浦尚*К^ЙМКi^^Вйй^険0йЗ^З靈期留&聲需修广 曙产,抬紂础产.":'…! Листинг &5. Файл addcatform.php! Листинг &5. Файл addcatform.php
微浦尚*К^ЙМКi^^Вйй^険0йЗ^З靈期留&聲需修广 曙产,抬紂础产.":'…
微浦尚*К^ЙМКi^^Вйй^険0йЗ^З靈期留&聲需修广 曙产,抬紂础产.":'…
! Листинг &5. Файл addcatform.php
! Листинг &5. Файл addcatform.php
<?php
// Устанавливаем соединение с базой данных require_once {**-./ config * php”);
/ / Если управляющие переменные переданы - выставляем значения //по умолчанию
if (1 isset ($title)) $ title = н Добавление нового каталога1*; iff!isset($button)) $button = мДобавитьH;
if(!isset($action)) $action ^ Maddcat.phpM;
if(!isset($showhide)) $showhide = "checked”;
// Если позиция каталога не передана, // назначаем каталогу последнюю позицию
if(!isset(Spos))
$query = "SELECT MAX(pos) FROM catalog
WHERE id_parent=M*$_GET[1id_parentF];
$num = mysql_query($queryj;
if($num) $pos = mysql_result($numf 0) + 1;
}
?>
<HTML>
<HEAD>
<title><?php echo $title; ?></title>
</HEAD>
<a hre^r,index. php?id_catalog=<? echo $_GET [f id_catalog* ]; ?>
&id__parent=<? echo 9_GET [ * id_parent' ]; ?>"> Вернуться на страницу адаинистрирования каталога</а> <form action=<?php echo $action; ?> raethod^post>
Название : <input type=text name=name value= * <?php echo $name; ?>1><br>
Описание : <textarea name=description rows=10 cols=60>
<?php echo $description; ?>
</textarea>
Позиция : <input type=text name=pos value-*<?php echo $pos; ?>*>
Отображать : <input type=checkbox name=hide <?php echo $showhide; ?»
<input type=subniit value^<?php echo $button; ?»
〈input type=hidden name=id_catalog value^<?^ $_GET[1id_catalog•] ?» <input type=hidden name= id parent value^<?^ $_GET [ * ici_parent1 ] ?» </fonn>
В листинге 8.5 после установки соединения с базой данных происходит проверка переданных форме управляющих переменных (название окна $title, кнопки $button, имя обработчика формы $action), если переменные не переданы —— устанавливаются значения по умолчанию, настроенные на добавление нового каталога. Переменной $pos, ответственной за позицию подкаталога относительно других подкаталогов текущего каталога, присваивается значение, соответствующее последней позиции. Для этого предварительно из таблицы catalog извлекается максимальное значение поля pos среди записей, поле которых td_patent совпадает с первичным ключом текущего каталога. Полученное значение увеличивается на единицу.
Далее формируется HTML-форма, состоящая из двух текстовых полей пате и pos для названия и позиции соответственно, текстовой области description для описания и флажка hide (поле Отображать), сброс кото-
рого приводит к добавлению скрытого каталога, а установка — к добавлению доступного для просмотра каталога.
В каждом поле формы присутствует параметр value. Ему можно передать значение, которое будет отражено в элементе управления, за счет установки одноименной с элементом управления переменной. Данный подход уже использовался в главе 7 и необходим для повторного использования этой же формы при редактировании каталога.
Завершает форму кнопка типа submit, отправляющая обработчику addcat.php значения, введенные в элементы управления. Код обработчика приведен в листинге 8.6.
Листинг 8.6. Файл addcat.php';
<?php
/ / Осуществляем соединение с базой данных
require_once (".・ /config <php11);
/ / Проверим, достаточно ли информации для занесения в базу данных
// нового каталога
if (empty ($_POST [ Fame * ])) I inks ($__POST [' id_catalog1 ],
**Отсутствует название каталога11);
if (empty ($_POST ['pos1])) links ($_POST「id_catalog,J,
"He введена позиция каталога11);
// Определяем, скрыт каталог (hide) или доступен (show)
if($_POST[fhidef] «= "on”)$showhide = **showM;
else $showhide = "hide";
// Заменяем одинарные кавычки обратными
$_POST[,name,] = st ^replace(••>»,"'«, $_POST [ - name1 ]);
$_POST(fdescription*] = str_replace(M,”,心 ”,$_POST[description1]);
// Формируем и выполняем SQL-запрос на добавление нового каталога
$query = nINSERT INTO catalog VALUES (NULL,
,M.$_POST(*name,] .,,rf
,"・ $_PCST [• description '】."',
"・$_POST「poM] J,
'Sshowhide I
”.$_PCST[1id_catalog'】J} \ if(mysql_query($query)) <
// Автоматически осуществляем переход
//на главную страницу адаинистрирования
echo "<HTMLXHEAD>
<МЕТА HTTP-EQUIV=‘Refresh' CONTENT—。; URL=index.php?
id^parent=,1 * $_POST [ fid_catalog ']・"‘> </HEAD>M;
} else links($_POST[•id_catalog1], "Ошибка при добавлении каталога11);
// Функция вывода предупреждения и ссылок возврата function links ($id__catalogr $msg) {
echo "vp>"・ $msg."</p>";
echo ”<p><a href=# onClick= * history.back()1>
Вернуться к правке каталога</а></р>";
echo "<р><а href^index»php?id_parents$id_catalog> Администрирование каталога</а></р>м;
exit ();
) ?>
Скрипт в листинге 8.6 начинается с проверки обязательных для заполнения полей паше и роз, соответствующих имени каталога и его позиции. В случае, если хотя бы одно из полей пустое, происходит вызов функции links о » Функция принимает два параметра: имя текущего каталога $id_cataicg и текст предупреждения $msg, которое она вместе со ссылками возврата выводит в окно браузера, после чего прекращает выполнение скрипта.
После проверки корректности введенных данных проверяется значение параметра hide. Если данный параметр принимает значение “cm” (что флаг Отобразить установлен), то новому каталогу необходимо присвоить статус доступного для просмотра. Для этого временной переменной $showhide, используемой далее для формирования SQL-оператора insert, присваивается значение 'show'. Если параметр hide принимает любое другое значение, временной переменной $ showhide присваивается значение 'hid©, и новый каталог добавляется как скрытый.
( Замечание )
В любой момент времени администратор каталога может изменить статус любого каталога, воспользовавшись ссылками Скрыть/Отобразить или редактированием параметров каталога при помощи ссылки Исправить.
После этого во всех текстовых параметрах, переданных обработчику из формы, одинарные кавычки (') заменяются обратными (、) для предотвращения конфликтов при выполнении SQL-оператора insert.
Далее формируется оператор insert для добавления нового каталога в таблицу catalog. В случае успешного выполнения SQL-запроса происходит автоматический переход на страницу администрирования текущего каталога.
Далее будет рассмотрено редактирование уже существующего каталога. В листинге 8.3 ссылка на редактирование каталога Исправить указывает на файл editcatform.php» содержимое которого представлено в листинге 87
Скрипт принимает два параметра id_catalog и id_parent, передающие первичные ключи исправляемого и текущего (родительского) каталогов соответственно.
上噂承氏咨皆替^^畢對 &;,.,:"寧军广弊a W^J*E".呼史$«¥h¥±寸 "м^_у:мат笠貿弊卞*1■1屮f ,? Y,… :,曜:[*¥聲¥.,理1,¥,*炉* 举f :空””㈠”卡弋”中:器3:”“一经^强不递坤七".””
:Листинг 8.7. Файл editcatform.php
irtiti'wiilV ?» й 7Ji •;;;二.:“•';;";衿“?1;7,扌.戸.弁+嘀・『,富^^^^^^^;仆,*;二 hi;::*.;.;;*;於 1;;;无*&a;7i,r『s* 工*;*,♦“,T;i;”£V,'i: :; ;. ;:«,“;;*」.;
<?php
// Осуществляем соединение с базой данных
require once("・ */config.phpм);
/ / Переопределяем управляющие переменные HTML-формы,
/ / ра сположе иной в файле addcat form, php
$ title = и Редактирование каталога**;
$button = ^Исправить11;
$acticn = "editest,php";
// Формируем и выполняем SQL-запрос на извлечение
// записи из таблицы catalog, соответствукяцей исправляемому каталогу
$query = "SELECT * FROM catalog
WHERE id_catalog=M. $_GET[1id^catalog*];
$cat = mysql_query ($query);
if(!Scat) puterror(^Ошибка при обращении каталогу1');
$catalog = mysql_fetch_array($cat);
// Присваиваем значения переменным для соответствующих параметров
// value HTML-формы
$name = $catalog[,name1];
$description = ^catalog[description *];
$pos = $catalog['pos'];
if(^catalog[1hide *] = 1showf) $showhide = "checked";
else $showhide =
/ / Включаем в файл HTML-форму
include naddcat form* php11;
?>
Как уже говорилось выше, при редактировании каталога используется та же HTML-форма, что и при добавлении. Для этого переопределяются управ-
ляющие переменные $titie, ^button и $action для названия окна, кнопки н обработчика формы соответственно.
После это('о из таблицы catalog извлекается запись, соответствующая исправляемому каталогу, и переменным, используемым в параметрах value управляющих элементов HTML-формы addcatfbnn.php, присваиваются значения нз базы данных. Исключение составляет поле hide, в зависимости от значения которого временной переменной $ showhide присваивается либо пустое значение, либо "checked".
[ Замечание )
В листинге 8.5 значение переменной $showhide проверяется при помощи функции isset ( ). которая вернет true, даже несмотря на то, что значение переменной $ showhide равно пустой строке. Данная функция возвращает false только в случае, если переменная ни разу не инициализировалась.
В конце файла при помощи оператора include происходит включение файла addcatform.php. Последний содержит HTML-форму, обработчиком которой теперь выступает файл editcat.php, в точности повторяеющий файл addcat.php (см. листинг 8.6) за исключением переменной $ query, содержащей SQL-запрос. Вместо оператора insert данная переменная содержит оператор update, представленный в листинге 8.8.
:Листинг 8.8. Оператор UPDATE файла editcat.php
:,il/、・f^^:&♦“♦**♦***♦,**・・、*i^**9'Hf*;*:・*#*'*;****;2、》・・ *,仙,•扌*・K:^^i:**・、+,■*•),*4*+・E・;・*#:;・*・・ir>.,*..*.*+**『+**3...*♦**“♦**«***;•・•*♦、♦*•*♦,•・♦*********«•♦••*,;,J**:i*14・i+<r*«■"=】
$ query = "UPDATE catalog SET name=,n. $_POST [fname ']・"‘,
description=1 n .$_POST「description']・."I
PQS=".$_POST [ • pos」.", hide^‘ $showhide‘
WHERE id^catalog^1*. $_POST [ ‘ id__catalog1 ];
Удаление каталога осуществляется при помощи скрнпта, расположенного в файле delcat.php, содержимое которого приведено в листинге 8.9. Как и editcatform.php, скрипт принимает два параметра id_catalog и id_parent, передающие первичные ключи удаляемого и текущего каталогов соответственно.
發期蟹麗電聲圏理脚*^4聚挙蹴您嘰^陷史I皆"I :Листинг 89 Файл delcat.儲温*; ♦ :^t Я"5^~Jfr^f^S^jMTT,,・ Л^^^^К е ^^匕% ^f";,~土式、\ ■ ■"}'а~я~Л~аЛ'» 1發期蟹麗電聲圏理脚*^4聚挙蹴您嘰^陷史I皆"I :Листинг 89 Файл delcat.儲温*; ♦ :^t Я"5^~Jfr^f^S^jMTT,,・ Л^^^^К е ^^匕% ^f";,~土式、\ ■ ■"}'а~я~Л~аЛ'» 1二,…—詳:琴蕊二,…—詳:琴蕊
發期蟹麗電聲圏理脚*^4聚挙蹴您嘰^陷史I皆"I :Листинг 89 Файл delcat.
儲温*; ♦ :^t Я"5^~Jfr^f^S^jMTT,,・ Л^^^^К е ^^匕% ^f";,~土式、\ ■ ■"}'а~я~Л~аЛ'» 1
發期蟹麗電聲圏理脚*^4聚挙蹴您嘰^陷史I皆"I :Листинг 89 Файл delcat.
儲温*; ♦ :^t Я"5^~Jfr^f^S^jMTT,,・ Л^^^^К е ^^匕% ^f";,~土式、\ ■ ■"}'а~я~Л~аЛ'» 1
二,…—詳:琴蕊
二,…—詳:琴蕊
<?php
// Осуществляем соединение с базой данных
requireonce(”../config,php");
/ / Удаляем каталог рекурсивной функцией
del^catalog ($_GET [ * id_catalog* ]);
// Осуществляем автоматический переход на страницу администрирования // текущего каталога
echo h<HTML><HEAD>
<МЕТА HTTP-EQUIV=fRefresh1 CONTENT"‘0; UR]>index.php?
id_parent=,1.$_GET[ *id_parent'].'"> </HEAD>n;
// Рекурсивная функция удаления каталога с первичным ключом $id_catalog function del_catalog ($id_catalog)
(
// Осуществляем рекурсивный спуск, для того
// чтобы удалить все вложенные подкаталоги
$ result = mysql_que ry (м SELECT * FROM catalog
WHERE id_parent = $id_catalogM);
if ($result)
while($row = nysql_fetch_array($result)) {
del_catalog($row[1id_catalog1]);
}
}
// Удаляем товарные позиции, принадлежащие каталогу
$ query = 1t DELETE FRCM product WHERE id_catalog=$id_catalogr,;
if(!mysql_query($query)) puterror(11 Ошибка при удалении продукции");
// Удаляем каталог с первичным ключом $id_catalog
$query = "DELETE FROM catalog
WHERE id__catalog=$id_catalogn;
if(*mysql_query($query)) puterror("Ошибка при удалении каталога");
?>
Удаление каталога осуществляется при помощи рекурсивной функции del_cataiog(), принимающей в качестве единственного параметра значение первичного ключа $id_cataiog удаляемого каталога.
При ПОМОЩИ SQL-запроса “SELECT * FROM catalog WHERE id_patent = $id__cataiogH функция извлекает из таблицы catalog все подкаталоги, для которых удаляемый каталог является родительским, и производит рекурсивный вызов с передачей в качестве параметра первичных ключей подкаталогов удаляемого каталога.
После этого осуществляется удаление каталога и его товарных позиций. После завершения работы функции dei_catalog () происходит автоматический возврат к странице администрирования текущего каталога (id_parent).
Сокрытие и отображение каталога осуществляется при помощи скриптов hidecat.php и showcat.php соответственно. Данные скрипты принимают два параметра id_catalog и id_parents передающие первичные ключи скрываемого (отображаемого) и текущего каталогов соответственно. Код файла hidecat.php приведен в листинге 8Л0.
<?php
// Осуществляем соединение с базой данных
require_once(".・/config* php");
// формируем и выполняем SQL-запрос на сокрытие каталога
$query = "UPDATE catalog SET hide=*hide*
WHERE id_catalog=M.$_GET「id_catalog'];
if(mysql_query($query))
{
echo "<HTMLXHEAD>
<META HTTP-EQUIV^1 Refresh1 CONTENT=,0; (JRL==index.php?
id_j)arent=M. $_GET [ • id_j)arent1 ] .11 r >
</HEAD>";
} else puterror (^Ошибка при сокрытии каталогам); ?>
В скрипте функции mysql_query () передается SQL-оператор update, исправляющий значение поля hide на значение 'hide', для записи таблицы catalog с первичным ключом, передаваемым через параметр id_cataiog. Файл showcat.php аналогичен представленному в листинге 8.10 файлу hidecat.php, за исключением того, что в операторе update полю hide присваивается значение 'show', помечающее каталог как доступный для просмотра.
На этом построение системы администрирования каталогов закончено. Далее будет рассмотрено администрирование товарных позиций.
Традиционно начнем построение системы администрирования с файлов, ответственных за добавление товарной позиции* HTML-форма addprodfbrm.php для добавления новой товарной позиции представлена в листинге 8.1L Файл принимает параметр id_parent, передающий значение первичного ключа текущего каталога.
券二臨;嗡*:岂t¥』**,穹才建窗予"":生我”:w^^^*绚 学“■:F^|^"M;吟:渐缕ща..;二:"中7閉*?^f塁:*:**":"岂 产/支党^^:空
[Листинг 8.11, Файл addprodform.php
%";...;....; 工,“”'ли**-*. ;“... ;?X*T;*“g.. ;““*“、■*,臥乔,」Ki”,^i^/;“、““.;..“..;$..“*『;;“.、」.*“『“;“」“*““=.;““」.,,:;
<?php
〃 Риэлтерские услуги
// Устанавливаем соеданение с базой данных require_once (**../config, php");
// Если управляющие переменные переданы, выставляем // значения по умолчанию
if(Iisset($title)) $title = "Добавление квартиры";
if (! isset ($bntton)) ^button - ,rДобавить11;
if(!isset($action)) $action = Haddprod.phpn;
if (1 isset ($showhide) ) $showh.ide 二 "checked°;
// Если позиция товарной позиции не передана, // назначаем ей последнюю позицию if(!isset($pos)) {
$ query = "SELECT MAX (pos) НЮМ product
WHERE id_catalog=,'. $_GET [1 id_j>arent'];
$num = mysql_query ($query);
if ($num) $pos = mysql_result ($num, 0) + 1;
} ?> <HTML> <HEAD> <titlex?php echo $title; ?></title> </HEAD>
<a href =иindex.php?id_j>3rent=<? echo $id_j>arent ?>"> Вернуться в администрирование каталога</а> vfcrm action=<?php echo faction; ?> method=post> Район :
<select type=text name=district>
<option value=1kanavinskii'
<?php if(^district = *kanavinskii1} echo "se工曰oted"; ?» Канавинский
<option value=,nizhegorodskii*
<?php if($district = 1nizhegorodskii1) echo "selected"; ?» Нижегородский
<option value=,sovetskii'
<?php if (^district 一 T sovetskii *) echo "selected'*; ?»
Советский
<option value= ^riokskii1
<?php if($district ~ 'priokskii1) echo "selected”; ?» Приокский
<option valne='moqkQvskii*
<?php if ($district = *moskovskii1) echo f,selectedr,; ?» Московский
<option value='avtozavodskii *
<?php if (^district === •avtozavodskii1) echo "selected"; ?» Автозаводский
<option value='leninskii'
<?php if ($district = leninskii*) echo 11 selected"; ?» Ленинский
<option v a1ue^f sоrmovs ki i
<?php if ($district = 1 sormovskii *) echo ^selected**; ?» Сормовский
</select>
Адрес : <inputАдрес : <inputtype^texttype^textname=address value=,<?php echo ^address;name=address value=,<?php echo ^address;пл (O)nn (米)пл (К)пл (O)nn (米)пл (К):<input :<input :<input:<input :<input :<inputtype=text type=text type=texttype=text type=text type=textname=sqnare_o value=1<?php echoname=sqnare_o value=1<?php echo$square_o; ?> r>$square_o; ?> r>name=square_j value=l<?php echo $square j; ?>‘>name=square_j value=l<?php echo $square j; ?>‘>name^squa re_k value=l<?php echoname^squa re_k value=l<?php echo
Адрес : <input
Адрес : <input
type^text
type^text
name=address value=,<?php echo ^address;
name=address value=,<?php echo ^address;
пл (O)
nn (米)
пл (К)
пл (O)
nn (米)
пл (К)
:<input :<input :<input
:<input :<input :<input
type=text type=text type=text
type=text type=text type=text
name=sqnare_o value=1<?php echo
name=sqnare_o value=1<?php echo
$square_o; ?> r>
$square_o; ?> r>
name=square_j value=l<?php echo $square j; ?>‘>
name=square_j value=l<?php echo $square j; ?>‘>
name^squa re_k value=l<?php echo
name^squa re_k value=l<?php echo
Кол-*во комнат :
<select type=text name=rooms>
<option value^l<option value^2<option value-3<option value=4<option value=5<option value=6</select><option value^l<option value^2<option value-3<option value=4<option value=5<option value=6</select><?php if($rooms®=l) <?php if ($rooms=2) <?php if ($rooms—3) <?php if ($rooms—4) <?php if ($rooms—5) <?php if ($roouis=6)<?php if($rooms®=l) <?php if ($rooms=2) <?php if ($rooms—3) <?php if ($rooms—4) <?php if ($rooms—5) <?php if ($roouis=6)echo T,selected"; ?»1 echo "selected"; ?»2 echo "selected"; ?»3 echo "selectedr,; ?»4 echo "selected"; ?»5 echo "selected11; ?»6echo T,selected"; ?»1 echo "selected"; ?»2 echo "selected"; ?»3 echo "selectedr,; ?»4 echo "selected"; ?»5 echo "selected11; ?»6
<option value^l
<option value^2
<option value-3
<option value=4
<option value=5
<option value=6
</select>
<option value^l
<option value^2
<option value-3
<option value=4
<option value=5
<option value=6
</select>
<?php if($rooms®=l) <?php if ($rooms=2) <?php if ($rooms—3) <?php if ($rooms—4) <?php if ($rooms—5) <?php if ($roouis=6)
<?php if($rooms®=l) <?php if ($rooms=2) <?php if ($rooms—3) <?php if ($rooms—4) <?php if ($rooms—5) <?php if ($roouis=6)
echo T,selected"; ?»1 echo "selected"; ?»2 echo "selected"; ?»3 echo "selectedr,; ?»4 echo "selected"; ?»5 echo "selected11; ?»6
echo T,selected"; ?»1 echo "selected"; ?»2 echo "selected"; ?»3 echo "selectedr,; ?»4 echo "selected"; ?»5 echo "selected11; ?»6
Этаж : <input typ^text name=floor value=1 <?php echo $floor; ?> Этажность дома : <input type^text name=floorhouse
value=*<?php echo $floorhouse; ?>‘>
Материал дома :
<select type=text name^material>
<option value=1brick1
<?php if ($material — 'brick') echo ft selected"; ?» Кирпичный
<option value='conerete1
<?php if($material = 'concrete') echo ^selected"; ?» Панельный
<option value='reconcrete'
<?php if ($material = * reconcrete1) echo "selected11; ?» Монолитный
</select>
санузел
<select type=text name=su>
<option value='corribined1
<?php if($su h= 1 combined1) echo "selected"; ?» раздельный
<option value=,separate1
<?php if($su ^ 1 separate *) echo "selected"; ?» совмещенный
</select>
balcony
oelect type=text name=su>
<option value-'balcony *
<?php if($balcony = 'balcony') echo "selected"; ?»балкон
<option value=*loggiaT
<?php if ($balcony ~ * loggiaf) echo "selected"; ?»лоджия </select>
Цена : <input type=text name^price valuer1<?php echo $price; ?>‘>
Цена м.кв. : <input type=text name^priceiueter valuerf<?php echo $pricemeter; ?> *>
Валюта
<select type^text name=currency>
<option value=lRUR,
<?php if ($currency = f RUR *) echo "selected1*; ?»RUR
<option value='USD‘
<?php if ($currency r *USD') echo ^selected1'; ?»USD </select>
Примечание : <textarea name?=note><?php echo $note; ?></textarea>
Позиция : <input type=text name=pos value-1<?php echo $pos; ?>*> Отображать : <input type=checkbox name=hide <?php echo $showhide; ?» <input class=button type=sutmit value=<?php echo $button; ?» <input type^hidden name=id_catalog
value=<?php echo $_GET[*id_ca七alog'1; ?»
< input type^hidden name=icPproduct
value^<?php echo $_GET[* id_product']; ?» </form>
Структура файла addprodfbrm.php, представленного в листинге 8.11, аналогична структуре файла addcatform.php (см. листинг 8.5). Вначале устанавливаются управляющие переменные формы: $title, $button и $action, со- держащйе название окна, кнопки и имя обработчика формы соответственно. Дале% если форме не передается позиция $pos, вновь добавляемой товарной позиции назначается последняя позиция. Для этого из таблицы product извлекается максимальное значение поля pos товарных позиций текущего каталога:
SELECT MAX(pos) FROM product WHERE id_catalog=$_GET[1id^parent1] Полученное в результате запроса значение увеличивается на единицу и присваивается временной переменной $рсз. После этого выводится HTML- форма, содержащая элементы управления для каждого поля из таблицы product. Значения полей таблицы product, чей тип соответствует перечислению (enum), оформляются в виде выпадающих списков, что не позволяет ввести ошибочное значение. В связи с этим в обработчике формы, расположенном в файле addprod.php (листинг 8Л2), значения этих переменных не проверяются на соответствие.
М<А『♦■标 гь;М<А『♦■标 гь;:*. ”,.卄”, :*♦:;*• * 丁二.* ;尸...* Г. “ “а* •«. ■-. ■ *? ■. «^ • ?. V л * *.*-*4 Л V-V V* 'Ь^ХУДЯЩ
М<А『♦■标 гь;
М<А『♦■标 гь;
Листинг 8.12. Файл addprod.php
<?php
// Осуществляем соеданение с базой данных require__once(". ./config.php");
// Проверим, достаточно ли информации для занесения в базу данных
// новой товарной позиции
if(!isset($_POST[,address•])) links($_POST[1id_catalog1], ^Отсутствует адресn);
if (! isset ($_POSTrsquare_o'])) links ($_POST[1 id^catalog1 ],
"Не введена площадь(О)");
if(!isset($_POST[*squarej•])) linksC$_POST[Tid^catalog,],
"He введена площадь(Ж)”);
if(!isset($_POST['square_k口)) links($_POST[*id__catalog1],
11 He введена площадь (К)");
if (! isset ($_POST「floorhouse1 ])) links ($_POST 厂 id__catalog* ] r
"He введена этажность дома11);
if (! isset £$_POST (* price' J)) links ($_POST [ * id_catalog'], MHe введена общая цена");
if(!isset($_POST['pricemeter1])) links($_POST[1id_catalog*],
"He введена цена квадратного метра");
if(Iisset($_PQST['pos,])) links{$_POST[1id-Catalog1],
"He введена позиция продукта");
// Выясняем, скрыта товарная (hide) позиция или доступна (show) if($hide в "on") $showhide = 1show *;
else $showhide = 'hide';
// Заменяем одинарные кавычки обратными
$_POST[ 'note1 ] н str_replace(,M,tr "'"^^POSTrnote1 ]);
$_POST[*address'] = strreplace{",”■"、— $_POST['address']);
// Формируем SQL-запрос на добавление новой товарной позиции $query = "INSERT INTO product VALUES (
NULL,
,”・$一POSTUnot/] ・”•, '”*$_EOST「district'] J', '"• $_POST P address」."', ". $_POST [f square_o']・”, ”.$_F0ST Г square_j Ц ”* $_POST r square_k']・ \ ".$一POST「rooms'] J,
"^^POSTE1 floor1]
”. $_POST[,floorhouse*]・", T".$_POST(*material']J'* lY—POSTPsul")
"”・ $_PQSTI'balcony1)." I ”.$_POST['price*] J, M.$_POST[1 pricemeterf).",
1 ”・$_POST[1 currencyr]. n, f ,$showhide I
".$_POST[ rpos1 ] J,
“.$_POST [f id-Catalog『].")"; if (mysql_query ($query))
{
// Осуществляем а в томатиче с кий переход на страницу администрирования // текущего каталога echo M<HTML><HEAD>
<МЕТА HTTP-EQUIV=fRefresh1 CONTENT=,Q; URb= index.php?
idjp а г ent=$ POST (1 id^catalog1 ]1 > </HEAD></HTML>M;
} else puterror ("Ошибка при добавлении продукции11);
/ / Функция вывода предупрежде ни я и ссылок возврата function links($id_catalog,$msg)
{
echo "<p>".$msg.”《/p>";
echo ('<p><a href=# onClick^^istory.backO *> Вернуться к правке продукта</а></р>";
echo n<p><a href=index.php?id^parent-$id_catalog>
Администрирование каталога продукиии</ах/р>г,; exit ();
После проверки информации из формы на соответствие, а также замены в текстовых переменных одинарных кавычек (,) на обратные (') происходит формирование SQL-оператора insert, который располагается в переменной $ query, передаваемой функции mysqi_query(). В случае успешного выполнения функции происходит автоматический переход на страницу администрирования текущего каталога.
Редактирование уже существующей товарной позиции осуществляется с использованием HTML-формы editprodfbrm.php (листинг 8.13), принимающей два параметра id_product и id_parent, в которых передаются значения первичных ключей редактируемой товарной позиции и текущего каталога соответственно.
j Листинг 8.13. Файл editprodform.php:
<?php
// Осуществляем соеданение с базой данных
require_once (rr../config.phpn);
// Устанавливаем управляющие переменные для настройки
/ / HTML-формы, рас положе иной в файле addp rodform., php
帛title = ,тРедактирование данных о квартире*1;
$button = "Исправить11;
faction = neditprod.phpM;
// Формируем и выполняем SQL-запрос на извлечение записи таблицы
// product, с первичным ключом id_product
$query = “SELECT * FROM product
WHERE id_product=M.帛_GET['id_j)roduct'];
$pro = mysql_query ($query);
if (1$pro) puterror(^Ошибка при исправлении продукциин);
$product = mysql_fetch_array($pro)『
// Заполняем переменные для передачи в HTML-форму
$note = $product[1note *];
$district = Sproduct[* district *];
$address = ^product[faddress *);
$square_o = $product「square^1 ];
$square_j = $product「square_j ■];
$square_k = $product 11 square_k1);
$ roams = $product [ * rooms * ];
$floor = $product[1 floor*];
$floorhouse = ^product[* floorhouse,J;
$charact = $product[•charact*];
$su = ^product fsu1];.
$point 工 $product(1point'];
$price = $product[* price1];
$pricemeter = $product[,pricemeter」;
$currency = $product[*currency,];
$pos = $product[fpos1];
if($product[1hide *] = * show1) $showhide = "checked";
else $showhide =
〃 Включаем HTML-форму
include naddprodfonn.phplt; ?>
В файле editprodfbrm.php происходит переопределение переменных $titie, Sbutton и $action, ответственных за заголовок окна формы, название кнопки и имя обработчика соответственно. После этого при помощи SQL- оператора select из таблицы product извлекаются значения полей, соответствующие записи с первичным ключом id_product. В конце скрипта при помощи оператора include в тело скрипта включается файл addprodfbrm.php, содержимое которого приведено в листинге 8.11. Обработчиком формы здесь выступает файл editprod.php, который по структуре аналогичен addprod.php, аа исключением SQL-оператора: вместо оператора insert используется оператор update (листинге 8.14).
士岁;:験騎戰掰纳鸭承揣跳—ф轡唯逑缪屮懸了整魁映学К礎薔眦嘟制師押用吋卄编邮||1|[1|111^ Листинг 8.14. Оператор update файла editprod.php
$query = "UPDATE product SET
note=….$_POST ['note 1" I district=*". $_POSf [r dis trict D address®'". $_POST [ * address']・"I square__o= *и . $_POST [,square__o * ] .,M , squareJ»M\ $__POST [,squarej ']•"', square_k= *". $_POST [' square_k ']・”‘, rooms®*'. $_POST [1 rooms']・'" floor^n.$_POST[Tfloor r ]・",
floorhouse=", $_POST [ * floorhouse' ] •
material»'".$_POST['material1]
su='".$_POST[1 su1 ] . " \
balcony^™.$_POST[*balcony']J',
price=M.$_POST[1 price')・",
pricemeter=M.$_POST[1pricemeter']・".
currency 7 ・ $_POST [ Currency」.” I
PQS="•$_POST「pos『]J,
hide=『”・ $_POST厂 showhide']・"'
WHERE id_product=".$_POST[*id_product1 ];
Удаление товарной позиции осуществляется при помощи скрипта, расположенного в файле delprod.php, содержимое которого представлено в листинге 8」5・ Скрипт принимает два параметра: id_product н id_parent, в которых передаются значения первичных ключей редактируемой товарной позиции и текущего каталога соответственно.
举2二'型图豬^譜¥,“4卷鳖券举咨*端蠻第j学岁戏簡胱犠齊招陇聯^^^^jg^^.縮勲咏<7tfc^j^^^g^g^^^^ 学堂fj ? Листинг 8.15, Файл delprod.php
<?php
/ / Осуще ст в ляем соединение с базой данных requi re_once (. . /confi g. php ");
/ / Нормируем и выполняем SQL-запрос на удаление товарной позиции // с первичным ключом idjroduet из таблицы product
$query = "DELETE FROM product
WHERE id_product^M. $_GET (* id_p匸oductf ];
if(mysql_query($query))
{
echo "<HTML><HEAD>
<МЕТА HTTP-EQUIV^ * Refresh1 CONTEND* 0; UHL^index.php? id parent=H * $_GET[1id_parenf].nt> </HEAD>";
} else puterror (н0шибка при удалении продукции,r);
?>
В листинге 8J5 формируется и выполняется SQL-запрос на удаление записи с первичным ключом id_product из таблицы product, после чего происходит автоматический переход на страницу администрирования текущего каталога.
Скрытие и отображение товарной позиции осуществляется при помощи скриптов hideprod.php и showprod.php соответственно. Файлы принимают те же параметры, что и остальные управляющие скрипты для работы с товарными позициями* Содержимое файла showprod.php, ответственного за отображение товарной позиции, представлено в листинге 8.16.
i Листинг 8.16. Файл showprod.php
<?php
// Осуществляем соеданение с базой данных requireonce(”../config.php");
/ / Формируем и выполняем SQL-запрос на отображение товарной позиции $query = "UPDATE product SET hide=* show1
WHERE id_product=n. $_GET [1 id__product1 ];
if(mysql_query($query))
(
echo l,<HTML><HEAD>
<META HTTP-EQQIV= * Refresh1 CONTENT二‘0; URL=index.php? id_parent=',.$_GET['idjarent'], ” '>
</HEAD>";
} else puterror ("Ошибка при товарной позиции11);
Как it в случае с подкаталогами (см. листинг 8.10), товарная позиция становится доступной для просмотра, если поля hide файлов showprod.php и hideprod.php имеют значения 'show* и 'hide' соответственно.
На этом построение системы администрирования каталога "Риэторские услуги0 завершено. Далее будет рассмотрен ”двн*ок" для отображения каталога на сайте, который состоит из двух файлов:
□ index.php — файл, содержащий код, ответственный за вывод структуры каталогов;
口 product.php ——файл, содержащий код, ответственный за вывод таблицы с товарными позициями в текущем каталоге.
В листинге 8.17 представлено содержимое файла index.php.
**4W * * * t!£ • •» V ч » ^ V-4» VJ ' '-• •-1*4WS? ? ?! * ^-* К ViWWilWb'W * ?.' ■ 5 ■ f t5-5* t • J 'JfW4J ^ 5- W WSM %! * V * **.', * * : ”, *:' ГГСЛУ %:f :»Mft撃』・Т,,”М*&к,Н2 壮*"小"1 ”,"融使W” 1睛* 5
<?php
// Устанавливаем соединение с базой данных require_once ("…/ config. php");
// Если значение параметра id^parent не установлено, следовательно,
// выбран самый верхний уровень меню
if(iisset($_GET[1id^parentЧ)) $_GET[*id_parent4 = 0;
// Если имеем дело не с корневым каталогом^ то выводам ссыпку на
〃 уровень выше и воспроизводим вышележащее меню
if ($J3ETt1 id_parent1 ] != 0)
{
$catc = mysql_fet:ch_array ($ctgc);
// Выводим ссылку дая возврата в предыдущее меню
echo "<а href=index.php?id_parent=".$jGET[* id_parent *].">
Верхний уровень</а></р>п;
// Во с произ водим меню верхнего уровня
$query ^ мSELECT * FR64 catalog
WHERE hide= * show' AND id_parent=". $_G£T [1 idjarent * ];
$prev = mysql^query {$query);
iff!$prev) puterror("Ошибка при обращении к каталогун);
// Выводам список каталогов
while ($pmenu = xnysql_fetch_array ($prev))
{
echo "<a href=index.php?id_parent^n.$piuenu[*id^catalog*]."& id_catalog=v. $jxnenu [1 id_catalog1 ] . и>
”.$pmenu11name,].H</a><br>M;
// Если вышележащий каталог совпадает с текущим, и у него имеются
// подкаталоги, то выводим их
if ($__GET [1 id__parent * ] = $pmenu [ * id_catalog,])
{
// Формируем и осуществляем SQL-запрос, извлекающий подкаталоги
〃 текущего каталога
$query = "SELECT “ FROM catalog
WHERE hide=,show' ftND id_parent=". $_GET [' idjparent * ]-"
ORDER BY posn;
$ctg = mysql_query($query);
if (!$ctg) puterror(^Ошибка при обращении к каталогу**);
// Если SQL-запрос вернул хотя бы одну запись, / / выводим подкаталоги.
if (mysql_num_rows ($ctg) >0)
{
while($cat = mysql_fetch_array($ctg))
{
// Выводим список катгшогов
echo "<a href=index.php?id_parent=,*.$_GET['id_parent *].M& id_catalog="・ $cat[,id_catalog,].H> M .$cat [ tname, ]. °</a><br>r,;
}
}
}
// Если это корневой каталог, просто выводим его подкаталоги else
{
// Выводим список подкаталогов
$ query = "SELECT ♦ FRC^M catalog
WHERE hide=' show' AND id_j>arent3=11. $_GET [ * id_parent']・" ORDER BY pos";
$ctg = mysql_query($query);
if (!$ctg) puterror (иСй11ибка при обращении к каталогу!н);
while ($cat = mysql_fetch_array ($ctg))
{
// Выводим список подкаталогов корневого каталога
echo 'Уa href=index.php?id__parent=n. $cat ['id_catalog*) . **& id__catalog«M. $cat [1 id__catalog * ] • H> "・ $cat(*name1]."</a>";
// Если это не корневой каталог, выводим таблицу с товарными позициями if($_GET[*id^parent*] 1= 0) include "product.php";
?>
После установки соединения с базой данных в файле index.php (см. листинг 8J7) происходит проверка параметра id_parent, во время которой происходит передача первичного ключа текущего каталога. Если параметр не передан, следовательно, происходит запрос к корневому каталогу, не имеющему записи в таблицы catalog. В этом случае элементу суперглобального массива $_get [1 id_parent」присваивается значение 0.
Если текущим каталогом является корневой каталог, файл index.php осуществляет вывод только его подкаталогов. Если же текущим каталогом является каталог с первичным ключом 土d_parent, отличным от нуля, помимо подкаталогов выводятся ссылки на каталоги, расположенные уровнем выше, и таблица с товарными позициями текущего каталога, если, конечно, они имеются*
В конце файла index.php при помощи оператора include происходит включение файла product.php, ответственного за вывод таблицы с товарными позициями. Содержимое файла product.php приведено в листинге 8J8.
Включение файла product.php происходит только в том случае, если текущим каталогом не является корневой каталог.Включение файла product.php происходит только в том случае, если текущим каталогом не является корневой каталог.: Замечание )
Включение файла product.php происходит только в том случае, если текущим каталогом не является корневой каталог.
Включение файла product.php происходит только в том случае, если текущим каталогом не является корневой каталог.
:轡步 nЖ导"У 号 " W " ''AV**A:VA ^" * :."'/!f ^;1!4,
i Листинг 8.18 Файл product.php
<?php
// Выводам название каталога и его краткое описание
echo H<hl class^1 zag21 Жаталог: ,r * $catc [ * name * ] . M</hl>";
echo "<p>n.$catc[ description1 j,ft</p>'*;
// Выводим таблицу с товарными позициями
〃 Формируем ассоциативный массив, где в качестве ключей выступают
// парам©трж, передаваемые в строке запроса, а в качестве значений —
// имена полей в таблице product
$order = array();
$order IT roomsorder* ]工 11 rooms**;
$order [1 districtorder * ] = "district**;
$ order [f sguare_o_orderf ] = "§qu&re_o”;
$order[1s qua re_j _order1] = nsquareJ”;
$order[1square_k_order1] = "square_k”;
$order [ ^ricej^et^order1) = мргicemeter";
$crder【price—order " = "price”;
$order[1floor_order'] = "floor";
$order[* floor house order*] = °floorhouseH;
$order [' currency order' ] = "currency*1;
$order[13u_order1] = "su";
Sorder[1balcony_order,] = "balcony";
$order[rmaterial_order1] = "material";
// Формируем временную переменную $strtrop, которая далее
// используется для сортировки результатов SQL-запроса при извлечении
// товарных позиций из таблицы product
// По умолчанию сортируем товарные позиции по полю роз
$strtinp - “роз”;
// Если через параметр строки запроса задана прямая или обратная
// сортировка по одному из полей таблицы order, изменяем значение
// временной переменной $strtnip
foreach($order as $parametr => $field) {
if(isset(帛一GET[* $parametr1])) (
if {$_GET [ * $parainetr1 ] = "up”)
$_GET [1 $parametr * ] = “down” ; $strtmp = $field;
} else {
$_GET [ * $parametr1 ] = Hup";
$strtmp = "$field DESCH;
}
}
else $_GET[,$parametr,] = "up";
}
// Выбираем из таблицы product
$query = "SELECT * FROM product
WHERE id catalog^".$_GET[1id_catalog']・”
ORDER BY $strtH5)"『
$prt = mysqlquery($query);
if (!$prt) puterror (**Ошибка при запросе к таблице product1');
// количество рядов в наборе должно быть больше нуля
if (mysql^jium__rows ($prt) >0)
〃 формируем ссылку, с помощью которой можно сортировать товарные
// позиции по выводимым полям таблицы
$href = "index.php?id_parerig” . $_GET [1 id_parent' ] J
&id_catalog=M. $_GET [1 id_catalog * ];
?>
<table>
<tr>
<tdxa href^<? echo $href. **&
roomsc>rder=“ . $_GET (' roamsorder1 ]; ?»Кол. комн .</а></td>
<td><a href^<? echo $hgf J&
districtorder=T,. $_GET [ fdistrictorder1 ]; ?»Pai4OH</a></td>
<td>Anpec</td>
<td><a href=<? echo $hre£・”&
square^o^order^11. $_G£T [1 square_o_order' ] "; ?>> (0) </ax/td>
<td><a href=<? echo $href."&
square_j_order=". $_GET [1 square_j_order」";?»(Ж) </ax/td>
<td><a href=<? echo $href.”
&square_k_orde^n. $_GET [1 square k^order1 ] п; ?»(K) </ax/td>
<td><a href=<? echo $href.H&
floor_order=**. $_GET [' floor_order *); ?»Этаж</a></td>
<td><a href=<? echo $hre£."&
f loor_house_order=,* * $ jGET [' floor_house_order1 ]; ?»
Эт*flOMa</a></td>
<td><a href=<? echo $hre£."&
material_order-H . $_GET [ ^aterial^order1 ]; ?»Xap</a></td>
<td><a href=<? echo $hre£J&
su_order=H. $」GET [1 su_order* 1; ?»C/y</a></td>
<tdxa href=<? echo $href. "&
balcony_order=M. $_GET [ * ba 1 cony_order' ]; ?»Л/Б</ax/td>
<td><a href=<? echo $href."&
price_metr_order=H. $_GET [' price_metr_order' ]; ?»
Цена, vbr>M.kb* </a></td>
<td><a href=<? echo $href.n&
price_order=H. $_GET [ *price_order1 ]; ?»Цена, общ.</a></td>
<td><a href-<? echo $href*M&
сиггепсу^гбег30 * $_GET [1 currency_order 】]” ;?»
Валюта</a></td>
<td>HpwM. </td>
</tr>
]3?акЛ81
<?
while ($par = mysql_fetch_array (Sprt)) {
// Определяем район switch ($par[Mistrict * ]) {
case 'kanavinskii':
$distr = "Канавинский"; break;
case 'nizhegorodskii1: $distr в tT Ниже городе кий** ; break;
case * sovetskiir:
$distr = n Советский11;
break;
case 'priokskii':
$distr = "Приокский0;
break;
case fmoskovskii*:
$ distr = ^Московский**;
break;
case 'avtozavodskii1:
$distr = "Автозаводекий°; break;
case 'leninskii':
$distr = "Ленинский**;
break;
case 'sormovskii1:
$distr = нСормовскийи;
break;
default: $distr=“  ”;
}
// Определяем материал дома switch ($par[*material* J) {
case 1brick*:
$material = *'кирп.M;
break;
case *concrete*:
$material = "панел."; break;
case 'reconcretef:
$material ^ "монолит.";
break;
default: $materia^M rl;
}
// Определяем тип санузла switch ($par[*зи*]) {
case 1 separate*: $su = "COE*"; break;
case 'combined':
$su = "разд.";
break;
default: $su=" ";
}
// Определяем наличие балкона switch ($par「balcony']) {
case 'balcony':
$balcony «= "балкон”; break;
case * loggia *:
$balcony = °лоджия°;
break;
default: $balcony=HfinbspM; }
// Выводам строку таблицы echo "<tr>
<td>". $par [1 гошш,]. H$tnppict></td> <td>$distr</td>
<td>".$par[* address'].и</td>
<td>°.$par[* square^o,].M</td>
<td>".$par(T square_j,]・"v/td>
<td>"-$par[fsquare_k,]•"</td>
<td>M.$par[•floor']Jv/td>
<td>".$par[,floorhouse*]."</td>
<td>$material</td> <td>$su</td>
<td>5balcony</td>
<td>M.$par[* pricemeterr]・"</td> vtd>”.$par['price,].M</td> <td>"・ $par[1 currency *].n</td> <td>"-$par[* note *]・"v/td> </tr>";
) }
</table>
Как видно из листинга 8.18, в начале скрнпта происходит вывод названия подкаталога и его описание с помощью ассоциативного массива $catc, объявленного в index.php.
При помощи SQL-запроса из таблицы product извлекаются товарные позиции подкаталога, доступные для просмотра, т. е. те, у которых поле hide принимает значение 'show1. Если в каталоге присутствует хотя бы одна товарная позиция, внводнтс兄 шапка таблицы и — при помощи цикла while —— ее строки. Так же как и в листинге 8.4, поля записей типа enum преобразуются при помощи конструкций switch.
Отдельного рассмотрения заслуживает шапка таблицы, в которой перечислены названия столбцов. Практически все имена являются ссылками, позволяющими производить прямую и обратную сортировку таблицы по столбцу. Ссылка передает файлу index.php три параметра: id_parent, id_cataiog —— первичные ключи записей таблицы catalog, соответствующие родительскому и текущему каталогу, а также параметр, совпадающий с одним из ключей массива border, формируемого в начале файла index.php. Значения массива совпадают с имена столбцов таблицы product. Обработка последнего из передаваемых index.php параметров осуществляется сравнением его с ключами массива $order в цикле foreach. Если значение параметра установлено в "up”,то временной переменной $ st гtmp5 используемой для формирования SQL-запроса, присваивается имя столбца, по которому далее проводится прямая сортировка. Сам параметр при этом принимает значение "down", что при повторном переходе по ссылке приводит к присвоению переменной $strtmp имени столбца с ключевым словом desc (например "price desc”),что далее приводит к обратной сортировке таблицы по столбцу.
Импорт прайс-листа
Добавление в каталог нескольких сотен товарных позиций может быть большой проблемой и занимать большое количество времени, если это делать вручную. Кроме того, сотрудники фирмы, предоставляющие риэлтерские услуги, возможно, предпочтут поддерживать в актуальном состоянии прайс-листы в формате Excel, чем постоянно исправлять товарные позиции на сайте. Решением данных проблем может служить импорт существующего прайс-листа в формате Excel. Ранее, в главе % уже рассматривалась процедура импорта данных из Excel через промежуточный формат CSV.
Файл формируется в MS Excel, а затем данные нз этого файла помещаются в базу данных путем импорта через соответствующую страницу. Страница импорта открывается по ссылке Добавить квартиры из CSV-файла, расположенной над списком квартнр (см. рис. 8*5). Страница импорта показана на рис. 8.7.
Рис. 87 Страница импорта квартир из CSV-файла
Данная страница содержит форму для загрузки файла в формате CSV, в котором содержится список добавляемых квартир.
工 Пр—менанме
При эагруэке CSV-файла существующие данные в группе, в которую происходит загрузка квартир」будут удалены
Загрузите CSV-файл в поле файл. В поле разделитель занесите символ, ко* торый используется как разделитель в загруженном CSV-файле. Нажмите кнопку Добавить.
CSV-файл (Comma Separated Value) — это текстовый файл, в котором данные построены следующим образом: строки файла представляют собой строки таблицы, а столбцы таблицы разделены в файле заранее определенным символом-разделителем, например Так как символ-разделитель может быть произвольным, то для правильного импорта CSV-файла символ-разделитель необходимо внести в соответствующее поле формы.
8-4-1. Совершенствование системы администрирования
При разработке системы администрирования будем исходить из того факта, что в актуальном состоянии поддерживается прайс-лист в формате ExceL Посетители сайта могут либо загружать файл в формате Excel, либо просматривать каталог продукции на сайте. Через определенные интервалы времени, раз в день или раз в неделю, в зависимости от динамики изменения информации в прайс-листе, происходит его экспорт в базу данных.
Так как Excel сохраняет в формате CSV только один лист ——за один раз будут импортироваться товарные позиции только в один подкаталог с уничтожением всех предыдущих записей таблицы product, которые относятся к обновляемому каталогу.
Для реализации механизма импорта понадобятся два файла:
口 addfilefbrm.php ——форма для импорта прайс-листа;
口 addfile.php — обработчик, непосредственно осуществляющий импорт из CSV-файла, переданного из формы addfilefbrm.php.
Форма addfilefbrm.php полностью аналогична представленной в листинге 7.24 форме addtablefbrm.php. Ее обработчик addfile,php представлен в листинге 8.19.
инг 8.19. Файл addfile.phpинг 8.19. Файл addfile.php
инг 8.19. Файл addfile.php
инг 8.19. Файл addfile.php
<?php
// Осуществляем соединение с базой данных require_once ("…/config ・ php");
if (empty ($_FILES「filename * ] [ * 5p_name * ])) puterror (^Ошибка при отправке csv-файла'1);
// Открываем временный файл на сервере
if (I ($fp = fopen ($_FILES [1 filename1 ] [1 W_name11, nrbH))) puterror (г,Ошибка при открытии отправленного файда");
// Читаем содержимое файла
$buffer к fread($fp, filesize($_FILES[1 filename1][1tnp__name1]))r
// Закрываем файл fclose($fp);
// Удаляем файл
unlink ($_FILES [,filename']【'tmp_name']);
// Разделителю присваиваем по умолчанию значение ";“
$separator а О
// Если передан параметр separator, изменяем значение
// переьденной $ separator
$separator = $_POST['separator1];
// Если имеются пустые позиции, забиваем их прочерком "-"
// В начале файла
^buffer = str_replace(п\пи.$separator, "\n-"•$separator, $buffer);
// В середине файла
$buffer = str_replace($separator.$separator, $separator.n-"* $separator, $buffer);
// В конце файла
$buffer = str_replace($separator.и\п*\ $ separator. ^-Xn11, ^buffer);
// Разбиваем файл по строкам, каждую из которых заносим
// в отдельный элемент временного массива $strtirp
$tok ^ strtok($buffer# H\nv);
while ($tok)
{
$tok = strtokt11^11);
$strtmp[] = $tok;
}
// Начало формирования SQL-запроса на вставку данных из // CSV-файла
$insert_query = "INSERT INTO product VALUES ”;
// Разбиваем строку по отдельным словам, используя
// разделитель $separator
foreach($strtmp as $value)
{
// Если строка пуста# выходим из цикла. Пустые строки могут
// появиться, если в конце CSV-файла находятся пустые строки, if(empty($value)) break;
// Определяем район по первым трем буквам его названия switch(substr(strtolower(strtok($value,$separator)), 0, 3))
case 1 кан*: $district = break;
case * ни黨': $district = break;
case 1cos1: $district - break;
case *при*: Sdistrict = break;
case 'мое': $district = break;
case 1 авт *: $district = break;
case 'лен': ^district н break;
case 'cop': $district =
"kanavinskii";
"nizhegorcdski土
“SQvetskii";
"priokskii*1;
"moskovskii";
Mavtozavodskiin
"leninskii”;
"sormovskii";
break;
// Адрес
$address ^ strtok($separator);
// Этаж
$tok = strtок($separator);
$floor н substr($tok, 0, strpos($tok, ”/"));
〃 Этажность дома
$floorhouse = substr($tok, strpos($tokf "/") + 1);
〃 Материал дома
switch(substr{strtolower(strtok($ separator)), 0, 3)) (
case 'кир甘:
$material = "brick";
break;
case 'пан*:
$material - "concrete11;
break;
case 'moh':
$material = иreconcrete"; break;
)
〃 Количество комнат
$rooms ^ strtok($separator);
// Площадь
$tok = strtok($separator);
$square_o = substr($tokf 0, strpos ($tok, rt/M));
$squarej = substr($tok,
strpos ($Sk# "/”)+1,
strlen ($tok) - strrpos ($tok, **/*') - 1);
$square__k = substr ($tok, strrpos ($tok, *7")+1);
〃 Санузел
switch(substr(strtolower(strtok($separator)), 0, 1)) {
case *c1:
$su = "separate";
break;
case *p1:
$su = "combined"; break;
}
// Лоджия/Балкон
switch(substr(strtolower(strtok($separator)), 0, 1))
case ’л':
$balcony = "loggia";
break;
case '6':
$balcony = "balcony"; break;
}
// Цена за кв.метр
$tok = strtok($separator);
$pricemeter = substr($tok, 0, strlen{$tok) - 1);
$currency и substr($tok, strlen($tok) - 1);
// Удаляем пробелы из строки
$pricemeter = str_replace(м ", "", $pricemeter);
// Общая цена
$tok = strtok($separator);
$price = substr($tokr 0,strlen($tok)-1};
$price 工 str^replacet11 ", "3 $price): // Валюта
if($currency = "$”) $currency ^ "USD";
else $currency = "RUR";
// Примечание
$note = strtok($separator);
// Преобразуем прямые кавычки в обратные $note = str_replace("'", "、”, $note);
^district = str_replace(M*и, "'% $district);
$address = str_replace("'",$address);
// Форьаируем и выполняем SQL-manpoc на добавление позиции $ inse г t_que г у 宀"(NULL,
1$note I ^district1, 1$address,, $square__o, $ square」, $square_k, $ rooms, $floor,
$floorhouse, 1$material,r
^balcony1, $price, $pricemeterr '$currency I * show*I 0,
". $_POST「id_catalog」・”),";
//Из запроса $ insert_query удаляем последнюю запятую
$insert_query = substr($insert_query, 0, strlen($insert_query) - 1);
// Удаляем записи из таблицы product, принадлежащие данному подкаталогу $query__delete ^ "DELETE FROM product
WHERE id_catalog=M.$_POST[* id_catalog'];
mysql_query($query_delete);
// Выполняем многострочный оператор INSERT if (mysql_query ($insert_query))
(
// Осуществляем автоматический переход на страницу администрирования // текущего каталога echo n<HTMLXHEAD>
<МЕТА HTTP-EQUIV=1 Refresh1 CONTEND10; URL=index.php?
idj)arent=ff. $_POST [r idcatalog1 ]. M1 > </HEADX/HTML>u;
}
?>
После передачи CSV-файла на сервер его содержимое размещается во временной переменной $buffer, сам файл при этом уничтожается. Не все поля могут иметь значение, например, примечание может отсутствовать. В связи с этим пустые значения необходимо заменить на прочерк ”一”. Осуществляется это при помощи трех операторов str_replace о, заменяющих пустые значения полей в начале, середине и конце строки на символ
Далее при помощи функции strtok()содержимое строки $ buffer разбивается на подстроки, заканчивающиеся символом переноса строки ”\п”. Полученные подстроки размещаются во временном массиве $strtmp, который обрабатывается в цикле foreauh. Основная задача этого цикла разбить строки, размещенные в массиве $strttnp на подстроки, разделенные снмволом Sseparator, который задан в форме addfileform.php, и преобразовать полученные значения в формат, необходимый для добавления товарной позиции в таблицу product. В результате работы цикла в переменной $insert__query
384 Часть Н. Разработка алстемы адммнистр酬рованвх ca询та формируется многострочный оператор insert, который выполняется после удаления всех записей таблицы product, соответствующих текущему подкаталогу. В конце работы скрипта происходит автоматический возврат к странице администрирования текущего каталога.
Поиск по каталогу
Каталог продукции был бы незаконченным, если бы не предоставлял сервиса поиска по товарным позициям. В данном разделе будет рассмотрена система поиска по ряду критериев.
Поиск осуществляется по содержимому полей базы данных, описывающих каждую квартиру, и не зависит от подкаталога, в котором квартира расположена.
Страница поиска по каталогу (рис. 8.8) содержит форму поиска, поля которой соответствуют столбцам таблицы представленной на рис. 8.2.
Рис. 8.8. Форма поиска по каталогу
При поиске квартиры посетитель может установить критерии поиска в по* лях формы, например, задать район, количество комнат, ценовой диапазон как самой квартиры, так и квадратного метра и т. д. После нажатия кнопки Искать, если поиск успешен, под формой поиска будет выведена таблица с результатами (рис. 8.9).
Рис. 8.9. Результаты поиска
Таблица с результатами поиска полностью аналогична таблице квартир, отображаемой при "ручной" навигации по каталогу (рис. 8.2).
Если поиск не дал результатов, вместо таблицы квартир будет выведено сообщение — "Понск не дал результатов, попробуйте изменить критерии по- иска1*.
При разработке системы поиска для удобства форма и ее обработчик будут расположены в одном файле searchfbrm.php, содержимое которого представлено в листинге 8*20.
<?php
// Устанавливаем соединение с базой данных require_once("* ・/config.php");
?>
<html>
<head>
<title>riOMCK</title>
</head>
<body>
<form action-searchform.php method=post>
<input type="hidden” name='*id__parentH value=,,<? echo $idjparent ?>"> <table><td>
Район :
<select type-text name«district>
<option value®1none1
<?php if($_POST[district*]==*none1) echo "selected”; ?» не имеет значения
<option value®'kanavinskii*
<?php if ($_POST[ district* ] = *kanavinskii*) echo "selected"; ?» Канавинский
<option value=fnizhegorodskii'
<?php if ($_POST[ district1 ] = *nizhegorodskii,) echo Mselected11; ?» Нижегородский
<option value='sovetskii'
<?php if ($_POST[district*З^1 sovetskii*) echo "selected”; ?» Советский
<option valuer*priokskii*
<?php if ($_POST['district']K‘priokskii') echo "selected**; ?» Приокский
<option value=1moskovskii *
<?php if ($_POST[ * district * ] = *moskovskii*) echo "selected”; ?» Московский
<option value='avtozavodskii *
<?php if ($_POST[•district1 ] = *avtozavodskii•) echo "selected"; ?» Автозаводский
<option value-'Leninskii'
<?php if ($_POST[ district* ] = ,leninskii1) echo “selected"; ?» Ленинский
<option value= * sormovskii•
<?php if($ POST[1district1] = *sormovskii*) echo "selected"; ?»
Сормовский
</selectxbr> количество комнат :
<select type=text name=rooms>
<option value=0 <?php<option value=0 <?phpif ($_POST「rooms,] =0)if ($_POST「rooms,] =0)echo "selected1*;echo "selected1*;?»?»
<option value=0 <?php
<option value=0 <?php
if ($_POST「rooms,] =0)
if ($_POST「rooms,] =0)
echo "selected1*;
echo "selected1*;
?»
?»
не имеет значения
<option<option<option<option<option<option<option<option<option<option<option<optionvalue=lvalue=lvalue=2value=2values3values3value«4value«4value=5value=6value=5value=6<?php <?php <?php <?php <?php <?php<?php <?php <?php <?php <?php <?phpif {$_FOST Г rooms' ] =1) if ($_POST [' rooms 4=^2) if ($_POST [1 rooms1 ]=3) if ($_POST [• rocffns 4=4) if ($_POST [' rooms' ] =5) if ($ POST [ * rooms 1 ] =6)if {$_FOST Г rooms' ] =1) if ($_POST [' rooms 4=^2) if ($_POST [1 rooms1 ]=3) if ($_POST [• rocffns 4=4) if ($_POST [' rooms' ] =5) if ($ POST [ * rooms 1 ] =6)echo selected*1; ?»1echo selected*1; ?»1echo MselectedK; ?»2echo MselectedK; ?»2echo "selected"; ?»3 echo "selected0; ?»4 echo "selected”; ?»5 echo "selected"; ?»6echo "selected"; ?»3 echo "selected0; ?»4 echo "selected”; ?»5 echo "selected"; ?»6
<option
<option
<option
<option
<option
<option
<option
<option
<option
<option
<option
<option
value=l
value=l
value=2
value=2
values3
values3
value«4
value«4
value=5
value=6
value=5
value=6
<?php <?php <?php <?php <?php <?php
<?php <?php <?php <?php <?php <?php
if {$_FOST Г rooms' ] =1) if ($_POST [' rooms 4=^2) if ($_POST [1 rooms1 ]=3) if ($_POST [• rocffns 4=4) if ($_POST [' rooms' ] =5) if ($ POST [ * rooms 1 ] =6)
if {$_FOST Г rooms' ] =1) if ($_POST [' rooms 4=^2) if ($_POST [1 rooms1 ]=3) if ($_POST [• rocffns 4=4) if ($_POST [' rooms' ] =5) if ($ POST [ * rooms 1 ] =6)
echo selected*1; ?»1
echo selected*1; ?»1
echo MselectedK; ?»2
echo MselectedK; ?»2
echo "selected"; ?»3 echo "selected0; ?»4 echo "selected”; ?»5 echo "selected"; ?»6
echo "selected"; ?»3 echo "selected0; ?»4 echo "selected”; ?»5 echo "selected"; ?»6
</selectxbr>
цена общая, руб* :
от <input type=text name=price_min value=<?= $_POST [ fpricejiiin1 ] ?» до <input type=text name=price_max value=<?= $__POST [ ^price^ax* ] ?»<br> цена за кв.метр, руб.:
от <input type^text name=priceineter^nin valuer
<?php echo $_POST [1 pricemeterjnin' ]; ?»
до <input type=text name=pricemeter_max value"
<?php echo $_POST [1 pricemeter^ciax1 ]; ?»<br>
</td><td>
этаж : <input type=text name^floor value=<?php echo $_POST[* floor']; ?» санузел :
<select type=text name=su>
<option value=*nonef
<?php if ($_PQST [1 su1 ] =f none *) echo "selected11; ?»
не имеет значения
<option value='separate'
<?php if($_POST[1su*]= 1 separate1) echo "selected"; ?»раздельный
<option value® * combined'
<?php if ($_PGST [' su,)=' combined1) echo nselectecl,,; ?»cовмещенный
</select><br>
лоджия/балкон :
<select typestext пагпе=Ьа 1 cony>
<option value=1 none *
<?php if ($_POST [T balcony * ] =0) echo "none"; ?»не имеет значения
<option value='balcony1
<?php if ($_POST (* balcony1 ]== balcony*) echo 11 selected11; ?»балкон
<option value=* loggia *
<?php i f ($_POST [1 balcony1 ] = * loggia1) echo Mselected11; ?»лоджия
</select><br>
Характери стика :
<select type=text name=material>
<option value=1 none1
<?php if ($_FOST[ Material11 ^'none,) echo "selectedH; ?» не имеет значения
<option value=,brick*
<?php if ($_POST[1 material* ]==гbrick1) echo "selected**; ?»кирпичный
<option value='concrete'
<?php if ($_POST[ • material1 ] ===1 concrete1} echo "selected1*; ?» панельный
<option value='reconcrete'
<?php i f($_POST[* material1] = * reconcrete1) echo "selected"; ?» монолитный
</select>
</td>
<td colspan=2>
<input class=buttonpol1 typ&=submit va1ие=Искать>
</tdx/tr>
</table>
<input type=hidden name=search value=search>
</form>
<?php
// Скрипт-обработчик поискового запроса
// из формы
if (isset($_POST[1 search " )}
{
?>
<a name=table></a>
<hl class= * zagf >Результаты поиска</Ы>
<?php
// Флаг равен true, если есть хотя бы один критерий поиска
$is_query = false;
// Проверяем наличие и число параметров поиска
// Район
$Ёлф1 - $tmp2 - $tmp3 = $tinp3 = $tmp4 = $tmp5 = $tmp6 = $tmp7 = $tmp8 $tmp9 к $tnplO = $tmpll = $tmpl2 = $tmpl3 ^ $tinpl4 ^ $tmpl5 ="”; if (isset ($_POST [district1 ]) && $_POST [1 district1 ] I =1 none1)
$twl = " and district= * " * $_POST [1 districtT ] * H 1 и ;
// Площадь
if (isset ($_POST[1 squaxe_o」riin' ]) }
$tirp2 = " and square_o>*'. $_POST [ * square__ojnin ";
if (isset ($_POST[' squa匸е_о_шах‘ ]))
$twp3 = r, and square_o<*'. $_POST「square_ojnax * ];
if (isset ($_POST (1 squarej^rnin1 ]))
$tmp4 = ” and square_j>fl. $_POST [1 号quare_j』in'];
if (isset ($_POST [ * square_j_maxf ]))
$tmp5 = ” and square_j<rt. $_POST [1 square__j_inax1 ];
if (isset ($_POST [1 square_k_min1 ]))
$tmp6 = ” and square_k>H. $_POST [ * square_kjnin1 ];
if (isset ($_POST [' square_k_rnax1 ]))
$tmp7 = ” and здиаге_к<и. $_POST [f square_k_inax * ];
// Количество коьлнат
if (! empty (5_POST [1 rooms1 ])) $tn^>8 - ” and rooins=n, $_POST [1 rooms'];
// Этаж
if (!en^ty($_POST [1 floor1 ])) $tnip9 = ” and floor=,1 .$_POST[1 floor* ];
// Санузел
if(•empty($_POST[,su,]) && $_POST['su'] != Fone"
$tmplO = ” and su=f ,f.$_POST (rsu* ] .n,n;
〃 Характеристика
if(isset($_POST(,niaterialt1) && $_POST[1 material1] != 1none1)
$tmpll = " and material=1H.$_POST[*material*].”,”;
// Цена
if (! empty ($_POST [ ,price_minl ]))
$tmpl2 = ” and price>H.$_POST[1price_min,];
if(! ei叩 tу($_POST[*price_max']))
$tinpl3 = ” and price<*f * $_POST [1 price_niax1 ];
if {! empty ($_POST [ * pricemeter_jnin1 ]))
$tmpl4 = ” and pricemeter»1. $_POST「pricemegr^pin」;
if (! empty (§_POST [,pricemeter_maxf ]))
$tmpl5 ^ ” and pricemeter<". $__POST [f pricemeter__max * ];
// Формируем запрос из переданных данных
$ query = 11 SELECT + FROM product
WHERE hide=,show,
".$tmpll.$tmpl*$tn^)2・$tmp3・$tmp4 .$tn^>5.$ti^6• $tmp7,$tmp8.
$tnp9, $tmpl0. $tmpl2 , $5Pl3 ・ $ tmpl 4 • $tmpl5 ・" ORDER BY pos";
// Выполняем SQL-запрос $prt ■ mysql_query($query);
if(!$prt) puterror(^Ошибка при обращении к таблице риэлтерских услуг11); // количество рядов в наборе должно быть больше нуля
if (mysql_num_rows ($prt)>0) (
?>
<table>
<tr>
<td>Koл.комн.</td>
<td>PaiioH</td>
<td>Anpec</td>
<td> (0) </td>
<td> (Ж) </td>
<td>(K)</td>
<td>3Ta«</td>
<td>3T.zoMa</td>
<td>Ma териал</td>
<td>C/Y</td>
<Ъс1>Ло,1ркия/Балкон </td>
<td>UeHa,M. kb .</td>
<td>Ij£Ha, общ* </td>
<td>Baлюта</td>
<td>npwM. </td>
</tr>
<?
while($par = mysql_fetch_array($prt))
(
// Определяем район switch ($par[* district']) ( case 『kanavinskii':
$distr = мКанавинский11;
break;
case ^izhegorodskii*:
$ distr = ''Нижегородский";
break;
case *sovetskii*:
$distr н "Советский";
break;
case 'prickskii':
$distr = "Приокский";
break;
case 'moskovskii':
$distr = "Московский";
break;
case 1avtozavodskii *:
$ distr = н Автозаводский1*;
break;
case *leninskii':
$distr = "Ленинский";
break;
case 'sormovskii•:
$distr = "Сормовский";
break;
}
// Определяем материал дома
switch ($par['material*])
case 1brick*:
$_POST[1material•] = "кирп.";
break;
case *concrete':
$_POSTC material1] = "панел., break;
case 'reconcrete':
$_POST[,material1] = "монолит."; break;
}
// Определяем тип санузла
switch ($par["su*])
case 'separate':
$_POST['su」="разд.";
break;
case 1combined*:
$_POST[,su, ] = ”8В・”; break;
}
// Определяем наличие балкона
switch ($рзг[1baIcony*))
{
case balcony1 :
$_POST['balcony,] = "балкон";
break;
case 1loggia*:
串_POST [ * balcony1 ] = м лоджия**; break;
}
echo M<tr>
<td>*\ $par [ * rocsns1 ]・”《/td> <td>$distr</td>
<td>".$par「address,1・"</td>
<td>n.$par[* square_o*)."</td>
<td>".$par[1square_j'j."</td> <td>'\ $par f square_k,] . n</td>
<td>".$par[т floor1].H</td>
<td>".$par「floorhouse1]."v/td> <td>”. $_POST [ *Tnaterialf ] J*</td> <td>,r.$_POST[ ^u1] J</td>
<td>**. $_POST [ balcony' ] . "</td> <td>M* $par [1 pricemeter1J. "</td> <td> " . $par [ * price * 1 . n</td> <td>n,$par[* currency*]."</td> <td>r,. $par [1 note ■ ] . ”v/td>
</tr>K;
}
}
else echo "Поиск не дал результатов.
Попробуйте изменить критерии поиска.";
echo "</table>M;
}
?>
</body>
</html>
Как видно из листинга 8.20, в начале файла searchfbrni.php размещена HTML-форма, которая среди прочих параметров передает параметр search, устанонленное значение которого является сигналом для обработки поступивших из формы данных. Критерии поиска оформляются в 15 временных переменных от $tmpi до $tmpi5, которые далее используются для формирования SQL-запроса на извлечение информации из таблицы product.
Вывод товарных позиций, как и в файле product.php (см. листинг 8J8), осуществляется в виде таблицы.
Глава 9Глава 9回
Глава 9
Глава 9
Система полнотекстового поиска по сайту
Любому более или менее крупному Web-pecypcy требуется мощная система поиска, так как со временем поиск нужной страницы начинает отнимать у посетителей слишком много времени. В данной главе будет рассмотрена система полнотекстового поиска по сайту» опнравтаяся в своей работе на ранее разработанную систему CMS (см. главу "
Полнотекстовый поиск
в СУБД MySQL
Полнотекстовый поиск в СУБД MySQL на сегодняшний день поддерживается только для таблиц типа MylSAM и только для текстовых столбцов переменной длины (text и varchar). Эта возможность активируется созданием индекса типа fulltext для столбцов, по которым необходимо осуществлять поиск.
[З.мечанме )
Полнотекстовый поиск в СУБД MySQL не чувствителен к регистру. Кроме того» при поиске игнорируются так называемые мобщеупотребительныеи слова. К ним относятся слишком короткие слова (по умолчанию состоящие меньше чем из четырех символов), а также слова, встречающиеся, по крайней мере, в половине записей. Так, если в таблице имеется только две записи, то поиск не даст результатов, так как каждое слово будет присутствовать в половине записей.
Для использования возможности полнотекстового поиска в системе CMS необходимо создать индексы fulltext для поля name таблиц articles и paragraphes (см. листинги 7.2 и 7,3). Изменить уже существующие таблицы можно при помощи SQL-оператора alter (см. главу 2). Для таблиц articles и paragraphes создать индекс для поля паше можно при помощи запросов, приведенных в листинге 9.L
для поля name
ALTER TABLE articles ADD FULLTEXT name (name);
ALTER TABLE paragraphes ADD FULLTEXT name (name);
В листинге 9.1 сразу после ключевого слова fulltext следует имя создаваемого индекса, за которым в скобках следует имя текстового столбца.
Индекс fulltext можно создать сразу по нескольким столбцам* Так, если поиск следует осуществлять не только по названию статьи, но и по ее описанию, то в скобках после имени индекса следует указать имена двух столбцов через запятую: ALTER TABLE articles ADD FULLTEXT name (name, description);.
( Замечание J
Если индекс создается только по одному столбцу, и его имя совпадает с именем столбца, то имя индекса, размещаемое после ключевого слова fulltext, можно опустить.
На число индексов в таблице ограничений не накладывается, допустимо создание нескольких индексов:
ALTER TABLE articles ADD FULLTEXT (name),
ADD FULLTEXT (description),
ADD FULLTEXT together (name, description);
Поиск выполняется при помощи встроенной функции match(), в качестве аргумента которой выступает один или более индексов типа fulltext, а также функция against(), которая принимает в качестве аргумента искомую строку (листинг 9.2).
j^M^^^B^^^^tf^^^MlilY№<JMiWlWmMtffTMfWtMMWWWi№呵飞Mflft%**i* 解*1*MftWWiK种'i^fly生¥*-* 竺 Д睛*^]瑛至£哽1也逑^ S^'Yffll1軍**田*^脚迂~*}£望冷"* ItTWiHi^i^^W^1*,,止MW1
Листинг 9.2. Оператор select, осущестал яющий полнотекстовый поиск
WWmf^^nW№'iifc?^ff?^^^^^W*^^^№W^ffrrt*iV.^CTRwEWlMwO{*'»i¥»4wWMiK?WiroTiT<»»i¥*rf#M.i'»i«}ii*j'»*i5Jwrtf*:#'MW»*»*<iiHm'ii<3KwRMlww3Rir^^^Wfw<Wflrti^^^rK
SELECT * ЕКОМ articles WHERE MATCH (name) AGAINST (1 программирование *);
В листинге 9.2 из таблицы articles извлекаются все записи, соответствующие статьям, в названии которых встречается слово ^программирование". Синтаксис допускает использование нескольких конструкций match()- against о после оператора where, разделенных логическими операторами or ИЛИ AND (ЛИСТИНГ 9.3),
Листинг 93 Использование нескольких конструкций match()-against () после оператора where
SELECT * FROM paragraphes WHERE MATCH(name) AGAINST(* программирование1) OR MATCH (name) AGAINST (1 кодирование');
В листинге 9.2 из таблицы paragraphes извлекаются все записи, соответствующие параграфам, в теле которых присутствует либо слово 'Программирование'*, либо слово ^кодирование*'.
Замечание )
Результаты поиска сортируются в порядке уменьшения релевантности. Величина релевантности представляет собой неотрицательное число с плавающей точкой. Релевантность вычисляется на основе количества слов в данной строке столбца, количества уникальных слов в этой строке, общего количества слов в тексте и числа документов (строк), содержащих отдельное слово.
; Замечание )
Начиная с версии 4.0.1, СУБД MySQL поддерживает поиск в так называемом логическом режиме, позволяющем задать обязательное отсутствие слова в результатах запроса, изменять величину релевантности для отдельных слов и многое другое. За более подробной информацией следует обратиться к официальному справочному руководству по СУБД MySQL.
Полнотекстовый поиск в системе CMS
Результат поиска будет представлять собой список статей, в которых были обнаружены ключевые слова. При этом будет допускаться несколько режимов поиска: в названиях статей и в теле статьи с использованием логики "ИЛИ*' и "И", если число заданных ключевых слов больше одного.
:Замечание )
Ключевые слова, по которым осуществляется поиск, следует разделять пробелами.
Здесь, как и при поиске в каталоге "Рнзлтерскне услуги*', форма для поиска и ее обработчик будут размещена в одном файле searchfbrm.php, содержимое которого представлено в листинге 9.4.
T ;'嘴,父";'*:**!:畤":工":*-*"**:士'" "К《•Цй*:*?寞b **,m* Yi'3'2产:豐*"专УЛ',*'*?]?""*:*”':"?二*"*F*'***",:::y :…… …"……:".*'V:'**!: …У e'£"
Листинг 9.4. Файл searchform.php
:.Z "二…:"….….…;;..":..工篇;t熊£靑h富;3爲*a廳爲总;;.;父爲"新:;二.・心“.;七.,.“;府:;三,备工?.:;.;,8,心“"“•斤“±»,注,".":♦"
<?php
// Устанавливаем соединение с базой данных
include "config.php”;
// Определяем константы
// Логика поиска
define(LOLIC OR,1); // или
define(LOGIC_AND,2); // и // Режим поиска
define(TITLES,1); // В названиях
define(ARTICLES,2); // В статьях
// Устанавливаем переменные формы
$name = $_POST 11 name1 ];
$ number themes = $_POST [ *nuinbert hemes * ];
$srchwhere = $_POST[* srchwheref];
$id_page = $_POST['土d_pag巳'1;
$logic = $_POST[flogic']; ?>
<html>
<head>
<title>noMCK</title>
</head>
<body>
<table>
<form action^srchform.php method^post> etr>
<td>KniO4GBbie слова</td>
<td><input type=text name=name value=*<?php echo $name; ?>f></td> </tr>
<tr>
<td>KojiM4ecTBO выводимых статей</Ы>
<tdxinput type=text name=numberthemes value= <?php
if (empty ($numbert±ieines)) echo 30; else echo $nuraberthen^s;
?»</td>
</tr>
<tr>
<td>J4cxaTb s</td>
<td>
<select type-text name^srchwhere>
<option value=
<?php
echo TITLES;
if($srchwhere = TITLES) echo "checked”;
?»
в названиях статей
<option value=
<?php
echo ARTILES;
if ($srchwhere 一 ARTICLES) echo **checked**; ?»в статьях
</select>
</td>
</tr>
<tr>
<td>rpynna статей</td>
<td>
<select type=text name=id_page> coption value=0>He имеет значения <?php
// Формируем выпадаюощй список для выбора раздела поиска $рдз = mysql_query(иSELECT * FROM artpages
WHERE hide=,show,
ORDER BY PQS”); if($pgs) (
while($pages = mysql_fetch_array($pgs)) {
if($pages[*id_page1] ” $id_j)age) $strtmp = "selected"; echo $strtmp ="";
echo H<option value-0.$pages「id_page']・"$strtn^> ".$pages[* id_page *];
}
}
?>
</select>
</td>
</tr>
<tr>
<td>norKKa</td>
<td>
<input name=logic type=radio valuer
<?php echo LOGIC AND;
if($logic = UXHJAND) echo "checked"; ?» ИЛИ<Ьг>
<input name^logic type=radio valuer
<?php
echo LOG工C_CR;
if ($logic н LOGHC_OR) echo иchecked'*; ?»M</td>
</td>
</tr>
<tr><td> </td>
<td><input type=submit name=send value₽HaiiTM></td></tr>
</fom>
</table>
<?php
if (!empty ($nanie))
{
// Выбираем логику поиска, если задано несколько ключевых слов
if($logic == LOGIC_OR) $logstr = "OR";
else $logstr ^ "AND";
// Разбиваем поисковую фразу на подстроки, разделенные пробелам $name = trim(^name);
$temp = strtok($name, '* ”);
while ($temp)
{
// Полнотекстовый поиск по столбцу name тайпицы articles
// Формируем поисковую часть SQL-запроса» учитывая логику,
// заданную во временной переменной $logic
if t$is_query) ^search 宀"$logstr MATCH (name) AGAINST {'$temp')";
else $search .= "MATCH {name) AGAINST (^Ёегф1)H;
$is_query 匸 true;
$temp 工 strtok(11 “);
}
// Формируем фрагмент SQL-запроса, ответственный за поиск
// в определенном разделе, если значение $id_page не установлено,
// не имеет значения раздел и поиск осуществляется по всем разделам*
if (! en^ty ($id_j>age) ) $pagetinp = " AND id_page = $id_page";
// В зависимости от режима поиска формируем SQL-запрос switch($зrchwhere)
case TITLES: // Поиск в названиях статей
〃 ФорФйируем SQL-запрос
$query = "SELECT * FROM articles
WHERE $search
AND id_foruniF$id_fonnn
AND hide=1 show *
$pagetmp
ORDER BY time DESC LIMIT $numberthemesn;
$src = mysql__query ($query); break;
}
case ARTICLES: // Полнотекстовый поиск по сообщениям
〃 Формируем SQL-запрос
$query = "SELECT id_article FROM paragraphes WHERE $search AND hide=* show1 $pagetirrp
GROUP BY id_articleM;
$par = mysql_query ($query);
if (Spar) puterror (^Ошибка при выборке параграфов...*'); $nmntc>t = mysql_mnn_rows ($pa r);
if($numtot>0)
{
$query = "SELECT * FROM articles WHERE id__article IN (”; while($art = mysql_fetch_array($par))
?query ・=$art(1id_article1]*"/n;
)
$ query .= ”0) AND id_forum=$id_forum AND hide=1 show1 GROUP BY idjheme ORDER BY time DESC LIMIT $numberthemes H;
$src = mysql_query($query);
} else echo "По запросу ничего не найдено J;
break;
// Выводам список найденных статей
if($src) puterror("Ошибка при выборке статей.* * п);
// Выясняем количество найденных статей
$numtot = mysgl_nura^rows ($src); if($numtot>0)
(
while ($art = mysql_fetch_array ($src)) echo "<a href=airt:.php?
id^rticle^-'.^artt1 id_article']・">
11. $art [f name,]. "</axbr>";
}
} else echo пПо запросу ничего не найдено";
}
?>
</body> </h51>
В начале файла searchfbrm^php, приведенного в листинге 9.4, после установки соединения с базой данных за счет включения конфигурационного файла config.php, описанного ранее, размещается объявление четырех констант:
□ lolic_or — использование логики “ИЛИ” при поиске, включающем более одного искомого слова;
О logic_and ——использование логики иИ" при поиске, включающем более одного искомого слова;
□ titles ——поиск в названиях статей;
口 articles — поиск по всему тексту статей.
После этого из суперглобального массива §_post извлекаются параметры, передаваемые из HTML-формн файла search form. php. HTML-форма, которая следует ниже, СОСТОИТ ИЗ ДВУХ текстовых Полей: name И numberthemes, для ключевых слов и максимального числа выводимых статей соответственно. В HTML-форме присутствуют два выпадающих списка srchwhere и id_page, первый нз которых позволяет задать режим поиска (в названиях или тексте статей), а второй —— группу статей, в которой осуществляется поиск* Элементы списка id_page формируются динамически, за счет извлечения записей, соответствующих группам из таблицы artpages* Завершает HTML-форму группа из двух переключателей logic (позволяющих задать логику поиска ключевых слов) и кнопка отправки параметров формы обработчику.
При поступлении в файл searchfbrm.php параметров из HTML-формы, размещенных в первой части файла, происходит проверка переменной $патеэ содержащей ключевые слова. Если данная переменная непустая, происходит формирование SQL-запроса, осуществляющего поиск ключевых слов по базе данных системы CMS.
При помощи ранее описанной функции strtokO осуществляется разбивка строки запроса на отдельные ключевые слова. При этом в цикле while происходит формирование фрагмента поискового SQL-запроса» который размещается во временной переменной $search. В данной переменной размещаются SQL-конструкции вида MATCH (name) AGAINST (• ключевое^слово*), разделенные в зависимости от выбранного режима поиска ключевыми словами AND или OR.
Далее во временную переменную Spagetmp помещается условие для конструкции where, ограничивающее поиск определенной группой статей. Если при поиске был выбран пункт "Не имеет значения", данная переменная останется пустой.
Переменная $srchwhere может принимать два значения: titles и artiles, задающих режим поиска по заголовкам статей или по всему тексту. В зависимости от принимаемого значения, при помощи конструкции switch происходит либо формирование и выполнение SQL-запроса, извлекающего все записи таблицы articles, в названиях которых обнаружены ключевые слова, либо извлечение полей id_article параграфов, в которых обнаружены ключевые слова. В последнем случае при помощи SQL-оператора in происходит извлечение записей из таблицы articles, соответствующих статьям, в которых имеются искомые ключевые слова.
С Зам&чание )
Оператор field IN (list) используется после ключевого слова WHERE. Оператор извлекает из таблицы только те записи, поле field которых содержится в списке list.
Какой бы режим поиска не был выбран, в результате выполнения поискового запроса после конструкции switch будет получен дескриптор $src, с помощью которого можно извлечь статьи, удовлетворяющие поисковому запросу.
Завершает файл цикл while, выводящий список ссылок на все найденные статьи.
Глава 10Блок "Контакты"Глава 10Блок "Контакты"
Глава 10
Блок "Контакты"
Глава 10
Блок "Контакты"
Одна из основных функций любого корпоративного Web-сайта (сайт фирмы или организации)——предоставление контактной информации. И чем больше фирма, тем разнообразнее будет контактная информация. Отделы сбыта, снабжения, маркетинга, рекламный отдел, секретари — на сайте должна быть предоставлена информация обо всех лицах и подразделениях, бизнес- функции которых тесно связаны с внешним миром.
Одно из главных качеств контактной информации —— достоверность. Можно забыть указать одну из функций фирмы, забыть указать руководителей, не поместить на сайт сертификаты и дипломы, но контактная информация —— всегда должна быть достоверной. А иначе ——зачем фирме сайт?
Достаточно распространенное явление в Интернете — указание минимума контактной информации, например, только одного контактного телефон, к примеру, телефона секретаря, переключающего обращающихся в фирму клиентов на соответствующих контактных лиц. Отчасти такая ситуация связана с опасениями владельцев сайта, что за изменениями большого объема контактной информации будет сложно уследить н оперативно внести соответствующие изменения в сайт. В этой главе будет разработано Web- приложение "Контакты", предназначенное для публикации на сайте большого объема разнообразной контактной информации и позволяющее максимально быстро вносить изменения на сайт, причем это могут делать лица, владеющие минимальными навыками работы с компьютером.
Определим требования к контактной информации, публикуемой на сайте.
口 Информация может быть разбита по группам, которые соответствуют отдельным подразделениям компании или отдельным направлениям ее дея・ тельности. В каждой группе может быть несколько позиций с контактной информацией.
口 Контактная информация может быть различной: ФИО и занимаемая должность, адрес, телефон, e-mail, URL, фотография. При этом вывод должен осуществляться только для тех позиций, для которых имеется информация в базе данных.
Как и все предыдущие Web-приложения, блок "Контакты11 разделяется на две части:
口 система представления информации;
口 система администри рова н и я.
Система представления информации
На главной странице Web-приложения "Контакты” отображены названия групп контактов (рис. 10.1).
Рис. 10*1. Главная страница Web-приложения "Контакты"
Группа контактов — это объединенная по какому-либо признаку группа контактных лиц. Например, в группе "Руководство” может содержаться информация о директоре, его заместителях, главном бухгалтере м т. п. В группе "Отдел сбыта" может содержаться информация о руководителе и менеджерах отдела. Чтобы просмотреть информацию, в группе контактов следует щелкнуть по ссылке в виде названия группы. Выбранная группа раскроется, и на страницу будет выведен список контактных лиц и их реквизиты (рис. 10.2).
Рис. 102 Список контактных лиц выбранной группы контактов
Для того чтобы раскрыть другую группу, необходимо также щелкнуть по ссылке с ее названием. Текущая группа закроется и будет раскрыта выбранная группа контактов.
Система администрирования
Система админист)эирования предназначена для работы с контактной информацией, выводимой на сайт; добавления новой контактной информации, редактирования существующей, удаления, временного скрытия н отображения.
На главной странице системы адм и нистри рован и я группы контактов расположены в виде таблицы (рис. Ю.З).
14 3ax. II81
Рис. 10.3. Главная страница администрирования
Первый столбец таблицы содержит названия групп контактов. Во втором столбце отображается позиция контактной группы. Группы контактов отображаются на сайте по возрастанию значения этого поля. В оставшихся трех столбцах расположены управляющие ссылки: Скрыть или Отобразить, Исправить, Удалить,
Нажатие на ссылку Скрыть приводит к тому, что данная группа контактов перестает отображаться на сайте и становится доступной только со стран и ~ цы администрирования. Кроме того, в системе администрирования вместо ссылки Скрыть появляется ссылка Отобразить, с помощью которой можно снова включить режим отображения контактной группы на страницах сайта.
Нажатие на ссылку Исправить открывает страницу редактирования группы контактов (рис. 10.4).
На открывшейся странице можно изменить название и позицию группы» а также статус отображения.
Для добавления новой контактной группы следует нажать на ссылку Добавить группу контактов над телицей контактных групп (см. рис. 10.3), в результате чего будет открыта страница Добавление группы контактов.
Если заполнить поле Название и нажать кнопку Добавить, то в систему будет добавлена новая группа контактов.
Рис. 10.4. Страница редактирования группы контактов
Рис. 10.5. Страница Добавление группы контактов
Названия групп контактов (см. рис. 10.3) представлены в виде гиперссылок, по которым открывается содержимое выбранной группы контактов ——список контактных лиц с реквизитами.
Рис. 10.6. Список контактных лиц с реквизитами
Информация о каждом контактном лице выводится в виде строки таблицы. В последнем столбце каждой строки расположены управляющие ссылки: Скрыть или Отобразить, Исправить, Удалить.
Работа ссылок Скрыть, Отобразить н Удалить аналогична работе подобных ссылок на главной странице администрирования (см. рис. 103). Но в данном случае операции производятся не над группами контактов, а над нн- формацией о контактных лицах.
Нажатие на ссылку Исправить открывает страницу редактирования информации о контактном лице (рис. 10.7).
На этой странице содержится перечень реквизитов, которые могут быть у контактного лица. При необходимости можно добавить изображение. Это может быть фотография контактного лица или, например, карта проезда. Для удаления существующего изображения в это поле следует ввести символ прочерка
Отредактируйте контактную информацию и нажмите кнопку Исправить.
Для добавления нового контактного лниа следует перейти по ссылке Добавить контактную информацию, расположенной над таблицей контактных лиц (см. рис. 10.6).
S Реденгмро1мние книг шггмби нмйщыйцИк* МкгосоИ internet Explorer14阿 S
蛔л Зек* 飙I 3MHMи* 匸 в₽к 5мжа,g
^M*i f^htiT^iWort^cti^iwi/iidttoriton^phpvid^~""""' …习 口 Порелм Cm *
&аз"утье на ейтддлнннстри口овтнне с#п
Редактирование контактной информации
Има н дй/шй»С1Ь |Ибэмоа Петр Алексевич гем директорТвл«фомURLИзой]1г)Авнм«Позиция ”OfB&p^dltИма н дй/шй»С1Ь |Ибэмоа Петр Алексевич гем директорТвл«фомURLИзой]1г)Авнм«Позиция ”OfB&p^dltJ^Готово1:*jMecnrt*-Htp««(bJ^Готово1:*jMecnrt*-Htp««(bРис. 10.7. Страница редактирования информации о контактном лицеРис. 10.7. Страница редактирования информации о контактном лице
Има н дй/шй»С1Ь |Ибэмоа Петр Алексевич гем директор
Твл«фом
URL
Изой]1г)Авнм«
Позиция ”
OfB&p^dlt
Има н дй/шй»С1Ь |Ибэмоа Петр Алексевич гем директор
Твл«фом
URL
Изой]1г)Авнм«
Позиция ”
OfB&p^dlt
J
^Готово1:*jMecnrt*-Htp««(b
J
^Готово1:*jMecnrt*-Htp««(b
Рис. 10.7. Страница редактирования информации о контактном лице
Рис. 10.7. Страница редактирования информации о контактном лице
Рис. 10.8. Страница добавления контактной информацииРис. 10.8. Страница добавления контактной информации
Рис. 10.8. Страница добавления контактной информации
Рис. 10.8. Страница добавления контактной информации
В результате этого откроется страница добавления контактной информации (рис. 10.8).
По своей структуре она полностью аналогична странице редактирования контактной информации» показанной на рис* 10.7. Заполните необходимые поля и нажмите кнопку Добавить.
Разработка системы администрирования
Разработка системы будет начата с системы администрирования, позволяющей создавать, модифипировать н удалять группы и отдельные контактные позиции. Все файлы системы администрирования следует разместить в отдельной директории; кроме этого, для поддержки работы с изображениями системе необходима директория files, расположенная на том же уровне, что и директория системы администрирования.
Структуру базы данных, необходимой для создания структуры групп контактной информации, разрабатывать не потребуется. Воспользуемся структурой базы данных системы каталога продукции, разработанной в главе & Эта структура достаточно универсальна и может применяться для администрирования любых каталогов и групп. Так как разрабатываемая нами система представляет собой одноуровневый каталог, то адаптируем разработанную ранее систему каталога продукции для работы с одноуровневым каталогом.
Для работы понадобится таблица catalog (см. листинг 8,1), а также скрипты и HTML-формы управления каталогами addcat.php, addcatfbrm.php, delcat.php, editcat.php, editcatforni.php, hidecat.php и showcat.php.
[ Замечание )
Исходные коды адаптированного каталога для программного блока нКонтакты" вы можете взять на прилагаемом к книге компакт-диске.
Наполнение каталогов будет осуществляться при помощи информации из таблицы contacts, содержащей 9 полей:
id_contact ——первичный ключ таблицы, снабженный атрибутом AUTO_INCREMENT;
口 name — ФИО и занимаемая должность;
口 address — адрес офиса, где можно найти сотрудника;
口 phone — телефон сотрудника;
口 email —e-mail сотрудника;
О pos — позиция контактного блока относительно других блоков в группе;
picture — ссылка на фотографию сотрудника, карту проезда к офису и т. п.;
hide — поле, ответственное за сокрытие или отображение контактной позиции. Имеет тип enum и может принимать 2 значения: 'show1 (позиция доступна посетителям) и 'hide* (позиция скрыта);
id_catalog ——первичный ключ таблицы catalog, соответствующий группе, которой принадлежит контактная позиция.
SQL-запрос, создающий таблицу contacts, приведен в листинге ЮЛ.
CREATE TABLE contacts (
id^contact INT(11) NOT NULL AUTO_INCREMENT, name TINYTEXT NOT NULL, address TINYTEXT NOT NULL, phone TINYTEXT NOT NULL, email TINYTEXT NOT NULL, pos SMALLINT(3) NOT NULL DEFAULT *0\ picture TINYTEXT NOT NULL, hide ENUM(^how1,*hide') NOT NULL DEFAULT 'show', id_catalog INT(8) NOT NULL DEFAULT O, PRIMARY KEY (id_contact)
)TYPE=MyISAM;
Все текстовые поля таблицы contacts имеют тип tinytext, так как информация, размещаемая в данных полях, обычно не превышает 256 символов,
С Замечание
Далее в листингах для удобства восприятия будет опущен несущественный HTML-кодt связанный в первую очередь со стилевым оформлением Web- приложения. Полная версия данного Web-приложения находится на прилагаемом к книге компакт-диске, а также доступна по адресу http://www.softtime.ru/ Info/downloads.php.
Помимо приведенных выше файлов, предназначенных для работы с группами контактов, в системе администрирования также присутствуют файлы представления структуры контактной информации и управления отдельными контактными позициями:
index.php — главная страница системы администрирования, отображающая список групп и контактные лица (контактные позиции) выбранной группы;
412
contacts.php ——вспомогательный файл, включаемый в index.php для отображения позиций выбранной группы;
口 addcontfbrm.php — HTML-форма для добавления контактных позиций;
addcont.php — обработчик HTML-формы addcontfbrm.php;
delcont.php ——скрипт, удаляющий контактную позицию из базы данных;
edilcontfbrm.php ——HTML-форма для исправления контактной позиции;
editcont.php —— обработчик HTML-формы editcontfbrm.php;
口 hidecont.php —— скрипт, изменяющий статус контактной информации на ''скрытый'*;
showcont.php ——скрипт, изменяющий статус контактной информации на "доступный".
[ Замечание )
Здесь, как и в предыдущих Web-приложениях, используется вспомогательный скрипт соединения с базой данных config.php, содержимое которого приведено в листинге 7.5.
Содержимое файла index.php, ответственного за отображение контактных групп, представлено в листинге 10.2.
:;, + + *+++,.A^^W,S,,_f 3,3 :, q '尸:《[窗广 1Й^;察卡岁 2 煤匕!*^t^^^^^^^ft^^^^ 承"丈喲咨^^j^* 券置*„冢"^,i1^^*'^' ^^g^^^lf [['",,'卡 ¥泞-筋;^^^^jt』;
:Листинг 10.2. Файл index.php
1'«••«••• ^а'йЪ • f«/»C^^**<* J*««tfv^^/lM^№Z*7> i^^Te^ffn?^,. 77,.、• •* • «^Hr'*'»'* » tfn'f* * « tfl fTe^^^^vl i^^V^^r^^^^^^ .・i..“l... ^^J »<<H^»<i»»>li4>it> J^^l ^**4 ^v^TVtV •■/• *V ••♦• J
<?php
// Устанавливаем соеданение с базой данных require__once ("config.php'1);
// Извлекаем из строки запроса параметр id_parent
$id_catalog = $_GET[『id_uatalog1];
// Если параметр не задан, присваиваем переменной $id_jaarent значение // ноль, соответствующее корневому каталогу idqatent = 0 if{^npty($id^catalog)) $id_catalog 二 0;
// Если находимся в списке групп ($id_catalog == 0), то выводим ссылку // на добавление новой группы if($id_catalog == 0)
echo "<а href==addcatform.php?id_catalog=$id_catalog> Добавить группу контактов</а>**;
}
// Если выбрана группа контактной информации, то выводим ссылку
// для возврата в список групп
else echo н<а href=index.рКр>Верхний уровень</а>";
echo H<table bordersl>H;
// Формируем и выполняем SQL-запрос, извлекакшгй
// список контактных групп
$query = nSELECT * FROM catalog WHERE id_parent=$id_catalog ORDER BY pos";
$ctg ^ my sql_que ry ($ que г у);
if (!$ctg) puterror{"Ошибка при обращении к контактным группам11);
// Если в таблице catalog присутствует хотя бы одна контактная
// группа, выводим ее в таблице
if (mysql_num_rows (帛ctg) >0) {
// Выводим заголовок таблицы контактных групп
echo н<tг><td>Название группы контактев</td>
<td>no3.</td>
<td colspan=3^ei4CTBM4</td> </tr>11;
while($cat = mysql_fetcharray($ctg)}
{
// Выясняем, скрыт каталог или нет if($cat(,hidet] = 1) {
$strhide = "<a href=showcat.php?
id_cat:alc)g=" • $cat [1 id_catalog' ] .”& >0то0разить</а>м;
}
else
{
$strhide = "<a href=hidecat.php?
id_catalog=".$cat[1id_catalog'].”& >Скрьггь</а>м;
// Выводим список каталогов и управляющие ссыпки echo M<tr>
<td><a href=index.php?
id catalog®**. Scat I' id catalog* ].”>"•
$cat [Fame1 ]. n</a></td>
<td>".$cat「pos']・"</td>
<td>$strhide</td>
<td><a href^diteat form, php? id_catalog=r,. $cat [' id_catalog' ] . ” & >Исправить</a></td>
<td><a href=delcat.php?
id_catalog=n.$cat[1id_catalog'].n& >Удaлить</a></tci></tr>м;
}
echo ,'</table>*1;
// Если каталог выбран, то выводам контактную информацию // из выбранного каталога, подключая файл contacts *php if ($id__catalog!^0) include Mcontacts.phpn;
Приведенный в листинге 10.2 код выводит таблицу групп контактов, каждая из строк которой соответствует отдельной группе. Помимо названия и позиции напротив каждой группы выводятся управляющие ссылки, позво- ляюшие скрыть/отобразить, исправить или удалить группу.
Группы контактов выводятся только в том случае, если текущей является корневая группа ($id_catalog = о), так как в Web-приложении блок "Контакты” используется одноуровневая модель каталога.
Если текущей является одна из подгрупп корневой группы ($catalog !=» о), то посредством включения скрипта contacts.php выводится не список групп, а контактные позиции в данной группе (листинг 10.3).
<?php
// Выводам ссылку на добавление контактной информации в группу echo "<а href=addcont form* php?
1с1_саЁа1од=$1с1_саЪа1од>Добавить контактую информацжк/ a>11;
// Выбираем из базы данных контакты для выбранной группы $query = "SELECT * FROM contacts
WHERE id__catalog=n . $ id_catalog • ”
ORDER BY pos";
$prt = mysql_query($query);
if(!$prt) puterror("Ошибка при обращении к блоку Контакты<Ьг> $queryM);
/ / Если в текущей группе контактов имеется хотя 函 один
// контактный блок/ формируем таблицу с контактной информацией
if (mysql_num__rows (帛prt) > 0)
{
// Выводам заголовок таблицу
echo "<table width=100% border=l>M;
echo ”<tr>
vtd>WMH и должность</td>
<td>Anpec</td>
<td>Teлeфoн</td>
<td>e"mail</td>
<td>M3o6p-e</td>
<td>no3*</td>
<td^ew ствия</ td>
</tr>n;
while($par = mysql_fetch_array($prt))
{
// Выясняем, скрыта товарная позиция или нет
if{$par[^hide*] = "hide") $showhide = "<а href=showcont.php? id_contact=".$par[1id_contact1"& id_catalog=*' .$id_catalog.">Отобразить</а>м;
else $showhide = ”<a href=hidecont.php?
id_contact=r, < $par【'id_contact * ] * "& id cata工og=” . $id__catalog. ">Скрыть</а>п;
// Выясняем, имеется изображение или нет
if (! empty ($par [тpicture1 ]) && $par [1 picture1 ] । =n~**)
$urlpict = "<a href=.. / . ./"*$par[tpicture1 J . м>Изображение</а>п;
〃 Выводим контактные позиции
echo H<tr>
<td>u.$par[* name1],"</td>
<td>M.$par['address1].w</td>
<td>”.$par[*phone11> n</td>
<td>M. $par「email1] J4/td>
<td>$urlpict</td>
<td>n.$par[*pos " ."v/td>
<td>$showhide<br>
<a href^editcontform.php?
. id_contact=M.$par['idcontact1].
id_catalog^°.$id catalog.н>Исправить</а><Ьг>
<a href=delcont.php?
id_contact=" . $par [1 id_contact * 1 > *'&
id_catalog=". $id_catalog. n>yAannTb</a></td></tr>**;
} echo "</table>";
}
?>
По структуре листинг 10.2 похож на листинг 10.3: после вывода ссылки на добавление новой контактной позиции происходит обращение к базе данных с целью извлечения записей таблицы contacts, соответствующих контактным позициям текущего каталога. Далее при помощи функции mysql_num_rows {)определяется количество строк в ответе базы данных. Если функция возвращает число, большее нуля, происходит формирование таблицы с контактными позициями, поля которой соответствуют полям таблицы contacts. Кроме того, напротив каждой из контактных позиций имеются управляющие ссылки, позволяющие скрыть/отобразить, исправить и удалить позицию.
Добавление контактной позиции осуществляется при обращении к HTML- форме, расположенной в файле addcotilform.php, содержимое которого представлено в листинге 104
Листинг 10.4. Файл addcontform.php
<?php
// Устанавливаем соединение с базой данных
гequiгe_once ("config.php");
// Настраиваем управляющие переменные по умолчанию if(!isset(^button)) $button = "Добавить";
if(!isset(Saction)) $action = "addcont・php";
if(!isset($showhide)) 5showhide 二 "checked”;
// Получаем параметры из строки запросов
$id__catalog ^ $_GET [1 id^catalog1 ];
$id_contact = $_GET [' id_contact * ];
// Если позиция контактного блока не передана форме, // определяем ее как максимальное значение поля роз таблицы contacts / / плюс 5 единиц if (! isset ($pos)) (
$query = "SELECT MAX(pos) AS Maxpos FROM contacts WHEPE id_catalog=$id_catalogn;
$inaxpos = mysql_query ($query); if(Smaxpos) (
if (mys ql_nuro_r о ws ($maxpos) >0) {
$maxp = mysql_fetch_array ($maxpos);
$pos = $maxp[1Maxpos* J +5;
} else $pos = 1;
)else $p0s = 1;
} ?> <a href=Hindex.php?id_catalog=<? echo $id_catalog; ?>'*> Вернуться в администрирование контактов</а> <form enctype^1 multipart/form-data1 action=<?php echo $action; ?> method=post>
Имя и должность * : <input type^text name^name value=1<?= $name; ?>*><br> Адрес : <input type=text name=address value-^?^ $address; ?>‘><br> Телефон : <input type«text naIпe^phone valuerf<?php echo $phone; ?>*><br> e-mail : <input type^text name-email valuerT<?php echo $amail; ?>*>vbr> Изображение : <input type=file name=picture><br> Позиция * : <input type=text name=pos value=t<?php echo $pcs; ?>'><br> Отображать : <input type=checkbox name^hide <?= $showhide; ?»<br> < input type=submit value=<?php echo $button; ?»
<input type=hidden name=id_catalog value=<?php echo $id__catalog; ?» <input type=hidden name=id_contact value=<?php echo $id_contact; ?» </form>
Приведенная в листинге HTML-форма имеет 4 текстовых поля для имени (пате), адреса (address), телефона (phone), e-mail (email) и позиции контактного блока (pos). Кроме того, имеется одно поле типа file, позволяющее загрузить изображение, напрнмер, фотографию (picture), флажок Ото- бразить/Скрыть (hide), позволяющий добавлять контактный блок скрытым
или доступным для просмотра посетителями сайта,' и кнопка для отправки введенной в форме информации обработчику, которым по умолчанию вы* ступает addcont.php (листинг 10.5). Форма спроектирована для повторного использования при редактировании контактной позиции, позволяет изменять обработчик формы, передавая форме значение переменной $action, отличное от назначаемого по умолчанию. Точно так же можно изменить название кнопки (например, со значения Добавить на Исправить), поменяв значение переменной $button. В режиме редактирования форме необходимо передать значения полей таблицы contacts, пометенные в одноименные с элементами управления формы РНР-переменные.
Переменная $showhide, несет ответственность за состояния флажка Отображать и по умолчанию принимает значение "checked" (флажок установлен). Если форме не передано значение позиции контактного блока $pos, как это имеет место при добавлении новой контактной позиции, ей присваивается максимальное значение поля роз для контактных позиций текущей группы, увеличенное на единицу. Если в группе пока нет ни одной позиции, переменной $роз присваивается значение, равное единице.
Значения переменных ?id_catalog и $id__contact необходимы для корректного возврата на главную страницу администрирования и при редактировании контактной мнформаинм передаются в одноименных скрытых полях.
■ 'ул^мм 茎 5g 賞Y**,";,,:!?”*,,"" • ^t*l*l**!*tw***l***, "!'-!***и!”一+沿甘岁■丫梨?■起!к”'*}•?叶江 at*,*.**怨!*"***"里装j密:賢嶋TL[^Hf}*,九,"["f£?4壮騎*曲"力学,1?"凱"瞿*,庐芋|'重¥,〕'1',!,?? :Листинг 10.5. Файл addcont.php:
<?php
// Устанавливаем соединение с базой данных
require_once (мconfig.phpM);
// Проверяем корректность введенной информации
i f (empty ($_POST [Fame1 ]) ) 1 inks ($_POST [ * id_catalog'],
иОтсутствует имя и должность11);
if (empty ($_POST [ ^s' ])) links ($_POST [ FJcatalog,],
"He введена поэшхия продукта*1);
// Проверяем, скрыт контактный блок или нет
i£($_POST['hide'] = "on") $showhide = "show"; else $showhide = "hide";
^picture = ””;
// Если в суперглобальном массиве $_FILES имеется элемент,
// соответствующий полю picture, копируем изображение из временного // каталога в каталог /files
if (! empty ($__FILES【'picture ЧЕ1 tmp_name “)& &
$_FILES [1 picture1 П1 tmp__name1 ] != ”-")
〃 Переименовываем загруженный файл $path=Mfiles/11 .date (^YmdHis1*, time ()).
strrchr ($_FILES [ picture 4 (Tname* ],"・'');
if (copy ($__FILES [ * picture * ] [1 tmp_name1 ], "••/"・ $path)) $picture = $path;
}
/ / Заменяем оданарные кавычки обратными
$_POST['name']= str_replace(n1u, " ", $_POST[1name *]);
$_POSTfaddress'J = str_replace(”'% "'”, $_POST[1address*]); $_POST[*phone1]= str_replace(T,ll\ ” ”,$_POST[1 phoneT ]);
$_POST remail1]= str_replace (H1 "f "…,$_POST [1 email1 ]);
// Формируем и выполняем SQL-запрос на добавление контактного блока $query = "INSERT INTO contacts VALUES (NULL,
-'・$_POST['name‘]・"',
'L $_POST【'address' ] JI ,,1.$_POST[tphone,] <"S T".$_POST[,email'] .n\ 「$_POST['pos」・”, ,$pict:ui:el '$showhide *,
". $_POST [' id__catalog1J ・")"; if (mysql_query($query)) {
/ / Осуществляем автоматический переход
U на главную страницу администрирования echo n<HTML><HEAD>
<МЕТА HTTP-EQUIV^1Refresh' CONTENT^'O; URL=index.php? id_catalog=M. $_POST [ • id_catalog1 ]「> </HEAD>M;
} echo links ($_POST [ tid_catalog,], rlОшибка при добавлении контактной позиции");
// Вспомогательная функция для вывода ошибок function links($id_catalog,$msg)
echo $msg* *'</br>H;
echo "<a href=# onClick=,history.back() *>
Вернуться к правке продукта</а></Ьг>";
echo "<а href^index.php?id_catalog-$id_catalog> Адидинистрирование каталога продукции< /а>; exit();
} ?>
В обработчике addconi.php HTML-формы addcontfbrm.plip для добавления контактной позиции (см. листинг 10.5) после установки соединения с базой данных посредством включения скрипта, расположенного в файле config.php, происходит проверка обязательных для заполнения полей name м pos.
:Зам—нанме )
Все остальные поля являются необязательными и не выводятся на сайте, если редактор блока ^Контакты" их не заполнил.
! Замечание )
Следующим этапом подготовки SQL-запроса является замена всех одинарных кавычек обратными. Это необходимо для того, чтобы избежать конфликта при запросе к базе данных, так как текстовая информация согласно синтаксису SQL должна обрамляться одинарными кавычками.
Замечание )
Если вывод обратных кавычек вместо одинарных недопустим, например, на форуме, посвященном программированию, где каждая кавычка имеет значение, перед выводом необходимо провести обратное преобразование или при добавлении информации экранировать кавычки, заменяя символ『на \ \
В завершение скрнпта в листинге Ю.5 во временной переменной $ query формируется SQL-оператор insert для добавления новой контактной позиции. Перемен НсШ $ query передается в качестве аргумента функции mysqi query(), и если данная функция выполняется успешно, происходит автоматический переход на главную страницу адми н истрирова н и я.
Как уже упоминалось, для редактирования уже существующей контактной позиции используется ранее разработанная форма addcontform.php. Для этого в отдельном файле editconttbrni.php (листинг 10.6), производится на~ стройка управляющих переменных формы ——переменной $button, ответственной за вывод названия кнопки, присваивается значение Исправить, в качестве обработчика формы (переменная $action) назначается скрипт, расположенный в файле edilcont.php.
Замечание )
Для успешной работы скрипту editcontform.php передаются два параметра id_contact и id_catalog для первичного ключа контактного блока и текущего каталога соответственно.
:Листинг 10.6. Файл editcontform.php
?i^rtZ*^<.4A««»»*«<;.;A;....J.*h.j...;^.4*^„'r;»;..^..;...».........^...B;A^..i.^V../lw.^.;.«l»“.;*. ar “..“+"+** ;*.““.* ..:■»...k.
<?php
// Устанавливаем соединение с базой данных require_once (нconfig.phpи);
// Настраиваем управляющие переменные HTML-^opwbt addcont form.php // для исправления контактного блока $button = пИсправитьм;
$action = ''editcont * php";
Sid^contact = $_GET [,id__contact1 ];
// Формируем и выполняем SQL-запрос
// на извлечение редактируемой информации
$query = нSELECT * FROM contacts WHERE id_contac^$id_contact";
$cont = mysql_query($query);
// Если запрос выполнен, то извлекаем информацию
// и подключаем HTML-форму if ($cont) {
$contacts = mysql_fetch_array (Scont);
$name = $contacts[*name1];
^address = $contacts[1address *];
$phone = $contacts[* phone *];
$pQS к $contacts[1posr];
$email 工 $contacts[*email *];
if ($con tacts [ ^ide1 ] == "show") $ showhide = M checkedT,;
else $showhide = ”";
// Подключаем HTML-форму include иaddcontform.php11;
После настройки управляющих переменных $button и faction из таблицы contacts извлекается запись, соответствующая редактируемому контактному блоку. Информация, извлеченная из полей записи, присваивается соответствующим переменным, которые подставляются в качестве value-значения в HTML-форме. Завершает скрипт включение HTML-формы addcontforni.php, в качестве обработчика которой теперь выступает скрипт editcont.php (листинг 10.7).
/整密/冲支”尊w,曙暇書理哼疗/整密/冲支”尊w,曙暇書理哼疗
/整密/冲支”尊w,曙暇書理哼疗
/整密/冲支”尊w,曙暇書理哼疗
v?php
// Устанавливаем соединение с Оаэой данных require_once (мconfig.php11);
// Проверяем корректность введенной информации
if (empty ($_POST [Fame,]))
links($_POST[* id^catalog1]f"Отсутствует имя и должность11); if (empty ($_POST [ fpos * ]))
links($_POST[1id—catalog*],"He введена позиция продукта"); 〃 Проверяем, скрыт контактный блок или нет if($ POST[»hide'] = "on") $showhide = "shown;
else $showhide = *'hide11;
// Если редактор блока 0Контакты" в поле дая изображения ввел
// символ "-" или другое изображение, следует уничтожить предыдущее
if (I empty ($_FILES [1 picture * ] [ * tmp_name1 ]) || $_FILES「pictureЧЕ1 tmp__name1 ]=="-")
(
$query = "SELECT picture, id__contact FROM contacts WHERE id_contact=H,$_POST[,id^contact」;
$pct = mysql_query($query);
if(!$pct) links($_POST[1id_catalog1],
"Ошибка обращения к базе данных*1);
if (mysql_num_rows ($pct) >0) (
$ result = mysql_result ($pct, 0);
unlinkC*. ./../$resultи);
}
}
$picture = ^picture^1-1,";
if {! empty ($_FILES [ picture * ] [1 Ъгф_пате1 ]) && $_FILES [f picture」['name'] !="-") {
// Закачиваем файл
$path= "files/** .date ("YmdHis11^ time {)).
strrchr ($_FILES [picture* ] [ *nameT ] r ".");
if (copy ($_FILES [1 picture,] [1 tmp_name1 ],"・•/". $path)) $picture ^ llpicture= * $pat±i1, H ;
}
〃 Заменяем одинарные кавычки
$_POST【Fame * ] = str__replace (nint ,1%nt $_POST ['name»]);
$_POST [' address' ] = str_replace(","「、”,$_POST [ * address」);
$_POST J phone' ] = str_replace (" ■匕…,$_POST (• phone •]); $_POST[lemail,] = str_replace (”…,”、% $_POST「email * ]); // Формируем и выполняем SQL-оператор UPDATE
$query = "UPDATE contacts SET name='".$_POST['name']J', address=l **. $_POST t * address ']•”', phone= • °. $_POST [• phone '】・”', email='"・$ POST[1 email1 ] .11 *
роз=". S—POST fpos『]J, ^picture
hide=1$showhide,
WHERE id_contact^n * S_POST[1id_contact'];
// Если запрос выполнен успешно, то осуществляем
// автоматический переход на главную страницу администрирования if(mysql_query($query))
{
echo tf<HTML><HEAD><META HTTP*EQUIV=1 Refresh1
CONTENT^0; URL=index.php?
id_catalog=n < $_POST J id_catalog」J ' > </HEAD></HTML>";
}
// Вспомогательная функция для вывода ошибок function links($id_catalog,$msg) (
// ... текст функции приведен в листинге 10*7 }
?>
Код, приведенный в листинге 10.7, по структуре похож на код для добавления контактной позиции, приведенный в листинге 10.5. Здесь осуществляются те же проверки и преобразования. Отличие наблюдается при работе с изображениями, при редактировании могут сложиться следующие три ситуации:
口 изображение не подвергается редактированию;
口 изображение удаляется;
口 изображение заменяется новым.
В первом случае поле picture HTML-формы остается пустым м временная переменная ^picture, ответственная за поле изображения в SQL-операторе update, также остается пустой, так как программа не заходит ни в один из двух if-блоков по обработке изображений.
Во втором случае, если в поле picture помещен символ прочерка ”-“, производится удаление изображения из директории files, где хранятся все изображения данного Web-приложения. Для того чтобы выяснить, имеет ли исправляемый контактный блок изображение и, если имеет, каково его название, к табл ине contacts осуществляется SQL-запрос. Если число воз-
врашенных функцией mysql_query() строк больше нуля, при помощи функции unlink о файл удаляется, а временная переменная $picture получает значение "picture-* -', '\ что при выполнении SQL-оператора update приводит к замене пути к текущему файлу на символ прочерка.
Когда происходит замена старого изображения новым, необходимо удалить старое изображение мз директории files и скопировать туда новое изображение из временной директории сервера. Здесь программа захопнт как в первый if-блок, уничтожающий старый файл, так и во второй, где новый файл копируется в директорию files, а временная переменная ^picture получает значение °picture=l$path, гн, где $path — путь к новому файлу.
В конце листинга 10.7 формируется и выполняется SQL-оператор update, после успешного выполнения которого происходит автоматический переход на главную страницу администрирования.
Удаление контактного блока осуществляется при помощи скрипта, расположенного в файле delconi.php (листинг 10.8), который принимает три параметра —— id^contact, ld_parent И id_catalog —— ДЛЯ ПСрВМЧНОГО ключа контактного блока, корневой группы контактов и текущего каталога соответственно.
Листинг 10.8. Файл delcont.php
<?php
// Устанавливаем соединение с базой данных require_once ("config*php,f);
// Получаем параметры из строки запроса
$id__contact = $_GET [' id_contact * ];
// Если у контактного блока есть изображение, уничтожаем его
$query ^ "SELECT picture, id_contact FROM contacts
WHERE id_contact=^$id contact";
$pct = mysql_query ($query);
if($pct)
{
if (mysql_nwn_rows ($pct) >0)
{
^result = mysql_result($pct# 0);
unlink(n../../^result");
}
/ / Формируем и выполняем SQL-запрос на удаление // контактного блока
$ query = r, DELETE FROM contacts WHERE id_contact=$id_contact,';
if(mysql_query($query))
(
echo "<HTML〉<HEAD>
<META HTTP-EQUIV^1Refresh1
CONTENT= * 0; URL=index.php?id_cata1og=$id_catalog * > </HEAD>M;
}
else puterror(нОшибка при удалении контактной информации");
В листинге 10,8 перед удалением записи, соответствующей удаляемому контактному блоку в таблице contacts, проверяется наличие пути к изображению. Если в поле picture имеется путь к файлу, последний уничтожается при помощи функции unlink(). После этого формируется м выполняется SQL-оператор delete, удаляющий запись из таблицы contacts с первичным ключом $id_contact.
Файлы hidecont.php и showcont.php, осуществляющие сокрытие и отображение информации контактной позиции, аналогичны рассмотренным ранее файлам сокрытия и отображения других Web-приложений (см. листинг 7.30),
На этом разработка системы администрирования блока "Контакты" завершена. Далее будет рассмотрена страница представления, которая используется для отображения контактной информации на сайте.
Страница представления состоит из двух файлов:
□ index.php ——файл, ответственный за вывод групп контактов (лис^ тинг 10.9);
口 contacts.php —— файл, ответственный за вывод контактных блоков текущей группы контактов (см. листинг 10.10).
<html>
<title>KoHTaKTbi</title>
<head></head><body>
<МЖонтактная информания</Ы >
<?php
// Устанавливаем соединение с базой данных require_once ("admin/config.phpn);
// Извлекаем параметр id__catalog из строки запроса, // в случае его отсутствия переходим к корневой группе if(!isset($_GET[•id_catalog *])) $id_catalog = 0;
else $id_catalog ^ $_GET[1id_catalog1];
// Извлекаем список групп контактов из таблицы catalog
$query ^ "SELECT * FROM catalog
WHERE hide=,show'
ORDER BY pos";
$ctg = mysql_query ($query);
if (!$ctg) puterror (п0шибка при обращении к группам контактовн);
// Выводим группы контактов
while($cat = mysql_fetch_array($ctg))
/ / Если первичный ключ группы, извлекаемой из таблицы // catalog, равен первичному ключу текущего каталога, // выводим контактные блоки
if($id_catalog q $cat「id_catalogT]) {
echo rt<lixh2>". $cat [ ^ame1 ]. H</h2>M;
include "contacts.php";
echo "<br>";
}
else
{
// Если группа не является текущей, выводим ссылку, переход
// по которой приводит к выводу контактных позиций данной группы echo ,,<lixh3><a href=index. php?id_catalog=".
$cat [ * id_catalog' ]・”>"・$cat [ 'name,]. ”</a>"h3>”;
?>
</bcdy>
</html>
Скрипт index.php, приведенный в листинге 10.9, может принимать параметр id_cataiQg для задания текущей группы. Значение этого параметра передается переменной $id_cataiog. Если значение данного параметра не установлено, переменная $id_cataiog принимает нулевое значение, соответствующее корневой группе.
После этого из таблицы catalog извлекаются все группы контактов, отсортированные по полю роз, которые выводятся в виде списка гиперссылок. Вывод списка осуществляется в цикле while. Если первичный ключ записи равен значению переменной $id_catalog, происходит вывод контактной информации за счет включения в тело скрипта файла contacts.php (листинг 10.10).
了:"'汇炉:丁产亭.……二…"丁y胃:?奔м;::产繆:":'":"'?'注'm* "^^4*;・'6?"‘:=?m'却"比'.y/:':?:'""':"j'?,…工?'¥线^^^域 :Листинг 10.10. Файл contacts.php
«*•*■«• ..、;扌.♦;;;«.V1«...;..;,“• “:♦;*.■ ,.,,一4;. .»、.♦;;. .;;..<;S; ■,aV ♦ ♦・ Г,. ■ ♦;.«♦.♦.『,,,♦ ・;.『》..♦.'.......♦..."..;.,.♦.♦...,./♦ ...'♦•;,. X .... .1 .:;...♦;;“,.; С. } : ■,.>,§>>«• ;、、?♦】<.}♦ ; .丁.・W>t:;¥
<?php
// Выбираем блоки контактов из базы данных
$query = "SELECT * FROM contacts
WHERE id catalog=rl. $c&t [1 id_catalogT ] ■ *' AND hide= fshow' ORDER BY posn;
$prt ^ mysql_query < $query);
if($prt)
{
// количество рядов в выборке из базы долящо быть больше нуля
i f (mysql__nuni_rows ($prt) >0)
while($par 二 mysql_fetch_array($prt))
(
if (SparJhideT] !=1)
{
// Под каждое контактное лицо оформляем отдельную таблицу if (! errpty ($par [1 name11)) echo H<h4>11. $par [ 'name1 ] . n</h4>tr; echo "〈table〉 ”;
if (! eir^ty ($par (* addressr П ) echo r,<tr><td>A^ec t
n.$par faddress r] . "</td></tr>rt;
if(!empty{帛par['phone'】))echo K<tr><td>Te^:
”・ $par E1 phone1] . ,t</td></tr>";
if(!empty($par[1picture']) && $par['picture1]!^и-")
echo M<trxtd><a href="・ $par [' picture']・”>
Изображение</а></1с1></tr>**;
echo "</table〉”;
} else echo "Извините, в этой группе нет контактной информации. Обратитесь к администратору сайта.м;
?>
Листинг 10.10 начинается с формирования SQL-запроса на извлечение всех контактных позиций текущей группы. Если в текущей группе контактные позиции отсутствуют — выводится соответствующее предупреждение. Контактные блоки выводятся в цикле while; при этом, если поле с соответствующей контактной информации пусто, то соответствующая позиция не выводится.
Глава 11
Блоки Толосование" и Тостевая книга"
Для того чтобы иметь обратную связь с клиентами, на корпоративных сайтах применяют, среди прочих, два вида Web-приложений. Первое из них является блоком "Голосование'^ в котором посетителям предлагается вопрос н несколько вариантов ответа. Такого рода Web-приложения позволяют привлечь внимание к ресурсу, так как посетителям обычно самим интересно посмотреть на результаты опроса. Кроме этого, если ресурс достаточно посещаем, при помощи таких опросов можно собирать нужную социологическую информацию*
Другим Web-приложением, позволяющим наладить обратную связь с посетителями сайта и клиентами компании, является гостевая книга. В гостевой книге посетители сами могут задать вопрос администрации сайта или лицам, представляющим компанию, и получить ответ на интересующие вопросы. В отличие от форума (рассмотренного в главах 19—21) гостевая книга не подразумевает длительных обсуждений, лишь замечание или вопрос посетителя и необязательный ответ администратора.
( Замечание )
Аналогом гостевой книги является блок "Вопрос-ответ" или "FAQ".
Система голосования
Разрабатываемая в этой главе система голосования позволит оживить сайт, сделать его более динамичным. С помощью системы голосования можно узнавать мнение посетителей по тому или иному вопросу, организовать обратную связь с посетителями. Также следует учесть возможность ннакрутки'* и фальсификации результатов и защитить систему голосования от этого. Фальсификация возможна, если посетители множественным голосованием целенаправленно пытаются исказить результаты.
Система голосования обычно представляет собой небольшую форму, размещаемую на всех страницах сайта, которая содержит вопрос и варианты ответов на него. С помощью этой формы любой посетитель может ответить на задаваемый вопрос, выбрав один из предлагаемых вариантов ответа и нажав кнопку Голосовать. На рис. 11.1 приведен пример размещения блока голосования.
Рис. 11.1. Пример размещения блока голосования на сайте (слева)
Система голосования позволяет вести архив старых опросов. При добавлении нового опроса старый не удаляется, а перемещается в архив. Причем через систему администрирования любой опрос можно восстановить из архива н снова поместить на сайт.
Web-приложение состоит из двух частей: системы представления и системы администрирования. С помощью системы представления на сайте отображается текущее голосование, его результаты, а также предоставляется доступ к просмотру старых опросов из архива.
Рассмотрим систему представления более подробно. Пример вывода блока голосования на станицах сайта показан на рис. ILL После того как посети-
тель выбрал вариант ответа с помощью переключателя и нажал кнопку Голосовать, его ответ обрабатывается в системе голосования, н вслед за этим автоматически открывается окно с результатами опроса (рис. 11.2).
Рис. 11.2. Окно с результатами опроса
В окне с результатами опроса отображаются результаты текущего голосования. Посмотреть результаты голосования, находящиеся в архиве, можно по нажатию на ссылку Посмотреть архив голосований. Откроется страница, похожая на страницу с результатами опроса, изображенную на рис. 11.2, где будут представлены результаты всех голосований, находящихся в архиве.
Система администрирования
С помощью системы адм и н истри ровапия проводится управление системой голосования. Через интерфейс системы администрирования можно создавать новые голосования, добавлять новые варианты ответов в существующие, направлять голосования в архив it извлекать их из архива.
Главная страница системы администрирования представляет собой меню, с помощью которого можно просмотреть результаты текущего голосования, добавить новое голосование и перейти к просмотру архива.
Рис. 11.3. Главная страница системы администрирования
Нажатие на ссылку Текущий опрос откроет страницу с текущим опросом (рис. 11.4).
На этой странице отображаются управляющие ссылки и таблица с результатами голосования. Рассмотрим меню, расположенное над таблицей с результатами голосования. Нажатие на ссылку Вернуться к управлению голосованиями откроет главную страницу системы администрирования (см. рнс. 11.3). С помощью ссылки Добавить ответ можно добавить новый вариант ответа на вопрос в текущее голосование. По нажатию на эту ссылку откроется страница добавления нового варианта ответа (рис. I L5).
Для добавления нового варианта ответа необходимо вписать вариант ответа в соответствующее поле, изменить, если необходимо, номер позиции и нажать кнопку Добавить. Номер позиции позволяет определять порядок вывода вариантов ответа. Вывод осуществляется по возрастанию номера позиции.
Следующая ссылка в меню на странице с результатами текущего голосования (см* рис. 11.4) — Скрыть опрос. После нажатия на эту ссылку голосование перестанет отображаться на страницах сайта. Получить доступ к нему можно будет только через систему администрирования. Снова включить отображение голосования на сайте можно нажатием на ссылку Отобразить опрос, которая будет выводиться вместо ссылки Скрыть опрос.
Рис. 11.4. Страница с результатами текущего опроса
Рис. 11.5. Страница добавление нового варианта ответа на вопрос в голосовании
Переход по ссылке Направить опрос в архив (см* рис. 11.4) переместит текущий опрос в архив системы голосования. После перемещения текущего голосования в архив блок голосования перестанет отображаться на страницах сайта.
Таблица с результатами текущего опроса представляет собой список вариантов ответов и статистику по ним. В последних двух столбцах таблицы размещаются управляющие ссылки: Удалить и Исправить. Переход по ссылке Удалить приводит к удалению варианта ответа из системы голосования. При этом результаты голосования посетителей с участием этого варианта ответа будут потеряны. Переход по ссылке Исправить приводит к открытию страницы, похожей на страницу добавления нового ответа (см. рис. 11.5), с по* мощью которой можно откорректировать текст варианта ответа в соответствующем поле или изменить порядок вывода вариантов (поле Позиция).
Для того чтобы добавить новый опрос, необходимо вернуться на главную страницу системы администрирования (см. рис. 1.3) и перейти по ссылке Добавить голосование (старое отправляется в архив). Как видно из названия ссылки, при добавлении нового опроса текущий опрос автоматически помещается в архив. Страница добавления нового опроса представляет собой форму для ввода вопроса, на который будут отвечать посетители (рис. 11.6).
Рис. 11.6. Добавление нового голосования
После заполнения поля Вопрос и нажатия кнопки Добавить новое голосование будет добавлено в систему администрирования и назначено текущим. После создания нового голосования будет автоматически открыта страница с результатами текущего опроса (см. рис. 11.4), работа с которой была рас-
смотрена выше. Завершающим этапом при создании нового голосования является добавление вариантов ответа на вопрос, заданный в голосовании.
Перейти к просмотру архива опросов можно с ломошью ссылки Перейти к архиву голосований, распложенной на главной странице системы адмнннст- рирования (см. рис. 11,3). При переходе по этой ссылке откроется страница со всеми опросами, ранее проводимыми на сайте (рис. 1 L7).
Рис. 11.7. Архив системы голосования
Формат вывода опросов в архиве повторяет формат отображения текущего голосования на сайте. Отличие заключается в том, что добавляются две управляющие ссылки над таблицей с результатами опросов: Скрыть и Сделать активным. После нажатие на ссылку Скрыть данный опрос перестанет быть виден посетителям сайта при просмотре архива. Включить режим отображения можно нажатием на ссылку Отобразить, на которую будет заменена ссылка Скрыть после скрытия опроса.
Щелчок по ссылке Сделать активным переведет опрос из архива в режим активного (текущего) голосования- При этом существующее активное голосование будет перемещено в архив.
Разработка системы голосования
Разрабатываемая в этой главе система голосования функционирует с помощью СУБД MySQL. База данных для системы голосования состоит из трех таблиц: poll, answer, poii_ip. В таблице poll хранятся вопросы голосований, таблица answer содержит варианты ответов на вопросы голосований. Таблица poii_ip необходима для защиты голосования от "накруток" (повторных голосований в небольшой период времени)——она содержит IP- адреса последних проголосовавших посетителей.
Таблица poll содержит следующие поля:
□ idpcll — первичный ключ таблицы, снабженный атрибутом AUTO^INCREMENT;
name — содержит вопрос голосования;
口 archive — содержит идентификатор, отвечающий за местонахоадение голосования в архиве;
hide — содержит идентификатор, отвечающий за отображение опроса для посетителей сайта;
口 putdate ——время добавления опроса в систему голосования
SQL-оператор create, создающий таблицу poll, приведен в листинге 11.1.
м广;混幽; g’f :"号矮*:*海修^^=…穿‘吟二工:i『"蹴、?]7?*" ‘ :二ж?‘ ■-•■ 土,""?戈m二.*¥T買¥學:" •• "…" D+时任型辞縫 ;Листинг 11.1. Создание таблицы poll
纟x二:偉:;含疔9Л»к::;.;m1■品芸务工i*: r .K吊出:三* 疗^?^ 器:e*•*,, ■.wi-^ii.-itlS.iSi'.V.:注 E;盘 £, iSit^;,,. ;.*;「rз;‘Г.4;;.济;;.;Л.门改介::■%、£;;讓Н':余卡*3聚沾者.Л,.;
CREATE TABLE poll (
idqoll INT(8) NOT NULL AUTO_工NCREMENT,
name TEXT,
archive ENUM「archive — active') NOT NULL DEFAULT 'archive I
hide ENUM「shew —hide') NOT NULL DEFAULT『sho*
putdate DATETIME DEFAULT NULL,
PRIMARY KEY (id__poll)
)TYPE=My 工 SM;
Таблица answer состоит из следующих полей:
id_answer — первичный ключ таблицы, снабженный атрибутом AUTO_ 工 NCREMENT;
name ——содержит вариант ответа на вопрос;
15 Зак, 1181
口 pos — определяет порядок вывода ответов;
hit — количество проголосовавших за данный вариант ответа;
id_poii — идентификатор голосования, к которому относится данный вариант ответа.
SQL-оператор create, создающий таблицу answer, приведен в листинге 11.2.
崇"*沙图翠磔諜涔::曜T 鎖找更豈/蹴V%献 Hj嗨#畢骨/У。嘗渺吵叮叫丁"炉'"?":…:f *-,-Y'1洋*對M" " " "R:吋鴉"工"*,产? •,•:
;Листинг 11.2. Создание таблицы answer
CREATE TABLE answer (
id_answer INT(IO) NOT NULL AUTO_INCREMENTr
name TEXT,
pos 工NT(4) DEFAULT NULL,
hit INT(IO) DEFAULT NULL,
id__poll INT(8) DEFAULT NULL,
PRIMARY KEY (id_answer)
)TYPE=MyISAM;
Таблица poll_ip содержит следующие поля:
id_poll_ip — первичный ключ таблицы, снабженный атрибутом AUTOJNCREMENT;
ip — IP-адрес проголосовавшего посетителя;
puttime —— время ответа посетителя,
SQL-оператор create, создающий таблицу poil_ip, приведен в листинге 11.3.
t J.» ^ Л i tvrita ttiritfttitit'li &*.:iVr—;尸“芻;Л til Л >■<.t J.» ^ Л i tvrita ttiritfttitit'li &*.:iVr—;尸“芻;Л til Л >■<.idjsoll^ip INT(IO) NOT NULL AUTO_INCREMENT, ip TEXT, puttime DATETIME DEFAULT NULL, PRIMARY KEY (id_j>oll_ip))TYPE=MyISAM;idjsoll^ip INT(IO) NOT NULL AUTO_INCREMENT, ip TEXT, puttime DATETIME DEFAULT NULL, PRIMARY KEY (id_j>oll_ip))TYPE=MyISAM;
t J.» ^ Л i tvrita ttiritfttitit'li &*.:iVr—;尸“芻;Л til Л >■<.
t J.» ^ Л i tvrita ttiritfttitit'li &*.:iVr—;尸“芻;Л til Л >■<.
idjsoll^ip INT(IO) NOT NULL AUTO_INCREMENT, ip TEXT, puttime DATETIME DEFAULT NULL, PRIMARY KEY (id_j>oll_ip)
)TYPE=MyISAM;
idjsoll^ip INT(IO) NOT NULL AUTO_INCREMENT, ip TEXT, puttime DATETIME DEFAULT NULL, PRIMARY KEY (id_j>oll_ip)
)TYPE=MyISAM;
После того как база данных создана, можно приступить к разработке системы голосования. Традиционно начнем ее с разработки системы администрирования. Система адм и н истрирования включает в себя 14 файлов:
index.php — главная страница системы адм и н и стри ро ван и я;
config.php — скрипт подключения к базе данных;
口 currentpolLphp ——отображение текущего голосования;
П archivepoll.php — архив системы голосования;
О addpollfbrm.php ——форма добавления нового голосования;
П addpoll.php — скрипт-обработчик формы добавления нового голосования;
О addanswfbrm.php — форма добавления варианта ответа на вопрос голосования;
口 addansw.php ——с кри пт-обработчи к формы добавления варианта ответа;
editanswform.php — форма для редактирования ответа на вопрос;
editansw.php — скрипт-обработчик формы редактирования ответа на вопрос;
口 setactive>php — скрипт, устанавливающий обрабатываемое голосование активным и переводящий текущее голосование в архив;
putarchive.php — скрипт, переводящий текущее голосование в архив;
hide.php — сокрытие голосования;
口 show.php — отображение голосования;
delansw.php — удаление варианта ответа на вопрос голосования.
Главная страница системы администрированИ51 index.php (см* рис. 1.3) представляет собой обычную HTML-страницу и содержит только управляющие ссылки: Текущий опрос, Добавить голосование (старое отправляется в архив). Перейти к архиву голосований (листинг 11.4).
♦:"№、絹詁臺 ^^:單舅/,,:"二,XWWw岁,•芯fI"::"'•••■•? •%♦,.”・$:.”•♦::”"•••♦•■;,•■",••/»•••••»'***",*>(>*■'' *:
инг 11Л Файл Index.phpi
РтТ/ЙУЛ'Х?. .VjHTr< .3 .U.方./Г<><. *■■?;; Fr. “*.*****“. i * i *“**;,♦. “ “ * “ * * * 穽,П »■■>..-■ Wi,*^si\* + , ♦ ,;* * “;;i;:・'* ...>.;立爲,“ **.♦.♦*.* "dt;* . ” " i'T'i'Ik'ifYnrn • “ "::
<table cellspacing^*115 ">
<tr>
<td><a href=Mcurrentpoll,рЬрп>Текущий опрос</a></td>
<td><a href^Maddpo 1 Iform. php1'>Добавить голосование
(старое отправляется в архив)</a></td>
<tdxa href="archivepoll.рЬрп>Перейти к архиву голосований</а></td>
</tr>
</table>
1 Замечание )
Далее в листингах для удобства восприятия будет опущен несущественный HTML-код, связанный в первую очередь со стилевым оформлением Web* приложения. Полная версия данного Web-приложения находится на прилагаемом к книге компакт-диске, а также доступна по адресу http://www.softtime.ru/ info/downloads.php.
По нажатию на ссылку Текущий опрос открывается страница currenipoll.php, отображающая текущий опрос. Содержимое файла currentpolLphp приведено в листинге 11.5.
// Устанавливаем соединение с базой данных require_once{” ・./config.php");
?>
<а href=11 index.рЬри>Вернуться к управлению голосованиями</а><Ьг>
<?
// Запрашивается текущее голосование
$pol = mysql^query ("SELECT ♦ НЮМ poll WHERE archive = 1 active*rl);
// Если запрос выполнен с ошибкой, то останавливаем выполнение скрипта if(!$pol) puterror{"Ошибка при обращении к блоку голосования");
$poll = mysql_fetch_array($ро1);
if (mysql_nunt_rows ($pol) >0)
{
/ / Формируются управляющие ссылки на скрытие или отображение
if($poll[^ide1]) $strhide = “<а href=show.php?id_poll=
".$poll[*id_jjoll']・"&place=currentpol1.рЪр>Отобразить опрос</а>"; else $strhide ^ "<а href=hide.php?id_poll=
”.$poll [ * idjoll1 ] . M&place=currentpoll.php>CxpbiTb опрос</а>и; // Отображение управляющих ссылок
?>
<tablextr>
<tdxa href=addanswform. php?id_polle
<? echo $poll[ ^d^poll1 ] ?»Добавить ответ</ах/Ъб>
<td><? echo $strhide ?></td>
<tdxa href=putarchive.php?id_j>oll=
<? echo $poll [1 id_poll * ] ?»Направить опрос в apxwB</ax/td> </tr></table>
<?
}
// Просматриваются варианты ответов на вопрос голосования
$ query = 11 SELECT * FROM answer
WHERE id_poll = "^роХКЧа^оИ1]." ORDER BY pos”;
// Вычисляется количество проголосовавших
$total = "SELECT SUM(hit) FROM answer
WHERE id_poll=" t $poU [' idjoll」;
$ans = mysqlquery£$query);
$tot = mysql_query ($total|;
if (!$ans I I !$tot) puterror (*'Нет активных опросов");
// Отображение вопроса
echo и<Ы>Вопрос: м ,$ро11 [ ^ате1 ] . ,,</Ы>н;
// Результаты запросов присваиваются соответствующим переменным $totl = mysql_fetch_array ($tot);
$totalhits$totl['SUM(hit) *);
if($totalhits -= 0) $totalhits = 1;
<table border^l>
<tr ali gn= " cent er11 >
<td>OTBeT</td>
<td>4MQjio проголо сова вших < /1 d>
<Ъс1>Процент, %</td>
<td>no3Mm<H</td>
<td со1зрап=2>Действия</Ьс1>
</tr>
// Цикл While проходит по всем ответам на вопрос while ($answer = mysql_fetch_array ($ans)) {
// Отображаем ответы и управляюи^е ссылки
echo n<tr><td>n.$answer【,口amef].n</td>
<td>**. $ answer [ ^hit * ] . T*</td>
^^'^sprintf (f,%01.1f%sM,
$answer[Thitl)/$totalhits*100, ^l) ,u</td>
<td>".^answer[*pos *].rt</td>
<td><a href^delansw.php?
id_3nswer=".$answer[,id^answer']・"&
place=currentpoll.рЬр>Удалить </a></td>
<tdxa href=editanswform. php?
id_answer=°.$answer[* id_answer *]."&
place=currentpoll* рЬр>Исправить </а></td>
</tr>M;
}
echo T'</table>";
echo **<p>Общее число проголосовавших составляет:
”.$8tlLSUM 出it) r] J,</p>n;
?>
В первую очередь устанавливается соединение с базой данных. Это делается включением файла config.php (см, листинг 11.6), который расположен на один уровень выше системы администрирования. Затем из базы данных запрашивается активное голосование. Если запрос результативен, то формируются и выводятся на страницу управляющие ссылки. Далее происходят обращения к базе данных с целью получения вариантов ответов на вопрос голосования и получения общего количества проголосовавших на данный вопрос. Общее количество проголосовавших используется для подсчета процентного отношения голосов за каждый вариант ответа по отношению к общему числу. Варианты ответов вместе со сформированным и управляющими ссылками Удалить и Исправить выводятся на страницу в виде таблицы* Ссылка Удалить указывает на скрипт удаления варианта ответа из таблицы (delansw.php). Ссьитка Исправить указывает на форму редактирования варианта ответа (editanswform.php).
Подключение к базе данных осуществляется с помощью файла config.php, который включается во все файлы, в которых есть обращения к базе данных (листинг 11.6).
;Листинг 116 Файл config.php匸
<?php
// Имя сервера базы данных $dblocation = иlocalhostн;
// Имя базы данных
$dbname = "poll";
// Имя пользователя
$dbuser = "root";
// Пароль к базе данных
$dbpasswd ="”;
$dbcnx = @mysql_connect($dblocation, $dbuaer/ $dbpasswd);
if (!$dbcnx) {
echo( "<p>B настоящий момент сервер базы данных не доступен, поэтому корректное отображение страницы невозможно.</р>н); exit ();
}
if (! @mysql__select_db {$dbname, $dbcnx)) {
echo( "<р>В настоящий момент база данных не доступна,
поэтому корректное отображениепоэтому корректное отображениестраницы невозможно.</р>и);страницы невозможно.</р>и);
поэтому корректное отображение
поэтому корректное отображение
страницы невозможно.</р>и);
страницы невозможно.</р>и);
exit();
// Небольшая вспомогательная функция,
// которая выводит сообщение об ошибке
// в случае ошибки запроса к базе данных function puterror(^message) {
echo("<p>$message</p>"};
exit ();
}
?>
Файл archivepoll.php отвечает за отображение архива системы голосования. Его код приведен в листинге 117
罗y^gjjjj||j^^^t^^jm^uyj<t^j^y^yg^^£:貝.琴^y^^<^^^yi^*^Fy^H^^yj^My;?V^yi?一番..*¥1;”"**空1:督,***”¥,,・1¥:1;;臀?戈*斗¥『:笈狞3
Листинг 11.7. Файл archivepoll.php:
<?php
// Устанавливаем соединение с базой данных require_once ('*.. /config. php");
?>
<а href=llindex«рЬр">Вернуться к управлению голосованиями</а>
<?php
// Уст анавлива ем соединение с базой данных require_once (".. /config*php**);
// Запрашиваем все архивные опросы
$pol = mysql_query("SELECT * FROM poll
WHERE archive = * archive *
ORDEH BY putdate DESC");
if (!$pol) puterror (,rОшибка при обращении к блоку голосования**); while ($poll = mysql_fetch__array ($ро1))
// Вычисляем число участников
$query ^ мSELECT sum(hit) AS total FROM answer WHERE id_poll=n.$poll[1id_poll1];
$num = mysql_query($query);
if ($num) $number = mysql fetch array ($num);
else puterror("Ошибка выборки из таблицы ответов");
$totalhits = $number[1 total1];
// Формируем строчку скрыть/отобразить
if ($poll [ ^ide1 ]) $strhide = 11 <а href=show.php?id_poll=
”,$pcll「id_po:LL'] ."&
place^archivepoll.php>OTобразить </а>";
else $ strhide = м<а href=hide.php?id__poll=
”・$pcn.['id_jx>ll'] J&
place=archivepoll. рЬр>Скрыть < / a>11;
〃 Выводам результаты
echo "<h2>** .$poll[ ^ame1 ]・"v/h2>";
echo "<table bordemO cellpadding=7xtr>";
echo н<ъс1>Проголосовало: $totalhits</td>
<td>flaTa: ".$poll[*putdate']."</td>
<td>$strhide</td>
<td><a href=setactive.php?
id_poll=” ・ $poll [1 id__poll」• ”>
Сделать aKTHBHbiM</a></td>
</tr></table>,1;
// Выводим таблицу ответов и статистику
// Просматриваем вопросы опроса
$query = "SELECT * FROM answer
WHERE id_poll = ”・$polirid_polJL'] J
ORDER BY pos°;
$ans = mysql_query ($query);
if (! $ans) puterror ("Нет активных опросов11);
// Выводим ответы
if($totalhits = 0) $totalhits = 1;
?>
<table border=l>
<tr align=t*centerM>
<td>OTBeT</td>
<td>Число проголосовавших</td>
<td>npou,eHT, %</td>
<td>no3MUHH</td>
<td со1зрал=2>Действия</td>
</tr>
<?
while ($answer = mysql_fetch_array ($ans)) (
echo "<tr><td>n. $ answer「name * ] . rl</td>
<td>H,$answer[rhit1]* M</td>
<td>° .sprint f (**% 01. lf%sM,
$answer[ •hit* ] /$totalhits* 100, *%*) .**</td>
<td>". $answer [ *pos,]. ,r</td> ctdxa href^delansw. php?
id_answer=".$answer[1id_answer■]•"&
p1ace=archivepo11.рЬр>Удалить</а></td>
<tdxa href=editanswfona.php?
id_answer^n .$answer「id_answer* ] *n& place=archivepoll.рЪр>Исправить </а></td> </tr>H;
) echo l'</table>11;
} ?>
Файл archivepolLphp похож на файл currentpoll.php и отображает голосования, находящиеся в архиве. Из базы данных запрашиваются архивные голосования. Для найденных архивных голосований запрашивается общее количество проголосовавших за все варианты ответов. Далее формируются управляющие ссылки Скрыть или Отобразить, с помощью которых можно спрятать голосование от просмотра посетителями сайта. В формируемые ссылки дополнительно добавляется параметр place, который равен названию страницы с архивом голосований —— archivepoll.php. Этот параметр нужен для корректного автоматического перенаправления на эту страницу после сокрытия или отображения голосования, так как скрипты сокрытия и отображения (show.php и hide.php) могут вызываться как со страницы с текущим голосованием, так и со страницы с архивными голосованиями. Формируется управляющая ссылка Сделать активным, с помощью которой голосование можно сделать текущим. Для каждого голосования выводится таблица с вариантами ответов и управляющими ссылками для каждого ответа. Код, выводящий эту таблицу, аналогичен соответствующему блоку кода из файла cunentpoll.php.
Форма для добавления нового голосования, ссылка на которую находится на главной странице системы администрирования, расположена в файле addpollfbrm.php (листинг 11.8).
号国此彰瑞占?彎皆号国此彰瑞占?彎皆истинг 11.8. Файл addpollform.php
号国此彰瑞占?彎皆
号国此彰瑞占?彎皆
<a href=Hindex.рЪрм>Вернуться к управлению голосованиями</a> vform action^addpoll.php method=post>
Вопрос: <input type=text name=question valuer *f >
<input type^submit ¥а1ие«Добавить>
</form>
Файл addpollform.php содержит форму для ввода вопроса, который будет участвовать в голосовании. Обработчик формы находится в файле addpoll.php (листинг 11.9)*
:产,望涵?у気号屮胃学纲览鋼片f追绥变”,吧里簪£
;Листинг 11.9. Файл addpoll.php
Ш^Ш^<?php
Ш^
Ш^
// Устанавливаем соединение с базой данных require_once(".*/config.php");
// Проверяем, введен ли вопрос голосования
if (en^ty ($_POST[1 question' ]) ) links ("He введен вопрос голосования.. .,r); // Заменяем одинарные кавычки обратными
$_POST「question •] = str_replace「% …,$_POST「questionf]);
// Перед добавлением нового голосования
/ / все текущие голосования переводам в архив mysql_query("UPDATE poll SET archive = 'active'");
// Добавляем новый опрос
$ query ^ "INSERT INTO poll VALUES(0,
•".$_POST「question1] .MS 'active I
'show1,now())";
if (mysql__query ($query)) {
echo M<HTMLXHEAD>
<META HTTP-EQUIV=,Refresh1 CONTENT=rO; DRl>currentpoll.php*> </HEADX/HTML>”;
} else puterror ("Ошибка при обновлении таблицы опросовrt); function links <$msg)
echo "<p>"・ $msg ・"</p>";
echo "<р><а hrefM onClick=*history.back()*>
Вернуться к правке вопроса</ах/р>”;
echo "<р><а href-1 currentpoll.php1>Текущий опрос</а></р>°; exit ();
После подключения к базе данных проверяется, введен ли вопрос голосования. Если вопрос не был введен в форме addpollform,php, то управление передается вспомогательной функции links о, которая выводит сообщение об ошибке н останавливает выполнение скрипта. Далее текст вопроса обрабатывается функцией str_replace(), которая заменяет одинарные кавычки обратными. Это делается, чтобы избежать возможных ошибок при работе с базой данных в операции insert. Далее выполняется запрос к базе данных, переводящий текущее голосование в архив. И только после этого новый опрос добавляется в таблицу poll, где хранятся вопросы голосований. Если запрос на добавление нового голосования успешно завершен, то с помощью метатегов HTML происходит перенаправление на страницу просмотра текущего опроса, которым теперь стало новое добавленное голосование.
Чтобы добавить вариант ответа для нового голосования, необходимо открыть страницу, содержащую форму добавления варианта ответа (см. рис. 1L5). Это можно сделать по нажатию на управляющую ссылку Добавить ответ, расположенную на странице с текущим голосованием (см. рис. 11.4). Код файла addanswform.php приведен в листинге 11.10,
;Листинг 11.10. Файл addanswform.php
<?
// Устанавливаем соединение с базой данных require_once("•./config.php");
// Устанавливаем переменные по умолчанию
if(J isset($action)) $action = "addansw・php";
if(!isset($title)) $title = "Новый зариант ответа";
if {! isset (^button)) $ but ton = и Добавить'1;
// Если позиция варианта ответа не передана,
// устанавливаем новый вариант ответа в последнюю позицию + 1 iff!isset($pos))
$query ^ "SELECT MAX(pos) FROM answer
WHERE idjoll=". $_GET [' id___poll'];
$num = mysql_query ($query);
if ($num) $pos = mysql_result ($num, 0) + 1;
}
?>
<a href-"currentpol1.php">Вернуться к управлению голосованиями</а><Ьг>
<form action=<?php echo $action; ?> method=post>
<table>
<trXtd>BapMaHT ответа: </td><td>
<input type^text name=question value=*<?php echo $question;?>*> </tdx/tr>
<tr><td>no3HimH: </td><td>
<input type=text name=pos value=<?php echo $pos; ?»</tdx/tr> <tr><tdx/td>
<td><input type=submit value=<?php echo $buttan; ?»</tdx/tr> </table>
<input type=hidden name=place value^<?php echo $_GET[rplacer]; ?»
<input type=hidden name=id_answer
value«<?php echo $_GET[* id_answer1]; ?»
<input type=hidden name-id__poll value=<?php echo $_GET [1 id_poll1 ]; ?» </form>
После подключения к базе данных проверяется, определены ли переменнее $action, $title, $button, и если они не установлены, то им присваиваются значения по умолчанию. Это сделано для того, чтобы иметь возможность использовать данную форму не только для добавления нового варианта ответа, но и для редактирования уже существующего — в этом случае данные переменные будут определены в скрипте, в который будет включена форма. Далее проверяется, установлена ли переменная $pos, которая отвечает за порядок вывода ответов. Переменная может быть установлена, если данная форма используется для редактирования существующего варианта ответа. Если переменная $роз не установлена, то ей присваивается максимальное значение позиции для данного опроса, увеличенное на 1. Для этого выполняется запрос к таблице answer, с помощью которого вычисляется максимальное значение поля роз. Далее выводится форма ввода варианта ответа, которая включает в себя: текстовое поле для ввода варианта ответа, текстовое поле для ввода позиции, скрытое поле для передачи идентификатора ответа id answer (при редактировании) н скрытое поле для передачи идентификатора голосования id pcii, к которому относится вариант ответа. Если данная форма используется для добавления нового варианта ответа, то обработчиком формы назначается файл addansw.php (листинг 11.11).
霆畧;¥:W夂匕*"ey^^j&y.^jft^^^gj^^t^yw^^iijfyjjy^i^j^j^^j^p^Aiif^ftvj^Ef^pm* ■咎二”一…:叮:二中宀”罂”今尤中::.*、.*-,.,—……,^•м**м \”,±.竺**j
;Листинг 11.11. Файл addansw.php
<?php
// Устанавливаем соединение с базой данных require_once (".・ /config. php");
// Проверяем, введен ли вариант ответа на вопрос голосования
if (en^Dty ($_POST[f question1 ]) > links (**He введен вариант ответа. •.,f);
if (empty ($_POST [' posT ]))
links("He введена позиция ответа на вопрос...");
/ / Заменяем одинарные кавычки обратными
$_POST['quest ion1] = stjmplace (”'"「、",$_FOST「question']);
// Добавляем новый вариант ответа
$ query = rt INSERT INTO answer VALUES (0,
'"•$_POST「question『]•” I ,l.$_POST(,pos* ] -f\ 0,
"・ $_POST [,id_poll ']・”)“; if (mysql_query($query))
{
echo M <HTML><HEAD>
<META HTTP-EQUIV=1 Refresh * CONTENT=fO; URL^currentpoll. php * > </HEAD></HTML>";
}
else puterror ("Ошибка при обновлении таблицы ответов11);
?>
После подключения к базе данных проверяется, переданы ли переменные question и pos в суперглобальном массиве $__post. Если переменные не переданы, то управление передается функции links О, которая выводит сообщение об ошибке и останавливает выполнение скрипта. Код функции links () не приведен в данном листинге, но полностью соответствует коду в файле addpoll.php (см. листинг 11.9)- Далее в переменной $_post [f quest ion *} одинарные кавычки заменяются обратными с помощью функции str_repiace <). С помощью SQL-запроса insert вариант ответа добавляется в таблицу answer. Если запрос успешно выполнен, то осуществляется автоматическое перенаправление на страницу с результатами текущего голосования (см. рис. 11.4).
Редактирование варианта ответа осуществляется с помощью формы editanswform.php (листинг 11.12).
:Листинг 11.12. Файл editanswform.php
<?php
// Устанавливаем соединение с базой данных
require once (м. . /config.php1');
// Настраиваем форму addanswform.php - изменяем название
// окна, кнопки, переопределяем скрипт-обработчик
$title а иИсправить ответ11;
$button = ^Исправить|Г;
$action = "editansw.php";
// Запрашиваем содержимое записи таблицы answer с первичным
// ключом $id_answer
$query = "SELECT * FROM answer
WHERE id_answer = " • $_GET [,id__answer,1;
$ans = mysql_query ($query);
if(!$ans) puterror("Ошибка при обращении к таблице ответов");
$answer = mysql_fetch_array ($ans);
$question = $answer[* name *];
$pos = $answer[,pos,];
include "addanswform.php”;
В начале скрипта осуществляется подключение к базе данных. Затем настраиваются переменные $titie, ^button и $action, которые определяют название окна, название кнопки и обработчик формы ввода данных. Форма ввода подключается в конце файла инструкцией include r'addanswform*phpr,. Информация о варианте ответа (текст ответа и позицию) формируется с помощью запроса из базы данных. Идентификатор варианта ответа передается через строку запроса. Если запрос результативен, то инициализируются соответствующие переменные, которые необходимы для заполнения формы ввода данных. Обработчик формы добавления находится в файле editansw.php (листинг 11,13).
製宫附 fjjiy "!呸'■''•' V''1' У;『心”:”,叫,拈 1 总Ц* м»»' »уд*; *]•**[『,掌^f £ i
Листинг 11ЛЗ. Файл editansw.php
車口痴仁齐麺f” 片京 Z; Cn ・V« “ r;,tmiiV** .弄『.;.卜囁 С ♦ n;П • H.. /Г.т;-铝'it A
<?php
// Устанавливаем соединение с базой данных require_once(n../config.phpH);
// Проверяем, введен ли вариант ответа на вопрос голосования
if(empty($__POST[,question1])} links(пНе введен ответ на вопрос»,*");
if (eruptу ($_POST [ *pos1 ]))
links ("He введена позиция ответа на вопрос...11);
// Заменяем одинарные кавычки обратными
$_POST [1 question1 ] = str replace (н 1", f,' ", $_POST ['question1});
// Исправляем вопрос
$query = "UPDATE answer SET name=*". $_PO8T['question']."3 pos=,l.$_POST[,pos, ] J WHERE id_answer=n. $_POST [ * id_answer * ];
if (mysql_query ($query)) <
echo "<HTML>VHEAD>
<META HTTP-EQUIV='Refresh' CONTENT^「0;URL=rL $_POST(*place']."‘>
</headx/h™l>";
} else puterror("Ошибка при обращении к таблице ответов");
Удаление варианта ответа осуществляется с помощью скрипта delansw.php (листинг 11.14).
""f*'(!гУ'' '■ * *Л^?Л fЛ *■*■** ^ f ^5—* ”W,W$V‘‘" V' * tiV* V."' ,.^,*^ V W"." ・Ш,+У3,!5,.0 WE
:.■,靑(>;J;品心riN"J;**二*沦兩;T仁舔二t.必瓶%F糸而二、**;k…■匕.;;后氐M7;%肃二*,,.""';"3、言;;痴;露;;*f"扌**訂>宿*"***»二2呎,;1"**品”\**£."『门+岳"『岳,输
<?php
〃 Устана вливаем соединение с базой данных require__once (11.. /config * php");
// Формируем и выполняем запрос на удаление вопроса
$ query = "DELETE FROM answer WHERE id_answer=**. $_GET [ * id__answer1);
if (mysql_query ($query))
(
echo "<HTML><HEAD>
СМЕТА HTTP-EQU工V='Refresh' CONTENT=,0; URL=”.$_GET[fplace1] ^*'> </HEAD></HTML>M;
} else puterror("Ошибка при обращении к та&лице ответов");
В скрипте выполняется запрос на удаление записи из таблицы answer. Идентификатор удаляемой записи id answer передается через адресную строку браузера.
Актнвнь【访 опрос можно направить в архив с помощью скрипта puiarchive.php (листинг 11,15).
:Листинг 11.15. Файл putarchive.php
<?php
// Устанавливаем соединение с базой данных require_once(н../config.php"};
// Помещение голосования в архив
$query = "UPDATE poll SET archive = 1 archiver WHERE id^poll = ,f. $_GET [1 id_poll * ];
if (mysql_query($query))
{
echo "<HTMLXHEAD>
<META HTTP-EQUIV=1Refresh1 CONTENT=,0; URL=currentpoll.phpf> </HEADX/HTML>n;
else puterror ("Ошибка при обращении к таблице опросов*1);
В скрипт передается идентификатор голосования, которое предназначено для отправки в архив. Затем выполняется скрипт обновления записи с этим идентификатором в таблице poll. Если запрос успешно выполнен, то осуществляется автоматическое перенаправление на страницу текущего голосования.
Скрытие голосования от просмотра посетителя\ги сайта осуществляется скриптом в файле hide.php (листинг 11.16).
*・,..*?и«・^^Ц^*^^^Ц^0*З^^^^^^^^^Ц^(*^^^^^^,7¥+・.f*・,********.********«**,*F **・:•.*,,,,,**•••・・,•・・•・♦***,*+*****■•***♦*,*"***"•**<• у^лл、,,,・**3*,а,*,,,+#*^д^£у**・Н・**・,
j Листинг 11.1S. Файл hlde.php
<?php
// Устанавливаем соединение с базой данных reqnire_once(м../config.php");
// Формируем и отображаем SQL-залрос на сокрытие опроса
$ query = 11 UPDATE poll SET hide = 'hide'
WHERE idqlln” ・ $_GET ( * idjpollf];
if (mysql_query ($query)) { echo rr<HTML><HEAD>
СМЕТА HTTP-EQUIV='Refresh' CONTENT=' 0; URL=n . $_GET [ 1 place ']."'> </HEADX/HTML>t,;
} else puterror("Ошибка при обращении к базе опросовн);
?>
В скрипте выполняется запрос на изменение поля hide для записи с идентификатором id pQll, который передается через строку браузера. Если запрос выполнен успешно, то осуществляется автоматическое перенаправление на страницу, имя которой содержится в переменной place. Переменная place также передается через адресную строку браузера. Скрипт отображения голосования работает аналогично. Единственное отличие заключается в строке запроса к базе данных. Для отображения голосования необходимо поле hide установить в значение show*.
Система представления
Система представления отображает голосование, его результаты и архивные голосования для посетителей сайта. Система представления состоит из 3 файлов:
口 index.php —— скрипт отображения текущего голосования на сайте;
口 vote.php — скрипт, учитывающий голосование посетителя н отображающий результаты текущего активного голосования;
□ archive.php — скрипт, отображающий архивные голосования.
Код файла index.php приведен в листинге 11.17.
^,^tV^w''vvyv,\v;»,,»y^v'y,?x*^rViyr^^^Y*'r',1,,*'yV','^V? LT/'i,o'^'r'''V"''7',,'t"','',o,'',,T.^,,V,'',7^,r,*,,"**'*'^'''rr*^*,*V-*^V''','",'*',,,*,,T*V',*^vj,**'V*;i'*
;:Листинг 11.17. файл Index.php
> *«.*; *3; w г..*,”.; .丁启,3**“*+**>vb** ,】*,《*•,;. “♦ г.* ■■■/■■.. ",?*"2'%^7i ■乔1;『,••••;、;“;,..♦* г」.;■ ■ /. t.i,..、*異 f* irtH ait ■ *,. .、“,“;.. m rA;; “SI、**“♦;;;«■> 普箝;玄$*♦*;喜^ТГс+иП'、*,*;,*
<?php
// Устанавливаем соединение с базой данных
include "config.php”;
// Запрашиваем текущий опрос, на который есть варианты ответов $pol = mysql_query(иSELECT poll*name,
poll.id_poll,
answer»name As answer.
answer.id_answer
FROM poll, answer
WHERE poll.archive = 1 active1 AND
pell,hide = 1show * AND poll. id_poll = answer. id_poll,f);
// Если при обращении к базе данных не было ошибок и // результат запроса не пустой, то выводим голосование на страницу if ($pol and mysql_num_rows ($pol) >0)
$poll = mysql_fetch_array ($рт1);
// Отображаем вопрос голосования
echo $poll['паше1]."<br>";
echo "<fonn action^ote.php method=postxtable border=l>'*;
// Циклом do* *.while выводим на страницу варианты ответов do (
echo H<tr><tdxinput type=radio name=id_answer value=f,. $poll [ * id_answer,] , "></td>
<td>H.$po工丄['answer1 ] ."</tdx/tc”;
} while ($poll - mysql_fetch_array ($pol));
// Скрытое поле, передающее илентификатор голосования
echo "<input type=hidden name3id_poll value=".Spoil[rid_poll1].">";
echo M<tr><td></td><td><input type=submit
value='Голосовать,></td></tr>M;
echo M</table></fonn>";
Подключение к базе данных осуществляется включением файла config.php. Далее осуществляется многотабличный запрос к базе данных, который выбирает активные не скрытые голосования, при этом должны существовать варианты ответов на вопросы голосования. Если все условм$1 выполняются н результат выполнения запроса не пустой, то осуществляется вывод вопроса и вариантов ответов на него на страницу с помощью цикла do.. .while. Обработчиком формы с вариантами ответа является файл vote.php (листинг 11.18).
•”1^^?** 竺*м,я**и 裁*^у&&шим«аамл>:,рлм~жммкл,лг:&у&з”^,м*.?,г.шзмам:¥&,А?,,л” м 廿 “л;出•:ta 寸 ” f,乂,…:•лиьулыг'-зудймм'-млмс
ИНГ 11.18. Файл vote.php
• ••rfiii. X • >'■■ ri->i< ««■«•'I'i'a^».. J・z ••'. *、・U“ • ............'..I.. J. ,..ti ^S;4 .■г<:Л'Л41«,Х*1>'А*»,**11Й!',>Л**»'«'*1«Л-«'ч * a <r; > < >'« ■ 4 al k« ■ a ■ *,♦ ;i・■>< AntilSttrtt'tt ^41 r itift Л<??Л«'» •» «VW
<?php
// Устанавливаем соединение с базой данных
include "config*phpM;
// Учитываем голос
if(!empty($_POST「id_answer1]))
$id_answer = $_PQST [' id_answer1 ];
// Получаем IP^адрес посетителя —
// это необходимо для реализации защиты от накруток $forward = getenv(HTTP_X_FORWARDED_raR);'
$ip = urldecode(getenv(HTTP_CLIENTIP));
if (($forward != NULL) && ($forward != ^REMOTE_ADDR))
$ip ^ $ip* и/и*$forward;
// Проверяем наличие 工Р—адреса посетителя в таблице poll_ip:
// В ней содержатся IP-адреса последних проголосовавших посетителей $isip = "SELECT ★ FROM poll_ip
WHERE ip = ftp,
AND puttime > now() - interval *2’ minutefr;
$ipresult = mysql_query($isip);
// Удаляем устаревшие записи в таблице poll^ip
// (IP-адреса посетителей, голосовавших более 2 минут назад)
$delip = "DELETE FROM poll_ip
WHERE puttime < now() - interval ‘2’ minute";
mysql_query($delip);
// Проверка на ошибки при выполнении запроса
if($ipresult)
(
// Если результат запроса не содержит записей,
// то посетитель с данным IP-адресом не участвовал в голосовании
// за последние 2 минуты, и его голос дотскен быть засчитан
if (mysql_num_rоws {$ipresult) <1) {
// Заносим IP-адрес посетителя в таблицу poll_ip
mysql^query (11 INSERT INTO pol l_ip VALUES (0, 屮 ip *, now {))");
// Заносим результат голосования посетителя в таблицу answer
$query = мSELECT hit FROM answer
WHERE i cleans we r = $id_answerH;
$num = mysql_query($query);
if ($nmn)
(
$ number = mysql_fetch_array ($num);
$query = "UPDATE answer SET hit = °.($number[rhit1]+l).H WHERE id_answer = $id_answer";
mysql_query ($query);
} else puterror("Ошибка при обращении к блоку голосования");
}
else
echo и<Ь>Вы уже голосовали за последние 2 минуты.
Ваше голосование не будет засчитано.</Ь>°;
else
{
echo "Вы не выбрали вариант ответа";
}
// Отображаем результаты голосования
^query = “SELECT ★ FROM poll WHERE archive 匸 * active1 AND hide = 1 show1";
$pol = mysql_query ($query); if ($pol)
{
Spell - mysql_fetch_array($pol);
// Отображаем вопрос голосования
echo ,l<h3>".$poll fname1] Jv/h3>”;
// Просматриваем ответы на вопрос голосования
//и статистику голосования по ним
$query = пSELECT * FRCW answer
WHERE id_j>oll = *' • $poll【'id_poll']・" ORDER BY pos”;
// Подсчитываем сумму всех прогол о сова вших в текущем голосовании
$total = "SELECT SUM(hit) FROM answer
WHERE id^poll = '\ $poll[1id_poll11;
$ans = mysql_query ($query);
$tot = mysql_query ($total); if($ans && $tot)
$totl = mysql_fetch_array ($tot);
$totalhits = $totl[* SUM(hit)*];
if{$totalhits = 0) $totalhits = 1;
// Выводам заголовок таблицы с результатами голосования echo "<table border=l>
< t г > <t d >Отв ет< /1 dxt с!>Проголосовало< /1 d> < t d> % < / td></tr>n; while ($answer = mysql_fetch__array ($ans)}
{
// Выводим результаты голосования echo "<tc
<td>".Sanswer['name•]."v/td>
<td>".$answer['hit']."</td>
<td>,f ・ sprintf「鱷 01.1£%з”,
$answer[*hit*]/$totalhits*100r111)."</td>
</tr>";
echo "</table>“;
echo ** Общее число про голосов авшюс составляет:
”・$Stl「SUM{hit) *] ."<br><br>H;
〃 Ссыпка на просмотр архива
echo **<а href=archive*php>noсмотреть архив гсшосований</а><Ьг>н;
} else puterror ("Ошибка при обращении к блоку голосования*1);
} else puterror("Ошибка при обращении к блоку голосованияи);
Скрипт vote.php осуществляет учет голосования посетителя, защиту от повторного голосования с одного IP-адреса в короткое время (зашита от "накруток") и выводит результаты голосования на страницу. Первоначально осуществляете月 подключение к базе данных, затем проверяется, передан ли вариант ответа в скрипт через форму ($_post [ • id_answerr ] )* Если переменная не пустая, то осуществляется проверка на повторное голосование. Для этого IP-адрес только что проголосовавшего сравнивается с 1Р-адресамн голосовавших за последние 2 минуты; эти IP-адреса хранятся в таблице poii_ip* Если адрес только что проголосовавшего найден в таблице poll_ip, то голосование не засчитывается, н посетителю выводится соответствующее сообщение. Одновременно с этим из таблицы poii ip удаляются записи дл丸 голосовавших более чем 2 минуты назад. Это предупреждает накопление в таблице неактуальных записей. Если IP-адрес посетителя не найден в таблице poii_ip, то его голосование учитывается в системе, а его IP-адрес заносится в таблицу poll_ip. Затем на страницу выводятся результаты текущего голосования, после которых выводится ссылка на просмотр архива голосований. За отображение архива голосований отвечает файл archive.php (листинг 11.19)»
:Листинг 11.19. Файл archive.php:Листинг 11.19. Файл archive.php
:Листинг 11.19. Файл archive.php
:Листинг 11.19. Файл archive.php
<?php
/ / У стана вливаем соединение с базой данных include "config.php”;
// Запрашиваем доступные толосовани貝 в архиве $query = "SELECT * FROM poll
WHERE archive = Archive1 AND
Hide = * show1 ORDER BY putdate DESC";
$pol = mysql_query($query); if ($pol) {
// Циклом While проходим по всем голосованиям в архиве while ($poll = mysql_fetch__array ($pol})
{
// Вычисляем число проголосовавших
$total = иSELECT SUM(hit)
FROM answer
WHERE idjpoll^. $poll [1 id__p0111 ];
$tot = mysql_query ($total); if ($tot)
// Если запрос выполнен, то присваиваем
// число про голо сова вших переменной $totalhits
$totl = mysql_fetch_array ($tot);
$totalhits = $8tl [• SUM (hit) 4 ; } else $totalhits 二 0;
// Выводам результаты голосования
echo 11 <table border=0><tr>
<tdxh2>Bonpoc:"・ $poll [ 'name1 ] »n</h2></td>
<td>IIpo гол особ ало: ,r.$totalhits. M</tdx/tr>ri;
// Выводим таблицу ответов и статистику
echo n<trxtd colspan=2>
<table border=l width=100%>M;
// Прооматриваем ответы на вопрос голосования
$query = "SELECT * FROM answer
WHERE id_poll=" * $poll [' id』oJA' ] J ORDER BY pos'1;
$ans = mysql_query ($query); if($ans) (
// Проверяем переменною $totalhits на равенство 0,
// чтобы не было ошибок деления на ноль в дальнейшем
if ($totalhits==0) $totalhits ^ 1;
/ / Выводим отвечы
echo "<tr bgcolor=#EEEEEE>
<Ъс1>Ответ</ td>
<td>Число проголосовавших</td>
<td>nponeHTr %</td></tr>M;
// Циклом While проходам по всем ответам на вопрос голосования while($answer * mysql_fetch_array($ans)) {
echo "<tr><td>H.$answer「name,].M</td>
<td>".$answer[,hit']・"</td>
<td>". sprintf (,,%01.1f%stl,
$answer (1 hit1 ] /$totalhits*100, 1 %•)・"</td>
</tr>H;
}
echo "</tabIe>";
}
echo ,f</table><br>r,;
}
} else puterror ("Ошибка при обращении к блоку голосования1*);
?>
В файле archive.php происходит вывод результатов нескрытых голосований, находящихся в архиве. Признаком местонахождения голосования в архиве является значение поля archive = 1 archevef* В остальном формирование вывода архивных голосований происходит аналогично выводу результатов текущего голосования. На этом разработку системы голосования можно считать законченной-
Гостевая книга
Гостевая книга обычно состоит из двух частей: блока представления (страниц для добавления и отображения сообщений на сайте) и блока ад ми* нистрирования (страниц, где администратор может осуществить модерирование сообщений и ответить на вопросы посетителей).
Для работы гостевой книги потребуется таблица guest в базе данных MySQL Данная таблица будет содержать 9 полей:
id_msg — первичный ключ таблицы, снабженный атрибутом
AUTO_工NCREMENT;
口 name — имя посетителя;
D city — город, в котором проживает посетитель;
email — e-mail посетителя;
url — адрес домашней страницы посетителя млм сайт компании, которую он представляет;
口 msg — текст сообщения;
answer —— ответ администратора гостевой книги на сообщение;
口 puttime ——время добавления нового сообщения;
hide — поле типа еьшм5 принимающее 2 значения: fshow' н 'hide'; в первом случае сообщение доступно для просмотра с сайта, во втором ——оно скрыто, и доступ к нему можно получить только со страницы администрирования.
Обязательными для заполнения посетителями являются поля для имени (name) и текста сообщения (msg). Поля city, email н url заполняются по желанию. Все остальные поля не предназначены для заполнения посетителем И заполняются либо ДВИЖКОМ форума (id_msg, puttime И hide), либо адм И Н истратором (answer И hide).
SQL-оператор create, позволяющий создать таблицу guest, представлен в листинге 11.20.
CREATE TABLE guest {
idjnsg 工NT(8) NOT NULL AUTO^TNCREMENT,
name TINYTEXT NOT NULL, City TINYTEXT NOT NULL, email TINYTEXT NOT NULL, url TINYTEXT NOT NULL, msg MEDIUMTEXT NOT NULL, answer MEDIUMTEXT NOT NULL, puttime DATETIME NOT NULL DEFAULT 10000-00-00 00:00:00、 hide ENUMt^how', ^ide1) NOT NULL DEFAULT 'show1, PRIMARY KEY (idjnsg) )TYPE^ylSAM;
Разработка гостевой книги будет начата с блока представления, так как именно с помощью него происходит заполнение базы данных.
[ Замечание )
Разработку Web-приложения следует начинать с блока, несущего ответственность за информационное наполнение базы данных. В этом случае остальные
блоки, использующие информацию из базы данных, будет легче разрабатывать и отлаживать.
Блок представления состоит из 3 файлов:
口 config.php ——конфигурационный файл гостевой книги;
index.php ——главная страница гостевой книги, несущая ответственность за отображение сообщений гостевой книги на сайте;
口 addrec.php —— файл, содержащий HTML-форму м ее обработчик для добавления нового сообщения в гостевую книгу.
:Замечание )
Далее в листингах для удобства восприятия будет опущен несущественный HTML-код, связанный, в первую очередь, со стилевым оформлением Web- приложения. Полная версия данного Web-приложения находится на прилагаемом к книге компакт-диске, а также доступна по адресу http://www.softtime.ru/ info/downloads.php.
Конфигурационный файл config.php содержит код соединения с базой дан^ ных MySQL и ряд параметров Web-приложения. Код файла приведен в листинге 11.21.
峭歌产屮県;'+'****"":".1!'…'*翠…8***+,*………寸*,眩4览?.……"т…"Г” - ........»«..«О.-Л-«»-ЧЛО«"-»«»_■■*►»
Листинг 11.21. Файл config.php
<?php
// Имя сервера базы данных $dblocation = "Iccalhost";
/ / Имя базы данных
$dbname ^ ,(guest boo к11;
// Имя пользователя
$dbuser = **root";
// Его пароль
$dbpasswd ="”;
〃 Число выводимых сообщений на странице
$pnumber = 10;
// Отправлять письмо на e-mail при добавлении нового сообщения
// в гостевую книгу? Для включения данного сервиса исправьте на true $sendmail = false;
// e-mail, на который следует отправлять сообщение $valmail ^ "mymail^roail, ru11;
// Версия системы
$version ="1.2.0";
〃 Соехсиняемся с сервером базы данных
$dbcnx = 0mysql_connect($dblocationf $dbuser, $dbpasswd); if (!$dbcnx)
(
echo( "<P>B настоящий момент сервер базы данных не доступен, поэтому корректное отображение страницы невозможно.</Р>"); exit ();
)
// Выбираем базу данных
if (! 0mysql__select_db ($dbname, $dbcnx)) {
echo ( "<P>B настоящий момент база данных не доступна, поэтому корректное отображение страницы невозможно.</Р>11 ); exit ();
}
// Небольшая вспомогательная функция, которая выводит
// сообщение об ошибке в случае ошибки запроса к базе данных function puterror($message) {
echo (<p>$mes sage</p>,r); exit();
} ?>
Как видно из листинга 11.21, в начале файла следует конфигурационная информация. Первые 4 переменные определяют параметры соединения с базой данных:
口 $dbiocation — адрес сервера базы данных (localhost, если тестирование происходит на локальной машине);
口 $dbname — нмя базы данных;
口 $dbuser — имя пользователя;
П $dbpasswd — его пароль.
Переменная $pnumber определяет число сообщений, выводимых на одной странице> Если число сообщений в таблице guest базы данных MySQL будет превышать это значение (по умолчанию 10), на странице будет выведена ссылка Следующие, переход по которой приводит к странице, где представлены следующие 10 сообщений гостевой книги. Логическая переменная $sendmail может принимать 2 значения: true или false» разрешая или за-
прешая отправку уведомляющего почтового сообщения по адресу в переменной $valmail.
За вывод сообщений гостевой книги ответствен файл index.php, содержимое которого представлено в листинге 11.22. Внешний вид страницы, генерируемой файлом index.php, представлен на рис. 11.8.
Рис. 11.8. Внешний вид гостевой книгиРис. 11.8. Внешний вид гостевой книги
Рис. 11.8. Внешний вид гостевой книги
Рис. 11.8. Внешний вид гостевой книги
Скрипт в файле index.php принимает единственный параметр start. Через этот параметр передается позиция, начиная с которой следует выводить сообщения (листинг 11.22).
я g,*¥** *адлл" jj* 4*tS* ^* * • f,,,+*j^gy^^*y.,:K^^Uy、£: *.■_■_■_■ Kj * 5e5< *У5^УЛУЛ ■ :_*』*»*,,,,U,t::,航” :У,**广*,*拼,;Листинг 11.22. Файл index.php.工立二注£工之я g,*¥** *адлл" jj* 4*tS* ^* * • f,,,+*j^gy^^*y.,:K^^Uy、£: *.■_■_■_■ Kj * 5e5< *У5^УЛУЛ ■ :_*』*»*,,,,U,t::,航” :У,**广*,*拼,;Листинг 11.22. Файл index.php.工立二注£工之: J 1ПЬ ■ ПП1 I 1 •■*>. +SLIJ ;;,,*** “ * ;*;片二■;•““;曲 з;"%;}**^^;#?**;.*: J 1ПЬ ■ ПП1 I 1 •■*>. +SLIJ ;;,,*** “ * ;*;片二■;•““;曲 з;"%;}**^^;#?**;.*;;『;.-;*¥,.: • k 14 « > ;.»*"+;'d«;;『;.-;*¥,.: • k 14 « > ;.»*"+;'d«
я g,*¥** *адлл" jj* 4*tS* ^* * • f,,,+*j^gy^^*y.,:K^^Uy、£: *.■_■_■_■ Kj * 5e5< *У5^УЛУЛ ■ :_*』*»*,,,,U,t::,航” :У,**广*,*拼,
;Листинг 11.22. Файл index.php.工立二注£工之
я g,*¥** *адлл" jj* 4*tS* ^* * • f,,,+*j^gy^^*y.,:K^^Uy、£: *.■_■_■_■ Kj * 5e5< *У5^УЛУЛ ■ :_*』*»*,,,,U,t::,航” :У,**广*,*拼,
;Листинг 11.22. Файл index.php.工立二注£工之
: J 1ПЬ ■ ПП1 I 1 •■*>. +SLIJ ;;,,*** “ * ;*;片二■;•““;曲 з;"%;}**^^;#?**;.*
: J 1ПЬ ■ ПП1 I 1 •■*>. +SLIJ ;;,,*** “ * ;*;片二■;•““;曲 з;"%;}**^^;#?**;.*
;;『;.-;*¥,.: • k 14 « > ;.»*"+;'d«
;;『;.-;*¥,.: • k 14 « > ;.»*"+;'d«
<html>
<head>
<title>rocT6Ba^ KHMra</title>
</head>
<body>
ca class=link href=r, addrec.phpn titLe="”>HanncaTB coобщение</a><br>
<table><tr><td><nop></td><td>
<?
// Осуществляем соединение с базой данных include "config,php";
// Извлекаем из строки запроса параметр start
if(isset($_GET[1 start1])) $start = $_GET[* start1];
else $start ="”;
// Стартовая точка
if (empty($start)) $start = 0;
// Стартовая точка не может быть меньше нуля
if ($start < 0) $start = 0;
// Запрашиваем общее число отображаемых сообщений
$query = "SELECT count(*} FROM guest WHERE hide = khcw'";
$tot ^ mysql_query <$query);
// Запрашиваем сами сообщения
$query = "SELECT * FROM guest WHERE hide = 'show' ORDER BY puttime DESC LIMIT $start, $pnuwber";
$thm = mysql_query ($query);
if(I$tot I I !$thm) _puterror("Ошибка при выборке сообщений...n); // Общее число отображаемых сообщений $count= mysql_result($totr 0);
// Выводим ссыпки на предыдущие и следуоцие сообщения
if ($start > 0) print М<А href=index.php?start='\ ($start-$pnumber).n>Предыдущие < /А> ";
if ($count > $start + $pnuinber) print ” <A
href=index.php?start=u.(Sstart + $pnuniber).°> Следующие</A>";
while($themes = mysql_fetch_array($thm))
// Извлекаем переменные из базы данных
$палае = trim ($ themes [,name,]);
$city = trim ($themes [ ^ity* ]);
$ email = trim($themes [T emailr ] H
$url = trim. ($themes [ *urlT 1);
$msg = trim($themes [ 'insg']);
$answer = trim($themes[1 answer1]);
?>
<table>
<tr>
<td><? echo $name?x/b>
<? if (1 empty($city)) print H($city)r; ?></td>
<td>OT: <b><? print $themes{1putt ime *]; ?></td>
</tr>
<tr>
<td><? if (!empty($email))
print “e-mail: <a class=link href=mailto:$email>$email</a>M; ?>
<? if (!empty($url)) print "www: <a class^link href =1 $url1 >$url</a>,'; ?></td> </tr>
<tr>
<td><? echo $msg; ?><br>
<?
if (lempty<$answer) && $answer !=)
print n<p class=panswer>AjiMnHHCTpaTop: $answer</p>h;
?>
</td>
</tr>
</table>
<?
}
print '<a class=link href^addrec. ркр>Написать сообщение</А> *;
/ / Выводим ссылки на предыдущие 必 следующие сообщения
if ($start > 0) print И<А href=index.php?start=".($start-$pnumber).н>
Предаду1цие</А>";
if ($count > $start + $pnumberj print ” <A
href= index. php?start^". ($start + Spnurnber)."> След^оциес / A> ”;
?>
</tdx/trx/table>
</body>
</html>
Как видно из листинга 11.22, гостевая книга осуществляет два запроса к базе данных. Первый из них запрашивает число всех сообщений, доступных для просмотра (т. е. таких, для которых поле hide принимает значение 'show'). Второй — запрашивает $pnumber (по умолчанию 10) доступных для
просмотра записей из таблицы guest начиная с позиции start. Значения сортируются по убыванию даты добавления сообщения, в результате чего более новые сообщения всегда оказываются вверху.
Значение, возвращаемое первым SQL-запросом, извлекается при помощи функции mysqi_result () и помещается в переменную $ count, которая далее используется для вывода ссылок Следующие и Предыдущие (сообщения). Второй запрос является многострочным, и результат запроса извлекается в цикле while при помоши функции mysqi_fetch_array(), возвращающей ассоциативный массив, ключи которого соответствуют полям таблицы guest.
Каждое сообщение оформляется в виде отдельной таблицы, необязательные для заполнения поля проверяются при помощи функции empty (). Если поле не пусто (функция возвращает true)——его содержимое добавляется к сообщению.
Рис. 11.9. HTML-форма для добавления нового сообщения
За добавление нового сообщения несет ответственность файл addrec.php, содержимое которого представлено в листинге 11.23. Внешний вид HTML- формы, позволяющей добавить новое сообщение, представлен на рис. 11.9.
Листинг 11.23. Файл addrec.phpЛистинг 11.23. Файл addrec.php
Листинг 11.23. Файл addrec.php
Листинг 11.23. Файл addrec.php
// Устанавливаем соединение с базой данных include "config.php";
terror =
$action ="”;
// Возвращаем значение переменной action, переданной в URL $action 二 $_POST [ "action1* ];
// Если оно не пусто - добавляем сообщение в базу данных if {!empty($action)) {
// Проверяем сообщение на слишком длинные слова $lenmsg = strlen($msg);
$templen = 0;
$ten® = strtok($msg,"");
if (strlen($msg)>60)
while ($t已叩len < $lenmsg) {
if (strlen($terap)>60)
{
faction ="”;
$errcr н $error. н<Ы>Текст сообщения содержит слишком много символов без пробелов\пм;
break;
} else {
$templen = $texnplen + strlen ($ temp) + 1;
}.
$temp = strtok(" ”);
// Проверяем правильность ввода информации в поля формы if (trim($ POST["msg”]})
faction ="”;
terror = $error* "<Ы>Вьг не ввели сообщение\пГ|;
}
if (trim($_POST「name"])) <
faction -"";
$error = $ error. м<Ы>Вы ке ввели имя\п"; }
// При помощи регулярных выражений проверяем правильность ввода e-mail if ($_POST ("email"—
{
if (!preg_match("/[0-9a-z_]+@[0-9a-z_A\.]+\.[a-z]{2,3}/i", $email))
$action ="”;
$error = $error, °<Ы>Неверно введен e-mail.*
Введите e-mail в виде <i>something0server.com</i> \n";
}
// Обрабатываем HTML-теги и скрипты в сообщении и в информации
// об авторе, ограничиваем объем сообщения
$name = substr($_POST["namenJ/0,32);
$name = htmlspec i a1cha rs{stripslashes($name));
$city = substr($_POST[HcityM] ,0,32);
$city = htmlspecialchars(stripslashes($city));
$email = substr ($_POST [,TemailM] f 0, 32);
$email ^ htmlspecialchars(stripslashes($email));
$url = substr ($_РО8Т[ниг1иЬ 0/60);
$url = htmlspecialchars (stripslashes ($url));
$msg = substr ($__POST [ "msgH], 0,1024);
$msg = htmlspecialchars (stripslashes ($msg));
// Добавляем протокол в urlf если пользователь заОьш это сделать сам $и工上=strtr($nrl, "HTPF", "htpf”};
if (trim($url) !=,,H)
{
if (strtolower((substr($url, 0, 7))!=Hhttp://") &&
(strtolower(substr($urlf 0, 7)) !=”ftp://”)} $url="http://lf.$url;
if (empty(terror))
{
$msg = nl2br ($msg);
/ / Обраба'гыва ем встроенные теги
”<u>", $msg);**<u>n r $rnsg);"vi>”, $msg);"<i>”, $msg);“<B>”, $msg);”<B>”, $msg);”<u>", $msg);**<u>n r $rnsg);"vi>”, $msg);"<i>”, $msg);“<B>”, $msg);”<B>”, $msg);$msg = str_replace(M[sub]$msg = str_replace(M[sub],Y5UB>”,,Y5UB>”,$msg);$msg);$msg = str_replace (ri [SUB] '\ $msg = str_replace("[sup]", $msg = strreplacef"[SUP]"r$msg = str_replace (ri [SUB] '\ $msg = str_replace("[sup]", $msg = strreplacef"[SUP]"r"<SUB>“, M<SUP>", "<SUP>“,"<SUB>“, M<SUP>", "<SUP>“,$msg);$msg);$msg);$msg);$msg);$msg);$msg = str_replace(H[/u]M, $msg = str_replaceC[/U]M, $msg = str_replace (H [/i] **,$msg = str_replace(H[/u]M, $msg = str_replaceC[/U]M, $msg = str_replace (H [/i] **,"</u>", $msg);"</u>\ $msg);"</i>\ $msg);"</u>", $msg);"</u>\ $msg);"</i>\ $msg);$rasg = str_replace("[/I]n, $msg = str_replace (rr [/b] r\ $msg = str_replaae{"[/B]",$rasg = str_replace("[/I]n, $msg = str_replace (rr [/b] r\ $msg = str_replaae{"[/B]",lt</i>", $msg); "</B>\ $msg); ■У/В>”, $msg);lt</i>", $msg); "</B>\ $msg); ■У/В>”, $msg);$msg 二 st r_replace (h [ /SUB]11, "</SUB>“* $msg);$msg 二 st r_replace (h [ /SUB]11, "</SUB>“* $msg);$msg = str replace (11 [/sub] и, H</SUB>", $msg);$msg = str_replace{u[/SUP]", "</SUP>", $msg);$msg = str_replace("[/sup]", "</SUP>”, $msg);$msg = eregi_replace(*• (. *) \\ [url\\] (. *) \\ [/url\\] (, *) n,”\\1<а Ьге^\\2>\\2</а>\\3*\$msg = str replace (11 [/sub] и, H</SUB>", $msg);$msg = str_replace{u[/SUP]", "</SUP>", $msg);$msg = str_replace("[/sup]", "</SUP>”, $msg);$msg = eregi_replace(*• (. *) \\ [url\\] (. *) \\ [/url\\] (, *) n,”\\1<а Ьге^\\2>\\2</а>\\3*\§msg);$msg = str^replace(,f\n*\ ” **, $msg);帛msg 二 str_replace ("\r11, ” ”,$msg);// Запрос к базе данных на добавление сообщения$query = “INSERT INTO guest VALUES (0,* $name1,§msg);$msg = str^replace(,f\n*\ ” **, $msg);帛msg 二 str_replace ("\r11, ” ”,$msg);// Запрос к базе данных на добавление сообщения$query = “INSERT INTO guest VALUES (0,* $name1,*$city'#,串email I* $urlf,1 $msg1,*$city'#,串email I* $urlf,1 $msg1,NOW(),,show,);”;NOW(),,show,);”;16 Зак. MS I16 Зак. MS ISnisg = str_replace (" [u]", $msg = str_replace(H[U] *4 $msg = str^replace(”[i]”, $msg = str replace (° [IJr,f $rosg ^ str_rapla8 (" [b] ”, $msg ^ str_replace (M [B] r\
”<u>", $msg);
**<u>n r $rnsg);
"vi>”, $msg);
"<i>”, $msg);
“<B>”, $msg);
”<B>”, $msg);
”<u>", $msg);
**<u>n r $rnsg);
"vi>”, $msg);
"<i>”, $msg);
“<B>”, $msg);
”<B>”, $msg);
$msg = str_replace(M[sub]
$msg = str_replace(M[sub]
,Y5UB>”,
,Y5UB>”,
$msg);
$msg);
$msg = str_replace (ri [SUB] '\ $msg = str_replace("[sup]", $msg = strreplacef"[SUP]"r
$msg = str_replace (ri [SUB] '\ $msg = str_replace("[sup]", $msg = strreplacef"[SUP]"r
"<SUB>“, M<SUP>", "<SUP>“,
"<SUB>“, M<SUP>", "<SUP>“,
$msg);
$msg);
$msg);
$msg);
$msg);
$msg);
$msg = str_replace(H[/u]M, $msg = str_replaceC[/U]M, $msg = str_replace (H [/i] **,
$msg = str_replace(H[/u]M, $msg = str_replaceC[/U]M, $msg = str_replace (H [/i] **,
"</u>", $msg);
"</u>\ $msg);
"</i>\ $msg);
"</u>", $msg);
"</u>\ $msg);
"</i>\ $msg);
$rasg = str_replace("[/I]n, $msg = str_replace (rr [/b] r\ $msg = str_replaae{"[/B]",
$rasg = str_replace("[/I]n, $msg = str_replace (rr [/b] r\ $msg = str_replaae{"[/B]",
lt</i>", $msg); "</B>\ $msg); ■У/В>”, $msg);
lt</i>", $msg); "</B>\ $msg); ■У/В>”, $msg);
$msg 二 st r_replace (h [ /SUB]11, "</SUB>“* $msg);
$msg 二 st r_replace (h [ /SUB]11, "</SUB>“* $msg);
$msg = str replace (11 [/sub] и, H</SUB>", $msg);
$msg = str_replace{u[/SUP]", "</SUP>", $msg);
$msg = str_replace("[/sup]", "</SUP>”, $msg);
$msg = eregi_replace(*• (. *) \\ [url\\] (. *) \\ [/url\\] (, *) n,
”\\1<а Ьге^\\2>\\2</а>\\3*\
$msg = str replace (11 [/sub] и, H</SUB>", $msg);
$msg = str_replace{u[/SUP]", "</SUP>", $msg);
$msg = str_replace("[/sup]", "</SUP>”, $msg);
$msg = eregi_replace(*• (. *) \\ [url\\] (. *) \\ [/url\\] (, *) n,
”\\1<а Ьге^\\2>\\2</а>\\3*\
§msg);
$msg = str^replace(,f\n*\ ” **, $msg);
帛msg 二 str_replace ("\r11, ” ”,$msg);
// Запрос к базе данных на добавление сообщения
$query = “INSERT INTO guest VALUES (0,
* $name1,
§msg);
$msg = str^replace(,f\n*\ ” **, $msg);
帛msg 二 str_replace ("\r11, ” ”,$msg);
// Запрос к базе данных на добавление сообщения
$query = “INSERT INTO guest VALUES (0,
* $name1,
*$city'#
,串email I
* $urlf,
1 $msg1,
*$city'#
,串email I
* $urlf,
1 $msg1,
NOW(),
,show,);”;
NOW(),
,show,);”;
16 Зак. MS I
16 Зак. MS I
if (mysql_query ($query))
{
// Если в конфигурационном файле
// $sendmil = true, отправляем уведомление
if($sendmail)
$thm = 11 guestbook - a new post";
$msg == "post: $msg\nname: $name**;
mail ($valmail, $thm, $msg);
}
// Возвращаемся на главную страницу, если все прошло удачно
print ”<HTMLXHEAD>\n”;
print "<МЕТА HTTP-EQUIV=1 Refresh» CONTEND10; URL= index.php1 >\nr,;
print H</HEADX/HTML>\nM;
exit。;
}
else
{
// Выводам сообщение об ошибке в случае неудачи
echo "<а href='index.php'>Вернуться</а>н;
echo(П<Р> Ошибка при добавлении сообщения</Р>");
echo £Н<Р> $query</P>H);
exit();
if (empty($action)) {
?>
<html>
<head>
<meta http-equiv=MContent-TypeH content^"text/html; charset^indows- 1251">
<titlex/title>
<link rel=t*StyleSheetTt type=,,text/cssH href=Hguestbook.css">
</head>
<body bottommargin="0n marginheight=HO** marg^nwidth^,,0" rightmargin=MOM leftmatgin=”O" topmargin=H0">
<table border=l,On cellspacing= **0° width="100%" cellpadding= **Ом>
<tr>
<td colspan=”3" height=1135H><p class-Г|рсо1ог1'*><поЬг><Ь>Гостевая KHwra</nobr></bx/p></td>
<td width=H100%" colspan=M2"><nop></td>
</tr>
<tr alignstr cent er n>
<td widths1'150r, colspan=n2M><nopx/td>
<td height="4" bgco 1 оr =r,#EAEAEAr,><nop></1d>
<td bgcalor= " silve r r,Xnop>< /1d>
<td bgcolor=,,grayt,xnop></td>
</tr>
</table>
<table width=M100%n>
<tr align=MrightM> <td>
<a class₽link href="index.phpn title=nBepHyTbCH в гостевую книгум>гостевая книга</а>&пЬзр;
<а class^link href="http://ww.softtime.ruM Ъ1Ё1е="Вернуться на сайт">главная</а>
</td>
<td width="10%fl> </td>
</tr>
</table>
<form action=addrec»php method=post>
<input type=hidden name=action value=post>
Имя * : <input type=text name=name value=f<? echo $name; ?>‘><br> Город : <input type^text name=city value='<? echo 串city; ?> *><br> E-mail : <input type=text name-email valuer*<? echo $email; ?>*><br> URL : <input type=text size=40 name=url valuAY? echo $url; ?>1 ><br> Сообщение * : <textarea cols=42 rows=5 name=msg><? echo $msg; ?> </textarea><br>
<input type=°submit'* valuer*1 Добавить">
</form> </body> </html> <?php // Выводим сообщение об ошибке if (!®npty($error))
print Tf<P>Bo время добавления записи произошли следующие ошибки:</Р>11;
print "<UL>\n"; print $error; print 1f</QL>\n";
} } ?>
Как видно из листинга 11.23, файл addiec,php начинается с обработчика расположенной ниже HTML-формы.
После заполнения формы посетителем информация из полей формы отправляется этому же скрипту. Так как переменная faction в этом случае уже не пуста, начинается этап обработки сообщения и размещения его в базе данных гостевой книги. В первую очередь проводится проверка текста сообщения $msg на наличие слишком длинных слов, которые могут нарушить дизайн. Обычно это либо длинное слово, введенное ради "шутки”,либо длинный URL ресурса. В нашем случае длинным считается слово, число символов в котором превышает 60.
Далее при помощи строковой функции trim() удаляются предваряющие и конечные пробельные символы, так как они не несут смысловой нагрузки. При помощи функции empty проверяется, не забыл ли пользователь ввести данные в обязательные для заполнения поля: имя $паше, текст сообщения 帛msg. Кроме того, поле e-mail проверяется на соответствие регулярным выражением 'Ч [0^9a-z_]+@[0-9a-z_A\,]+\.[a-z](2,3}/in, подробно рассмотренным в 乙lose 4. Для того чтобы исключить возможность применения посетителем HTML-тегов, обрабатываем все поля при помоши функции htmispeciaichars(). После этого любые теги будут просто отображены на странице, не интерпретируясь браузером. Функция substr() ограничивает длину полей*
После этого, если при заполнении формы не было допущено ни одной ошибки, проводим окончательные приготовления перед занесением сообщения в базу данных: при помощи функции nl2br() заменяем символы переноса строки на тег <br> для корректного отображения сообщения в окне браузера. Встроенные теги, применяемые для форматирования текста, заменяем на HTML-теги.
После проверки правильности ввода сообщения и обработки текста для корректного отображения формируем строку запроса $ query к базе данных для добавления нового сообщения. Первичный ключ id_msg имеет значение 0 — база данных самостоятельно присвоит ему уникальное значение. Если запрос выполнился успешно, проверяется значение переменной
$sendamii, определенной в конфигурационном файле config.php. Если данная переменная принимает значение true, при помощи функции mail {)по адресу $valmail отправляется уведомляющее почтовое сообщение. После чего осуществляется перенаправление на главную страницу гостевой книги.
Завершает файл форма отправки сообщения, содержащая поля для ввода текста сообщения и дополнительной информации, а также скрытое поле action, нужное для сообщения скрипту о необходимости добавления нового сообщения.
На этом разработка блока представления гостевой книги завершена —— далее будет рассмотрена система админ истри рования, которая состоит из 6 файлов, помещенных в отдельную директорию (admin):
口 delpost.php ——скрипт удаления сообщения из базы данных;
口 editcommentfbrm.php ——HTML-форма для редактирования сообщения и ответа администратора;
口 editcomment.php — обработчик формы editcom me nt form. php;
口 hide.php ——скрипт сокрытия сообщения;
口 index.php ——главная страница алм и ни стри рова н и я;
口 show.php — скрипт отображения сообщения.
За отображение главной страницы адм и н истрирова н ия несет ответственность файл index.php, содержимое которого представлено в листинге 11.24. Главная страница системы администрирования показана на рис. 1IJ0.
:Листинг 11,24. Файл index.php
<?php
// Устанавлива ем соединение с базой данных
include "../config.php”;
// Воспроизводим гостевую книгу таким образам, как она выглядит на
// главной странице, но отображаем также невидимые сообщения
// Стартовая тоика
$start = $_GET["start”];
if (empty($start)) $start = 0;
if ($start < 0) $start = 0;
// Запрашиваем общее число сообщений
$tot = mysql_query ("SELECT count {*) FROM guest11);
// Запрашиваем сами сообщения
$query = "SELECT + FROM guest
ORDER BY puttime DESC
LIMIT $startr $pnun±>er”;
$gst = mysql_query($query);
if (!$gst I I !$tot) puterror(мОшибка при обращении к гостевой книге"); // При помощи цикла выбираем из базы данных 〃 сообщения
while ($guest = mysql__fetch_array ($gstj) {
// Если сообщение отмечено как невидимое (hide - 1 hide *),
// выводим ссылку ^отобразить'*, если как видимое (hide = 'show1),
// выводим ссыпку "скрыть"
iff!$guest[1hide1] = ’show') $showhide = "<a
href=hide. php?id_jnsg=,f. $guest [ * id_msg1 ] » "&start^$start> Скрыть сообщение</а>";
else
$showhide 工 ”va
href=s how * php ? id_msg=n,$guest[* id^nsg *]."&start=$start> Отобразить сообщение</а>";
// Выводим таблицу с сообщением
?>
<Р>
<table>
<tr>
<td>ABTop coo6ineHHH</td>
<td»〃aTa отправки< /td>
<t с!»Город</td>
<td»E-mai l</td>
<td»Url</td>
</tr>
<?
echo ”vtrxtd>".$guest「name1 ]. ”</td>";
echo "<td>"・ $guest [『put time,] . r*</td>";
echo H<td>r\$guest [ ^ity* ] . "</td>";
echo ”<td>"•$guest「email1].n</td>w;
echo "<td>f,. $guest [ ^rlr ]. ,,</tdx/tr>H;
echo IT<tr><td>Coo6meHMe: < /td>
<td colspans5>M .$guest [ *msg, ] . '^/tdx/t^11;
echo M<tr><td>AwwHMC,rpaTop: </td>
<td colspan=5><p>*r. $guest [1 answer * ], ,,</tdx/tr>n;
echo "</table>ri;
〃 Ссыпка на редактирование и ответ echo ”<а
href=editcoin[nentform.php?id_jnsg=".$guest [1 id_msg* ] . "&start==$start> Редактировать </a>f,;
// Ссылка на правку сообщений echo $showhide;
// Ссылка на удаление сообщений
echo "<а href==delpost *php?idjnsg=M.$guest[1id_msg *].Mfistart=$start> Удалить сообщение</а>н;
}
// Выводим ссылки на "пр曰дыдущие" и мследующие** сообщения
$count = mysql_result($tot);
if (Sstart > 0) print ”<A href^index*php?start=H.($start - $pnumber) . н предыдущие сообщения</А> '•;
if ($count > $start + $pnumber) print " <p><A
href»index.php?start=" . {$start + $рпипйэег) . ">Следую1Ш4е co общения < /А> \n";
?>
Рис. 11.10. Главная страница системы администрирования гостевой книги
После соединения с базой данных у базы запрашиваются сообщения и их количество. Сообщения выводятся в виде таблицы по $рпшпЬег штук (по умолчанию 10). Отличие от главной страницы гостевой книги состоит в том, что выводятся все сообщения: и скрытые, и отображаемые. Для каждого сообщения в зависимости от того скрыто оно или нет, выводим ссылки, позволяющие скрыть сообщение (ссылка на файл hide.php) или отобразить ранее скрытое сообщение (ссылка на файл show.php). Со строкой запроса в этих файлах передается номер $start, начиная с которого происходит вывод сообшений для корректного возврата на страницу администрирования, и первичный ключ id_msg скрываемого или отображаемого сообщения. Каждому сообщению также соответствует ссылка на скрипт del post, php, который осуществляет удаление сообщения из базы данных, и на editconimentfbrm.php, предназначенный для добавления комментариев администратора. Параметры запроса для этих ссылок аналогичны вышерассмотренным. В конце скрипта выводятся ссылки на следующие и предыдущие сообщения.
Удаление сообщений нз гостевой книги осуществляется при помощи скрипта delpost-php, содержимое которого представлено в листинге 11.25. Скрипт принимает два параметра: id_msg — значение первичного ключа сообщения, которое необходимо удалить, и start — необходимое для корректного возврата на страницу администрирования.
? Листинг 11,25. Файл delpostphp
<?php
// Устанавливаем соединение с базой данных include "•・/config・ php";
// Нормируем SQL-запрос
$query = "DELETE FROM guest
WHERE id亠g=" * S_GET [nid_msgH ];
// Удаляем сообщение с первичным ключом Sid^jnag if (mysql_query ($query))
{
// После удачного удаления сообщения переходим
//к дальнейшему администрированию гостевой книги
print °<HTML><HEAD>\nri;
print "СМЕТА HTTP-EQU工V=‘Refresh'
CONTENTh'O; URL=index.php?start=H.$_GET[rtstartH) J‘>\n"; print n</HEADX/HTML>\nM;
〃 В случае неудачи выводим сообщение об ошибке
else puterror ("Ошибка при обращении к гостевой книге11);
В файле delpost.php (см. листинг 11.25) во временной переменной $query формируется SQL-запрос delete на удаление сообщения с первичным ключом $id_msg, переданным в строке запроса. Переменная передается в качестве аргумента функции mysqi_query(), и в случае успешного выполнения запроса осуществляется перенаправление на главную страницу администрирования: index.php?start=Sstart.
:Замечание )
При перенаправлении строка запроса передает параметр start, необходимый для корректного возврата на страницу администрирования. Если этот параметр не передавать, то возврат будет всегда осуществляться на страницу, содержащую первые 10 сообщений.
Аналогично файлу delpost.php выглядят файлы hide.php л show.php. В листинге 11.26 приведен код, содержащийся в файле hide.php.
:Листинг 11.26. Файл hide.php
<?php
// Устанавливаем соединение с базой данных include "../config.php";
// Формируем SQL-запрос
$query = "UPDATE guest SET hide = 'hide'
WHERE id亠g = ” ・$_GET【”id_msg”];
// Скрываем сообщение с первичным ключом $id_jnsg без удаления
// его из базы данных
if (mysql_query ($query))
(
// После удачного удаления сообщения переходам к
// дальнейшему администрированию гостевой книги
print "<HTMLXHEAD>\nn;
print УМЕТА HTTP-EQUIV=1Refresh1
CONTENT='0; URL=index・ php?start=".$_GET["start"J.,f'>\n°;
print rt</HEAD></HTML>\n";
// В случае неудачи выводим сообщение об ошибке
else puterror("Ошибка при обращении к гостевой книгем);
Сокрытие сообщения происходит, если полю hide сообщения с первичным ключом $id_msg присваивается значение 'hide: При запросе из таблицы сообщений на главной странице гостевой книги (см. листинг 11.22) такие сообщения игнорируются, так как одним из условий их извлечения является равенство этого поля 'show'. Код файла show.php аналогичен, за исключением SQL-оператора update, который в этом случае выглядит следующим образом (листинг 11.27).
号.,…Sf
Листинг 11.27. Оператор update
$query = "UPDATE guest SET hide = 'show'
WHERE id』sg = ".$__GET[”id_msg”];
Для редактирования сообщений, а также добавления ответа администратора, предназначена HTML-форма editcommentfbrm.php, содержимое которой представлено в листинге IL28, Скрипт в файле editcomnientfonn.php принимает два параметра: id^nsg ——первичный ключ редактируемого сообщения и strat, необходимый для корректного возврата на главную страницу администрирования.
・・_%9甘・,.y^»i・ 丁,:!^,* шз*н ”■■!,"•”....,?,・л ♦,•£?...,,,[“ и、:/дд^№уи,.,*, а,.”, ♦.”.・_^>,.・ ”fu・q・ ”,,.,””“,.、•涉•・.".•.•■;.,,■"•,:.,:„уе, ]:,3找^^^^^二,}4 y去•■• :Листинг 11.28. Файл edKcommentform.php
轟• *•<«¥■ А >< ;%”}“, ;“”Е%“》Hlft«7»'H LI■/ 掲“th", f “:;”;;川\戈r*\*»** 才 3;fti%i¥ftT»&*W iitfiVtiiitHiintr^nif^^rttt VfHTi**4T
<?php
// Устанавливаем соединение с базой данных include "…/config.php";
// Извлекаем параметр $idjnsg из строки запроса
$id_msg - $_GET[*id_msg *];
$start = $_GET[fstart 4;
// Запрос к базе данных для извлечения сообщения с
// первичным ключом $id__msg
$query = "SELECT * FROM guest
WHERE idjnsg = $id」nsg";
$gst = mysql_query($query);
if (9gst)
// Преобразуем полученную информацию в ассоциативный массив
$guest = mysql_fetch_array($gst>;
}
// В случае неудачи выводам сообщение об ошибке
else puterror(^Ошибка при обращении к гостевой книге");
include ”topadmin. php**;
?>
<a href=tl index *рЬр°>Вернуться в администрирование гостевой книги</а><Ьг>
<form action^editcoiranent.php method=post>
<textarea class^input cols=42 rows=5 name=msg><? echo $guest[ *iusg1]; ?> </textarea><br>
<textarea class-input cols=42 rows=5 name=answer>
<? echo $guest[* answer1]; ?>
</textarea><br>
<input class声button type=submit value=MJ4cnpaBMTb rt>
<input type=hidden name=id二msg value^<?php echo $id_msg; ?»
<input type=hidden name=start value=<?php echo $start; ?» </form>
Перед отображением HTML-формы из таблицы guest извлекается запись, соответствующая первичному ключу id_msg, значения полей которой подставляются в value-значения элементов управления формы, HTML-форма содержит два текстовых поля: msg — для редактирования сообщения посетителя и answer — для ответа администратора. Помимо этого, форма содержит два скрытых поля для отправки значений параметров id_msg и start в обработчик формы, расположенный в файле editcomment.php, содержимое которого представлено в листинге 11.29.
;,,^»*V*,^*l^y»,*,,v.*w'^'",",'*':"*''''*^yi"'.!,*********',*-t**V*<*W*-,-,^-,,^*,*-,**!,*,***'*,**,.,*7yj'^,,**?!*-'*-,,,,-v,*''•••••* V****'•■*•*?•**• Ч;.?^',**','!Ч^***,*Д?Г1
;Листинг 11*29. Файл editcomment.php
為««>« «я «k■ ■ ■ 4«««4d««*«Г>-^*«+'+*ч"«ок・**»♦',*:,;:...,;;;;,*;••♦;;“,•• ЛГ#'1Т^Г^Л?■"!•■ "Лo*r> Av*«・«>V'»«r7>WArt*+«*;;• ;**,;'• ;;•・id;,,.,.\*^4*ok^Jj#>ieV#i■ e'»'»■ V ;•'♦%**■/;**'』* ■、•#♦%“*、中日
<?php
// Устанавливаем соединение с базой данных
include ”../config.php";
// Добавляем ответ модератора на сообщение с первичным ключом $1д^^пзд $query = "UPDATE guest SET answer = '”.$_POST["answer"]J',
msg = '".S—POSTrmsg"]・"'
WHERE id_ms驴”.$_POST【"idjnsg"];
if(mysql query($query))
// После удачного добавления переходим к
// дальнейшему адьдинистрированию гостевой книги
print ,,<HTML><HEAD>\nt,;
print "<МЕТА HTTP^EQUIV= 1 Refresh *
CONTENT=*0; URL=index.php?start=rt.$ POST[nstart"]. ° Xn"; print rr</HEADX/HTML>\nn;
}
// В случае неудачи выводим сообщение об ошибке
else puterror("Сйиибка при обращении к гостевой книге"); ?>
Как видно из листинга 11.29, для записи с первичным ключом, переданным через параметр id msg, обновляются значения полей answer (ответ администратора) и msg (сообщение посетателя), за счет выполнения SQL-оператора UPDATE.
Глава 12
Фотогалерея
Web-приложение "Фотогалерея*' предоставляет удобный инструментарий для публикации изображений на сайте. Он позволяет распределять фотографии по группам, сортировать их в нужном порядке, добавлять к каждой фотографии название и описание. Применение библиотеки GDLib позволяет автоматически создавать уменьшенные копии изображений.
: Заменанме )
Данное приложение работает только с файлами в формате JPEG. Это связано с тем, что из библиотеки GDLib 2 исключена возможность создания изображений в формате GIF.
(Замечание )
Формат PNG в данном приложении также не поддерживается. Однако работа с ним отличается от работы с форматом JPEG только именами функций GDLfb, и поддержку данного формата нетрудно добавить, немного модифицировав разрабатываемый код.
Описание фотогалереи
При первом обращении к Web-приложению иФотогалерея", на странице отображается таблица с группами фотографий, доступными для просмотра, а также число фотографий в каждой из групп (рис. 12.1),
Название каждой группы в таблице является гиперссылкой, переход по которой открывает группу фотографий для просмотра (рис. 12.2). На странице просмотра фотографии располагаются в таблице по пять фотографий в строке. В каждой ячейке таблицы размешаются название и уменьшенная копия фотографии, щелчок по которой открывает в новом окне фотографию большого размера.
Рис. 12.1. Главная страница Web-приложения "Фотогалерея"
Рис. 12*2. Страница просмотра фотографий
Система администрирования
При первом обращении к странице администрирования Web-приложения загружается страница управления группами фотографий (рис. 12.3).
Рис. 12.3. Система администрирования Web-приложения "Фотогалерее”
На странице администрирования выводится таблица, каждая строка которой соответствует отдельной группе фотографий. Таблица содержит пять столбцов, в первом из которых располагается название группы, во втором — позиция относительно других групп фотографий, в трех последних столбцах выводятся управляющие ссылки: Скрыть/Отобразить, Исправить и Удалить, позволяющие временно скрывать (на это время она недоступна для просмотра с сайта), осуществлять редактирование и удалять группы фотографий.
Переход по гиперссылке Добавить новую группу фотографий открывает форму, изображенную на рис. 12.4.
Форма имеет два текстовых поля для названия группы и позиции относительно других групп, а также флажок Отображать, позволяющий задать режим отображения группы фотографий на сайте. Переход по гиперссылке Исправить (см. рис.【2*3) открывает аналогичную форму для редактирования группы. Отличие заключается в том, что в режиме редактирования все поля формы уже заполнены.
Рис. 12.4. Добавление новой группы фотографий
Рис. 12.5. Редактирование фотографий в группе
Названия групп фотографий в таблице, приведенной на рис. 12.3, являются гиперссылками, переход по которым открывает страницу редактирования фотографий в группе (рис. 12.5).
На странице редактирования группы фотографий выводится таблица (см. рис. 12.5), каждая строка которой содержит по три ячейки. Ячейка таблицы соответствует одной из фотографлй н содержит название фотографии, уменьшенное изображение (являющееся ссылкой на увеличенное изображение), позицию относительно других фотографий в группе и три управляющие ссылки: Скрыть, Исправить и Удалить.
Гиперссылка Добавить фотографию открывает форму, приведенную на рис. 126
Рис. 12.6. Добавление новой фотографии в группу
Форма позволяет задать название, позицию фотофафии относительно остальных фотографий в группе, а также загрузить фотографию при помощи поля типа File. Как и форма для добавления группы фотографий (см. рис. 124), форма для добавления фотографий содержит флажок, позволяющий задать режим отображения фотографии на сайте. При установленном флажке фотография доступна для просмотра с сайта посетителем, при снятом —— фотография доступна только на страницах администрирования.
Разработка фотогалереи
Традиционно начнем разработку Web-приложения со структуры базы данных и системы ад ми н и стри рован ия. Как и в блоке н Контакты*', для создания групп фотографий будем использовать систему подкаталогов, разработанную в главе & Для этого понадобится таблица catalog (см. листинг 8.1) а также скрипты и HTML-формы управления каталогами addcat.php, addcatfbrm.php, delcat.php, editcat.php, editcatfbrm.php, hidecat.php и showcat.php, подробно описанные в главе £
; Замечание )
Таблица catalog в данном Web-приложении будет переименована в таблицу photocat.
При построении системы будет принят следующий подход. Текстовая информация, такая как название фотографии, ее описание, принадлежность к группе фотографий, будет храниться в таблице photo базы данных MySQL Графическая информация, такая как фотография и ее уменьшенная копия, будет храниться в директории files Web-приложения "Фотогалерея".
[ Замечание )
СУБД MySQL допускает хранение графической информации в полях типа blob. Однако MySQL проектировалась как очень быстрая база данных для работы с текстовой информацией, расположенной в сравнительно небольших базах данных. и при достижении большого размера базы данных ведут себя неустойчиво.
Таблица photo состоит мз 7 полей:
id_photo — первичный ключ таблицы, имеющий атрибут auto_ INCREMENT;
口 name — название фотографии;
口 small — путь к уменьшенной версии фотографии;
Ыд — путь к фотографии большого размера;
hide ——поле типа enum, принимающее 2 значения: 'show、если фотография доступна, и 'hide', если фотография недоступна для просмотра посетителем;
П pos — позиция фотографии относительно остальных фотографий в группе;
口 id_catalog — первичный ключ записи в таблице photocat, соответствующей группе, к которой принадлежит данная фотография.
SQL-оператор create, создающий таблицу photo, представлен в листинге 12.1.
ГлЕн^яШздан^аблицы^ **Г^,,,・ п в «чг,* 1ч ■ft*' ^ * ±,*”“»• ■vtjrjVw^Ieее .."..、**.*.,《“*"*“・*.*“*t.f,,*.tl*・..**h;.t.t・,ii* ・%**“,*,*.*』.♦*■
CREATE TABLE photo (
id^photo INT(ll) NOT NULL AUTO_INCREMENT, name TINYTEXT NOT NULL, small TINYTEXT NOT NULL, big TINYTEXT NOT NULL, hide ENUM「show*,'hide*) NOT NULL DEFAULT 'show', pos INT(11) NOT NULL DEFAULT 『0', id__catalog INT(ll) NOT NULL DEFAULT '□', PRIMARY KEY (id_photo) )TYPE=MyISAM;
Помимо перечисленных выше файлов для работы с группами фотографий, для построения системы администрирования понадобятся 10 следующих файлов:
口 addphotoform.php —— HTML-форма добавления новой фотографии;
口 addphoto.php — обработчик формы addphotofbrm.php;
delphoto.php ——скрипт удаления фотографии из системы;
editphotofbrm.php — HTML-форма редактирования информации по фотографии, а также загрузки новой фотографии вместо старой;
П editphoto.php ——обработчик формы editphotoform.php;
口 hidephoto.php — скрипт, ответственный за сокрытие фотографии;
口 index.php — главная страница системы администрирования;
photoes.php — вспомогательная страница, отображающая таблицу с содержимым группы фотографий;
О showphoto.php — скрипт, ответственный за отображение фотографии;
口 util.php —— файл со вспомогательными функциями.
Кроме того, для установки соединения с базой данных, как и в предыдущих Web-приложениях, будет использоваться файл config.php (см. листинг 7.5), расположенный на уровень выше директории с системой администрирования.
[ Замечание )
Далее в листингах для удобства восприятия будет опущен несущественный HTML-код, связанный, в первую очередь, со стилевым оформлением Web- приложения. Полная версия данного Web-приложения находится на прилагаемом к книге компакт-диске, а также доступна по адресу http://www.softtrme.ru/ Info/downloads .php.
Главная страница адм и н и стрирован ня index.php (листинг 12.2) предназначе
на для навигации по группам фотографий (см. рис. 12.3).
i Листинг 122 Файл Index.phpi Листинг 122 Файл Index.php
i Листинг 122 Файл Index.php
i Листинг 122 Файл Index.php
<?php
// Устанавливаем соединение с базой данных require_once (°../config.php");
/ / Извлекаем из строки запроса параметр id_j>arent
// id_parent — идентификатор группы фотографий
$id_parent = $_GET (1 id__parent1);
if (eiipty ($id_parent)) $id_parent = 0;
?>
<table>
<tr><td>
<?php
// Если текущий каталог не является корневым, // выводим ссылку дая возврата в предыдущее меню if ($id_parent != 0)
echo "<а href^index.php?id_parent=O>BepxHMir уровень</а><Ьг>,';
// Если каталог является корневым - выводим ссылку
// добавления нового подкаталога else echo "<а href=addcatform.php?
id—pa rent=$ id_pa rent> Добавить группу фотографий</а>г,;
// Формируем и выполняем SQL-запрос, извлекающий
// список групп фотографий
$query = "SELECT * FROM photocat WHERE i djpa rent==$ id_parent ORDER BY pos”;
$ctg = mysql_query($query);
if (!$ctg) puterror("Ошибка при обращении к Фотогалерееn);
// Если в таблице photocat присутствует хотя бы одна
// группа фотографий - выводам их в таблице
if (mysql_num_rows ($ctg) >0)
// Выводам заголовок таблицы групп фотографий echo ,,<table><tr>
<td>HasBaHHe группы фотографий</1d>
<td>no3,</td>
<td colspan=3>Действия</td>
</tr>n;
while($cat = mysql_fetch_array($ctg))
〃 Выясняем, скрыт каталог или нет,
//ив зависимости от этого формируем управляющую ссылку // дая скрытия или отображения группы фотографий if($cat[,hide,] = "hide") {
$strhide = "<a href=showcat.php?
id_catalog=**. $cat [1 id_catalog1 ]. "& id_parent=$id_pa rent>0тобразить</а> ”;
}
else
{
$strhide ^ "<a href=hidecat.php?
id__catalog=". $cat [ fid_catalog* ] . n& id_parent-$id_parent>Скрыть</a>";
}
// Выводим список групп фотографий
echo *T<tr>
<tdxa href=index. php?
id_parent=**. $cat [' ic^catalog1 ] . H>
11 ・ Scat「name* ] * **
</a>
</td>
<td>'1. $cat [ *pos , ]. "</td>
<td>$strhide</td>
<tdxa href^editcatform. php?
id_catalog=".$cat[•id_catalog']Ф"&
id_parent=$id__parent>HcnpaBMTb</a></td>
<td><a href=delcat.php?
id_catalog=fl. Scat (* id_catalogf ] . "& id_paren^$ id_parent>V4ajiMTb< /a>< /td> </tr>1';
} // Конец цикла while echo ”</table〉”;
} // Конец if(mysql num rows($ctg)>0)
?>
</tdx/tr>
<tr><td>
<?php
// Выводим содержимое групп фотографий, если
// текущий каталог не является корневым
if ($id_j>arent != 0) include "photoes.php";
?>
</tdx/tr></table>
Скрипт, расположенный в файле index.php. принимает единственный параметр id_parent. Если данный параметр принимает нулевое значение или отсутствует вообще, скрипт выводит содержимое корневой группы, а также ссылку для добавления новых подгрупп. Если данный параметр принимает значение первичного ключа одной из групп, выводится содержимое данной группы. При этом возможность добавления подгрупп имеется только в корневой группе, так как в данном Web-приложен и и реализуется модель одноуровневого каталога.
Подгруппы корневой группы выводятся в виде таблицы, где каждая строка соответствует одной из подгрупп. Помимо названия подгруппы и ее позиции в строке присутствуют три управляющие ссылки для скрытия/отобра• жения, редактирования и удаления подгруппы.
В конце файла index.php, в случае, если текущей группой не является корневая группа —— включается файл photoes.php (листинг 12.3), который несет ответственность за вывод информации по фотографиям, принадлежащим данной группе.
i Листинг 12.3. Файл photoes.phpi Листинг 12.3. Файл photoes.php7.*1 “<;***♦*«7.*1 “<;***♦*«Ж™3:Ж™3:
i Листинг 12.3. Файл photoes.php
i Листинг 12.3. Файл photoes.php
7.*1 “<;***♦*«
7.*1 “<;***♦*«
Ж™3:
Ж™3:
<a classs,,menun href^addphotoform.php?id_catalog₽<? echo $id_j}arent ?» Добавить фотографию
</a>
<?php
// Выбираем из базы данных фотографии
$query = "SELECT * FROM photo
WHERE id—Catalcg = $id_parent
ORDER BY pos";
$prt = mysql_query ($query);
if(!$prt) puterror(^Ошибка при обращении к блоку Фотогалерея");
// Если в текущей группе фотографий имеется хотя бы одна // фотография, формируем таблицу с фотографиями
if (mysql_num_rows ($prt) > 0)
echo M<table>u;
// Вспомога тель на я переменная для вывода // фотографий по 3 штуки в строке таблицы.
// Каждая фотография выводится в своей ячейке таблицы $td == 0;
// Формируем таблицу с фотографиями while($par = mysql_fetch_array($prt)) {
// Выясняем, скрыта фотография или нет
// и э зависимости от этого формируем управляющую ссылку
// Скрыть/Отобразить if($par[*hide*] == "hide”) (
$showhide = "<а href=showphoto.php? id_photo=H.$par[,id_photo*]* M& id_catalog=$id_p已 rent>OTобразить</а>и;
}
else•
$ showhide = **<a href=hidephoto. php? id_photo=” ・ $par[1id_phcto1].n&
i d_ca ta1og=$ id_pa гепЪ>Скрыть</а>”; }
// Формируем переменную $image, отвечающую за вывод
// уменьшенной копии изображения, которая является ссылкой //на фотографию большого размера
if(lenity($раг[1 small1]) &&
$par[fsmall1]l«n»M &&
file_exists (L /. $par [' small'])) {
$ image = "<a href='../". $pmr「big' ] « ”' target===_blank>
<img src=. ./'*.$par[1 small' ] .n>
</»
else $small = "Нет";
// Если значение временной переменной td равно О, // выводим тег начала строки таблицы <tr> if ($td = 0) echo "<”>";
// Вывода фотографию и управляющие ссылки к ней echo ,r<tdxtable border=0 width=100%xtr align-center^
<td colspan=2><p><b>H. $par [1 name1 ]. ,*</bx/px/td></tr>
<tr>
<td>$image</td>
<td alignsicenter>
<р>Поз:".$par[1pos*]."
<p>$showhide<br>
<a href=editphotofoxm.php? ijphotd=". $par [ *xd_photo■ ] . "& 1丄匚3 ta log=^$ i d_pa r ent >Исправить </a> <br>
<a href-delphoto.php?
idj)hoto=". $par [' idjhotd' ]. ”& id_cataloqH$ id_pa rent>Удaлить</a></td> </trx/table></td>,1;
// Увеличиваем значение временной переменной $td $td++;
// Если временная переменная $td приниьзает значение, // равное 3, то строка таблицы должна быть завершена.
// Необходимо вывести завершающий тег </tr>, а значение самой
〃 переменной обнулить
if ($td е 3)
{
echo "</tC
$td = 0;
</table>
Как видно из листинга 12.3, в начале скрипта происходит вывод ссылки для добавления новой фотографии. После чего во временной переменной $ query формируется SQL-запрос на извлечение записей из таблицы photo, соответствующих фотографиям в текущей группе. Если в группе присутствует хотя бы одна фотография, происходит формирование таблицы, строка
которой состоит из трех ячеек. В каждую из этих ячеек помешаются фотография и управляющие ссылки для нее. В ячейке выводятся название фотографии, уменьшенное изображение фотографии (являющееся гиперссылкой на фотографию большого размера), позиция относительного других фотографий и три управляющих ссылки: Скрыть/Отобразить, Исправить и Удалить.
: Замечание )
Управляющая ссылка Скрыть/Отобразить формируется во временной переменной $ showhide и указывает на скрипт hidephoio.php или showphoto,php, в зависимости от значения поля hide таблицы photo.
Добавление новой фотографии в группу (см. рис. 12.6) осуществляется при помощи HTML-формы addphotofbrm.php, содержимое которой представлено в листинге 12.4. Форма принимает один параметр: id_cataiog ——первичный ключ каталога, в который добавляется фотография.
:—Г: А”"""*; :1”,-£*-:*「炉-記體吋4'" ",№” ”r?****JW4***^' 'Г.'**'Ч1*、屮'М0-У*£ '屮• •••ц»*.*т1»г1»дмуд*дм.?." v»-aja,,,” …r …*3Ff **,…**『*,**,、* * **、”•№»,,&
;Листинг 12.4. Файл addphotoform.php
t.^,i^i^^^^,ii,:i^^i^.^^^..,,t^,i,.7,-,.^d,¥^r^,,ii-i.i...^..i..^,,.t..^.;t^.A.,..^'t'..^^.i..^...AiiL....^....i.^t^ii.^r^. ^;^:i
<?php
// Устанавливаем соединение с базой данных require_once (п.»/config* php");
// Настраиваем управляющие переменные по умолчанию
if(!isset($button)) $button = "Добавить”;
if(Iisset($action)) $action = "addphoto4phpH;
if(!isset($showhide)) $showhide = "checked";
// Получаем параметры из строки запросов
$id_catalog = $_GET['id_catalog*];
$id_j>hotQ = $__GET U id_photo」;
// Если позиция контактного блока не передана форме#
/ / определяем из таблицы photo. Для этого определяем максималь ное // значение поля pos в таблице photo и увеличиваем его на единицу if(!isset($pos))
$query ^ "SELECT MAX{pos) AS maxpos FROM photo WHERE id_cat:alQg = $id_catalogn;
$maxpos = mysql_query ($query);
if($maxpos)
{
if(mysql_num_rows($maxpos)>0) $pos = mysqlresult($raaxpos, 0) + 1; else $pos = 1;
} else $pos = 1;
I
?>
<a hre^index. php?id_parent=<? echo $id_catalog; ?»
Вернуться на страницу админис триро в а ни я
</а>
// Форма для добавления фотографии в каталог
<form enctype^ Multipart/form-data1 action=<?php echo faction; ?> method=post>
Название * : <input type=text name=name value®1<?php echo $name; ?> * xbr>
Описание : <input type=text name=description
valueac,<?php echo $ descript ion; ?> * ><br>
Изображение * : <input type=file name=linage><br>
Позиция * : <input type=text nanie=pos value=1<?php echo $pos; ?>1><br>
Отображать : < input type=checkbox naine»hide <?php echo $ showhide; ?»<br>
<input type=subniit value=<?php echo $button; ?»
〈input type=hidden name=id_cata1og value^<?php echo $id_catalog; ?»
<input type=hidden name=id_photо value=<?php echo $id_photo; ?» </form>
Форма состоит из трех текстовых полей для названия (name), описания (description), позиции фотофафии (роз), поля типа file для изображения (image), флажка (hide) Отобразить и кнопки, позволяющей отправить введенные данные скрипту-обработчику.
( Замечание J
Форма добавления фотографии содержит два скрытых поля — id_catalog и id_photo — для передачи обработчику одноименных переменных. Первое значение (id_catalog) необходимо для корректного возврата на главную страницу администрирования, а второе (id_photo) понадобится позже при редактировании фотографии для передачи первичного ключа редактируемой фотографии.
Из листинга 12.4 видно, что после установления соединения с базой данных происходит установка управляющих переменных HTML-формы. Переменные $button и $action хранят название кнопки формы и скрипта- обработчика соответственно. По умолчанию при добавлении фотографий в качестве обработчика выступает скрипт addphoto.php. При редактировании фотографии имя скрипта-обработчика будет переопределяться. Переменная $ showhide по умолнанню настроена таким образом, чтобы флажок hide был
установлен в значение show. Переменная $pos принимает значение, равное увеличенному на единицу максимальному значению поля pos таблицы photo среди фотографий текущей группы. Если в силу каких-либо причин SQL-запрос, извлекающий максимальное значение поля pos, не может быть выполнен, или фотография добавляется в пустую группу, переменная $pos, подставляемая в качестве value-значения, в текстовом поле pos принимает значение 1.
:Замечание )
На практике удобнее увеличивать значение поля pos на большую величину, напрммер, 5. Это сделает более удобным пересортировку фотографий при работе с фотогалереей. Можно будет изменять позиции, не опасаясь, что у разных фотографий будут одинаковые значения поля pos.
После заполнения полей HTML-формы и нажатия кнопки Добавить данные отправляются с кри пту-обработч и ку addphoto.php. Содержимое данного файла представлено в листинге 12.5.
単.*■1*看/岁卄瞄g樓i®■&圖繼置受衽譽МУ£?%.:胃罡宾*i|禺陷滔整,**毕巒绳壁聲?七Уm齊遂爭學严對学炉1^毁罩器里轡g^??^^^寸霊覚的m 訊吧豐囈图型.怏照吧吧勲嚙娥胤 „.譌譌總圖 <?php
// Устанавливаем соединение с базой данных
require_once (” ・./config・php1'};
// Включаем файл с вспомогательными функциями include ("util*php");
// Проверим - достаточно ли информации для занесения в базу данных
if (empty (S^OSTL1 name1]))
if (empty ($_POST Гроз1 ]))
// Проверяем# скрыта или if {S—BOSTLhidk] = “on" else $showhide = "hid曰“; // Заменяем одинарные кав $_POST[*name"1 = str_repl // Если во временном ката // файл, копируем его из if (! empty ($_EILE5「image {
// Определяем расширени $ext = strrchr($ FILES[
links ($_POST [1 id_catalog1 ]『
••Отсутствует название фотографии");
links ($_POST [1 id__catalog」,
"He введена позиция фотографии'1);
нет фотография )$showhide = "ghow";
ычки обратными
асе (и 911 f$_POST [1 name»]);
логе имеется соответствующий полю image временного каталога в каталог /files 〔厂 tmp_name」))
е файла
* image1 ][ * патле1 ], “ J;
// Формируем путь к файлу
$ image = "files/1' .date ("YmdHis^, time ()) . "$extH;
$sraallrmage = "files/".date("YmdKis", time ()) . I,_s$ext,';
// Перемещаем файл из временной директории сервера
// в директорию /files Web-приложения
if (copy ($_FILES [ * linageф Ц * tmp_name1 ], 11.. /". $image)) {
// Уничтожаем файл во временной директории unlink (帛.FILES [1 image"「tmp_name* 】};
// Изменяем права доступа к файлу chmod(м . . /11.$image, 0644);
}
} else links($_POST[*id_catalog1], "Фотография не передана на сервер"); // Вызываем функцию resizeimg () , создающую ^еныпенную копию фотографии 〃 $ image и помещающую ее в файл $ smallimage if(!resizeimg($image, $ smallwage, 133, 100)) (
links($_POST[,id_catalog'],
"Ошибка при создании уменьшенной копии изображения с помощью библиотеки GDLib");
}
// Формируем запрос
Squery = "INSERT INTO photo VALUES (NULL,
,,\$_POST[*name,] ・”', *$siriallimage*, f image I .1 $showhidef,
,,.$_POST[lpos,]
". $_POST [1 id_catalog」.”}";
if (mysql_query ($query)) {
// Осуществляем автоматический переход на главную
// страницу администрирования
echo "<HTMLXHEAD>
СМЕТА HTTP-EQUIV='Refresh1
CONTEND10; URL^index. php?id_parent=lf. $_POST [' id_catalog']."' > </HEAD>";
} else links ($_POST [1 id_catalog1 ],
иОшибка при добавлена новой записи в таблицу фотографий");
// Небольшая вспомогательная функция для вывода
// сообщений в окно браузера function links($id_catalog, $msg) {
echo Ур>「$msg. ”</p>";
echo rr<p><a href=# onClick= history .back (J *>
Вернуться к правке фотографии</ах/р>п;
echo "<p>v& href=index.php?id^arent=$id_catalog>
Адаинистрирование фотогалереи</а></р>н; exit ();
}
?>.
После установки соединения с базой данных проверяются обязательные для заполнения поля name и pos. Если их значения пусты, при помощи вспомогательной функции links о работа скрнпта останавливается, а в окно браузера выводятся соответствующие сообщения и ссылки для возврата.
Затем анализируется значение параметра hide, содержащего значение "on”, если флажок Отобразить установлен, и принимающего пустое значение, если флажок не установлен. В зависимости от его состояния переменная $ showhide, используемая далее для формирования SQL-оператора insert, принимает значение 'show' или 'hide:
Следующим этапом является замена одинарных кавычек обратными во всех текстовых полях таблицы.
После чего скрипт осуществляет копирование загруженного файла из временной директории сервера в директорию /files Web-приложения. При этом, для того чтобы названия файлов были уникальны в пределах одной директории, имя каждого файла формируется из значения текущего времени в формате yyyymmddhhmmss* Имя скопированного файла помещается во временную переменную $ image, а ИМЯ уменьшенного варианта ——$ small image. Для того чтобы имена файлов различались, уменьшенному изображению добавляется суффикс ”_з”. Расширение файла остается прежним и извлекается из названия при помощи функции strrchr(), которая осуществляет поиск последнего вхождения символа в строку (в нашем случае в качестве такого символа выступает точка В случае удачного копирования файла из временной директории сервера в директорию Web-приложения /files файл во временной директории уничтожается, а только что скопированному файлу присваиваются права доступа 0644 в нотации UNIX.
Восьмеричная цифра 0644 означает, что владелец файла может осуществлять чтение файла и запись в него (2 + 4 = 6), а пользователи группы и все остальные пользователи могут лишь осуществлять чтение файла (4). При этом никому не позволено исполнять загруженный файл (1).
После того как фотография перемещена в директорию /files, происходит вызов функции resizeimgo 9 создающей при помощи библиотеки GDLib 2 уменьшенную копию фотографии в файле, определяемом переменной $ small image. Функция принимает четыре аргумента н возвращает true в случае успеха, н false в случае неудачи. Первый параметр функции является строкой, в которой содержится путь к исходному изображению, второй параметр принимает путь к файлу, в который необходимо поместить уменьшенную копию изображения. Третий н четвертый параметры передают максимальную ширину и высоту уменьшенного варианта изображения в пикселах.
( Замечание )
Функция resizeimg () размещается в файле util.php, который будет рассмотрен ниже. Для своей работы функция требует расширения PHP — библиотеки GDLib 2, основы работы с которой будут рассмотрены в главе 22.
После этого во временной переменной $ query формируется SQL-оператор insert, который передается в качестве аргумента функции mysqi_query(). В случае успешного добавления новой фотографии осуществляется автоматический переход к главной странице администрирования.
Следующее действие, которое будет рассмотрено, это редактирование информации об уже существующей фотографии. Для этого будет использована рассмотренная ранее в листинге 12.4 HTML-форма addphotofbrm.php. Для редактирования фотографии она должна быть настроена с помощью файла edilphotofbrm.php, содержимое которого представлено в листинге 12.6. Файл editphotofbrm.php принимает в строке запроса два параметра: id_photo и id^catalog, содержащие первичные ключи для редактируемой фотографии и текущего каталога соответственно.
沙"空Н口喪£呷更唾虫吧7电里
<?php
// Устанавливаем соеданение с базой данных require_once {"•・/config ・php");
// Настраиваем HTML-форму для исправления позиции с фотографией
$button = ^Исправить11;
$ action = 11 editphoto. php11 ;
$id_j)hoto = $jGET [r id_j^hoto “ ;
/ / Формируем и выполняем SQL-запрос
$query к "SELECT 廿 FROM photo
where id_photo 二 $id_photo";
$pht = mysql_query ($query};
if ($pht)
$photo = mysql_fetch_array ($pht);
$name = $photo[“ name *];
$pos ^ $photo[^os1];
if($photо[1hide *] = "show") $showhide ^ "checked";
else $showhide ="”;
include **addphotoform* phprt;
В листинге 12,6 управляющим переменным faction и $button HTML- формы addphoiofbrni.php присваиваются значения, позволяющие настроить ее для редактирования фотографии. Переменная $action, в которой хранится имя скрипта-обработчика HTML-формы, получает значение "editphoto.php'\ переменная ^button, несущая ответственность за название кнопки формы, получает значение '^справить" * value-значения элементов управления формы извлекаются из таблицы photo. Обработчик формы editphoto.php (листинг 12.7) во многом схож с файлом addphoto.php.
"挙/‘帛-X 了&*$'K'/F":・ E*』;*';Y/::qr 1 ^г•,^^т^^•.■ ;У■芯产 *:'u'''*',''i"'y,''" У 丁;: i”t •■ "1У ':”',";
;Листинг 12.7. Файл editphoto.php
:"'■^^** 3*’***%";‘d:* W *;****■**,*!>,*;,;*";『****":*.*.*■*.■$*;i:'扌亡**,,-*・*[*¥告".**;*:.,*♦♦;・卜‘■*;・;/;**;\*毋.
<?php
// Устанавливаем соединение с базой данных
гequirе^_опсе (м.. /config.р!ф”};
include ,futil.php11 ;
// Проверим - достаточно ли информации для занесения в базу данных if (empty ($_POST【Fame Ч )) links ($_POST I1 id_catalog [,
"Отсутствует название фотографиип);
if(en^ty($_POST[гроз'])) links($_POST[* xd_catalog1],
**He введена позиция фотографииr);
// Проверяем, скрыта или нет фотография
if ($__POST[Ihidet] 一 ” on") $ showhide = “show”;
else $showhide = "hidd‘;
// Заменяем одинарные кавычки обратными
$_POST[ 'namer ] = str_replaceL …# "' L $_POST「namer ]);
// Инициализируем временные переменные
$ small = ”七
$big = "";
/////////////////////////////////////////////////////////////////////// // Блок удаления изображений
/////////////////////////////////////////////////////////////////////// // Если редактор в поле для изображения передает символ ”一" // или другую фотографию, следует уничтожить предыдущую
if (! empty ($_FILES [1 image' ] [' иф_пате1 ])) {
$ query = '* SELECT small, big, £Q_phoS FROM photo WHERE id_photo=M . $_POST [ ' id_photo * ];
$pct 二 nTfsql_query($ query >;
if {! $pct) 1 inks ($_POST[1 id_catalog1 ], "Ошибка обращения к базе данных");
if (пуsql_nujn_^ows ($pct) >0)
{
$photo = mysql_f etch_array ($pct);
/ / Если малое изображение существует - уничтожаем его
if (file_exists (и. ./и .$photo[ 'small * ]) &&> $photo[1 small' ] != "-"} unlink ("»./". $photo [' small']);
// Если увеличенное изображение существует - уничтожаем его if(file_exists("../".$photo['big']) && $photo['big'] !="-") unlink ("_/"・ $photo [ * big']);
}
$ small = "small = *-'「;
$big = "big ='-',”;
}
///////////////////////////У////////////////////////////////////////// // Блок загрузки изображений
/////////////////////////////////////////////////////////////////////// // Если поле выбора изображения не
// пустое - копируем его из временного каталога в каталог /files
if (! en^)ty ($_FHLES「image' ] [1 tnp_name")) {
// Определяем расширение файла
$ext = strrchr($_FILES[' image1]['name']f и.");
/ / Формируем путь к файлу
$ image 二"files/1' .date( MYnx3His*', time()) . "Sext11;
$ small image =竹 files/* .date ("YindHis",匕ime(}). "_s§ext";
// Перемешаем файл из временной директории сервера в
// директорию /files Web-приложе ни я
if (сору ($_FILES [1 imagef ] [' tn^L_name1 ] , ". / . $ image)) {
/ / Уничтожаем файл во временной директории unlink ($_FILES [,image "「tmp_name * ]);
// Изменяем права доступа к файлу
chmod(н../M.$image, 0644);
$big = ” big = * $ image1, *'J
// Вызываем функцию resizeiing()r создающую уменьшенную
// копию фотографии $ innage и помещающую ее в файл $ эта 11 image
if(!resizeimg($image, $smallimage, 133# 100))
links {$_FQST [Tdjagog,],
11 Ошибка при создании уменьшенной копии изображения с помощью библиотеки GDLib");
}
$small = ” small = '$smal1image',";
// Формируем SQL-запрос на исправление позиции
$ query = "UPDATE photo SET name ^ '". $_POST [' name1 ] . ,r ' # pos=,'.$„POST['pos,]-,S
$ small
Sbig
hide=,$showhide'
WHERE id__photo="・ $_POST【'id-photo1 ];
if (mysG_query ($query))
{
echo n<HTML><HEAD>
<meta HTTP-EQUIV=1 Refresh'
CONTENT二,0; URL=index. php?id_parent-и. $_POST [1 id_catalogf)."' > </HEADx/HTML>";
} else links ($_POST [' id^catalog' ] # ° Ошибка при исправлении фотографии");
17 Зак 1181
От скрипта добавления новой фотографии скрипт редактирования editphoto.php отличается SQL-запросом: вместо оператора insert используется оператор update. Кроме того, меняется порядок работы с файлами.
Для управления записями в таблице photo при формировании SQL- оператора update используются две временных переменных: $smaii и $big для уменьшенной и увеличенной версии фотофафий. Для управления файлами в директории /files используются два блока кода: “блок удаления нзо- бражений*1 и "блок копирования изображений0. После выполнения действий над файлами переменные $ small н $big принимают определенные значения. Каждая из этих переменных может принимать три значения (на примере переменной $smaii):
口 пустая строка в том случае, если действий над файлом не производится;
口 "smai^1-т, *' если уменьшенная версия фотографии удаляется;
□ "smai^^ath1 где path — путь к файлу, если происходит загрузка новой фотографии (например, взамен старой)*
Если в поле image предается новое изображение, то в "блоке удаления изображений0 уничтожаются уменьшенная и увеличенная версии фотографий (если, конечно, эти файлы существуют).
В результате работы этого блока переменные $ small и $Ыд получают значения "smal工=* -1 ” н "big=' -1 ” соответственно.
Блок копирования изображений повторяет код в файле addphoto.php (см. листинг 12.5). Отличие заключается в том, что переменные $smail и $big в случае успешного копирования файла получают значения "smalA Ssmallimage 1И,,big=, $ image1 /' соответственно.
Удаление фотографии из системы осуществляет файл delphoto.php, содержимое которого приведено в листинге 12.8.
<?php
// Устанавливаем соеданемие с базой данных requireonce (н../config.phpn);
// Получаем параметры из строки запроса
$id__catalog = $_GET [' id^catalog1 ];
Sid^hoto == $_GET [ * id_photo1 ];
// Выясняем пути к фотографии и ее уменьшенной копии
$query = "SELECT big, small, id_photo FROM photo WHERE id photo = $idjphoto,r;
$pct = mysql_query($query);
if($pct)
i f (mysql_num__rows ($pct) >0)
(
$photo ^ mysql_fetch_array ($pct);
if (file_exists (11.. /,f. $photo[1 big1 ]) && $photo[ fbig*} !="-") unlink{”••/"・ $photo「big']);
if (file_exists(H . .Z11 .$photo[ * small1 ]) && $photo[ lsmallr ] !=,'-”) unlink(M. * / ,1 .$photo[1 small* ]);
}
)
// Формируем и выполняем SQL-запрос на удаление
// фотографии
$query - "DELETE FROM photo
WHERE id_photo^$id_photoм;
if <mysql_query ($query))
{
echo "<HTML><HEAD>
<META HTTP-EQUIV=1Refresh1 CONTENT"‘0; URL=index.php?
id_parent=$id_catalog■>
</HEAD>";
} else pvrterror ("Ошибка при удалении фотографии*");
?>
Перед удалением записи в таблице photo в скрипте выясняются пути к фотографии и ее уменьшенной копии и, если они существуют, производится нх удаление. Уже после этого осуществляется выполнение SQL-оператора DELETE..
〔 Замечание )
Скрипты hidephoto.php и showphoto.php. осуществляющие сокрытие и отображение фотографии, рассматриваться не будут, так как они многократно уже рассматривались ранее м их реализация не представляет затруднений. В любом случае их можно найти на прилагаемом к книге компакт-диске или загрузить по адресу http://www.softtime.ru/info/downloads.php.
В файле utiLphp расположена единственная функция resizeimgO, код которой приведен в листинге 12.9.
:Листинга 12.9. Функция resizoimg (),三 j
<?php
// Функция, создающая уменьшенную копию фотографии $ filename,
// которая помещается в файл $ small image
// Уменьшенная копия имеет ширину и высоту, равную
// $w и $h пикселам соответственно.
// Это максимально возможные значения. Размеры сохраняемого изображения // вычисляются непосредственно в функции таким образом, чтобы // сохранить пропорции масштабируемого изображения. function resizeimg (^filename, $smallimage, $w, $h) {
// Имя файла с масштабируемым изображением $filename ^ 11. »/Г1. ^filename;
/ / Имя файла с уменьшенной копией, $snialliinage = °.. / ". $ small image;
// Определим коэффициент сжатия изображения,
// которое будем генерировать
$ratio ^ $w/$h;
// Получим размеры исходного изображения
$size_img = getimagesize (Sfilename);
// Получим коэффициент сжатия исходного изображения $src_ratio=$size_img fO) /$size_img [1];
// Здесь вычисляем размеры уменьшенной копии, / / чтобы при масштабировании сохранились
// пропорции исходного изображения if ($ratio<$src_ratio) {
$h - $w/$src_ratio;
}
else
$w - $h*$src_ratio;
}
// Создадим пустое изображение по заданные размерам $dest_img = imagecreatetrпесоlor($w, $h);
// создаем jpeg из файла
$src_img = iinagec reate from jpeg ($ filename);
// Maсштабируем изображение функцией imagecopyresanpled() // $dest_img - уменьшенная копия
// $src_irng 一 исходное изображение
// $w - ширина уменьшенной копии // $h высота уменьшенной копии // $size_img[0] - ширина исходного изображения // $size_img[l) - высота исходного изображения imagecopyresampled ($de5t_img, $src_img, 0, 。,
Or О, $w, $h, $size_img[0], $size__ini!g[l]);
// Сохраняем уменьшенную копию в файл image jpeg ($dsst_img, $smallimge);
// Очищаем память от созданных изоОражений imagedestroy ($dest_img}; imagedestroy ($src_img); return true;
} ?>
Более подробно принцип работы данной функции рассмотрен в главе 22, посвященной основам работы с библиотекой GDLib.
В завершение необходимо переработать скрипт в фа ri ле delcat.php, удаляющий каталог. В нем необходимо предусмотреть удаление фотографий из директории /files. С учетом того, что Web-приложение реализует модель одноуровневого каталога н рекурсивный спуск необязателен, файл deicat.php можно представить в следующем виде (листинг 12Л0).
:・^^'上5^ 需^^^е :УД^^!Д.力承:当少力学吐:长寧,±:±¥±*;::・:【::':/ V,:*嶋^受N±* t,,j,,-*"'A,J^^MJ^,* ”", *«ny、" *ку W,„寿 YJ.«*終,,...........,,..:}<♦[:
Листинг 12.10. Файл deicat.php
<?php
// Устанавливаем соединение с базой данных require_опсе {".•/config.php");
// Получаем параметры из строки запроса $id_catalog ^ $_GET[ *id__catalog*];
$id__parent = $_GET [' id_p&rent'];
// Удаляем фотографии из каталога
// Формируем запрос, чтобы найти все фотографии в каталоге
$ result = mysql__query (11 SELECT * FROM photo
WHERE id_catalog = $id_catalogM);
if (!$result) puterror (г,О!пибка при выполнении запроса");
while($row = mysql_fetch_array($result))
(
// Уничтожаем фотографии из удаляемого каталога
$query = "SELECT big, small, id^photo EROM photo WHERE id_photo = "•$row「id_photo,];
$pct = mysql__query($query);
if($pct)
{
if (my3ql_num_rows ($pct) >0)
// Удаляем файлы с фотографиями
$photo 二 mysql_fetch_array($pct);
if (file_exists{n. ,/rl.$photo[ rbig* ]) && $photo[ 'big* ] !="-") unlink(”・・/"・ $photo「big〔);
if(file_exist3 V\ Ju.$photo['small»]) && $photo[rsmall1] != ”-”) unlink(v.,/*\$photo[1 small* ]) f
)
}
// Формируем SQL-запросы на удаление записей из таблиц photo и photocat $query_photo = иDELETE FR6< photocat WHERE id_catalog=$id_catalog°;
$query_j>hotocat = "DELETE FROM photo WHERE id_catalog=$id_catalog11; if (mysql_query($query_photo) && mysql_query($query_photocat)) (
// Осуществляем автоматический переход к странице администрирования echo °<HTMLXHEAD>
<МЕТА HTTP-EQIjIV=,Refresh' CONTENT=T0; URI产index.php?
id_pa rent=$ id_pa rent'>
</HEAD>";
Здесь перед удалением записей, относящихся к группе из таблиц photo н photocat, в цикле while, осуществляется удаление всех фотографий, относящихся к удаляемой группе.
На этом разработка системы администрирования фотогалереи завершена и можно перейти к представлению фотогалереи на сайте. Для этого понадобятся два файла:
口 index.php ——отображающий группы фотографий (см. рис. 12.1);
□ photos.php — выводящий таблицу с фотографиями в текущей группе (см. рис. 12.2).
Содержимое файла index.php представлено в листинге 12.11.
期/”咨*蟀解畢哇縛好 讐霹*歷吃"酬留'涉岩季*绡■■^里2境‘£:二畢里=隼".滑【,■箍撈■嚟罩緻瞄"¥"举中"耳产岑;锻蜜缁部繼|継犖/当
i Листинг 12.11. Файл index.php
<?php.
// Устанавливаем соединение с базой данных require_once ("config.php");
// Извлекаем из строки запроса параметр id_parent $id__parent = $_GET [1 idjparent1);
if(empty($id_parent)) $idjparent = 0;
?>
<html>
<body>
<tablextrxtd width=10%> </tdxtd>
<table>
<trxtd>
<?php
// Если текущий каталог не является корневым, // выводим ссылку для возврата в прелыдущее меню if ($id_parent != 0)
echo "<а class^enu href-index.php?id_parent=O>BepxHnii уровень</а>**; ?>
<table >
<?
// Выводам группы фотографий
$query = иSELECT photocat.id_catalog AS id_catalogr photocat.name AS name, COUNT (photo. id_j>hoto) AS total FROM photocat, photo WHERE photo.id_catalog = photocat.id_catalog AND photocat.hide - 'show' AND photo.hide = 'show' GROUP BY photocat.id_catalogH; $ctg = mysql_query($query);
if (!$ctg) puterror(*'Ошибка при обращении к Фотогалереем);
// Если в таблице photocat присутствует хотя бы одна
// группа фотографий - выводим их в таблице
if (inysql_nuin_rows ($utg) >0)
{
// Выводам заголовок таблицы групп фотографий
echo "<tir class=' tabi eheader cat * >
<td align=center><p class=zagtable> Название группы фотографии</td>
</tr>H;
while ($cat = mysql_fetch__array ($ctg))
{
// Выводим список каталогов
echo "vtt>
<td><p><a href=index.php?id_parent=".Scat['id_catalog']•”> ” * $cat[*name*]."</a></td>
</tr>r,;
}
?>
</table>
</td></tr>
<tr><td>
<?php
// Выводам содержимое групп фотографий, если текущий каталог
// не является корневым .
if ($id_parent != 0) include "photos.php1*;
?></td></trx/table>
Скрипт начинается с многотабличного запроса к таблицам photo и photocat, извлекающего названия групп фотографий и число фотографий в них. Если в системе имеется хотя бы одна группа —— формируется таблица со ссылками на группы и числом фотографий. Переход по ссылке на название группы приводит к выводу таблицы с фотографиями. В каждой строке таблицы размещается по 5 уменьшенных копий фотографий, которые являются гиперссылками. Нажатие на них приводит к открытию фотофафии большого размера в новом окне. За формирование таблицы фотографий отвечает файл photos.php, содержимое которого представлено в листинге 12.12.
■ ■■• W:,"М/*厚1 ;?•*■**♦»• ••••^1^,м».м*;*«,叫!•芋畢,уд*/ • • ?••••.• у •.•<*,*•••■•• ”“ у, : .,у ,竺?! R»■”:銀泣锄竺м銀qj 呼 *,、孽*,“ • jg筹 H^t *-" *『^;*Т"-У "■ 嵯逑^2ё®
Листинг 12.12, Файл photos.php
/:.; J ・$二“;仁.*.*.,‘I кК士“.т.?#1*.. *.;*.♦“*;*.«?:/:.; J ・$二“;仁.*.*.,‘I кК士“.т.?#1*.. *.;*.♦“*;*.«?::ЯчУ*4M. • ■ X •;…* 3*2v,M*iT・c; • ♦ ;; ; .1『「.;-,十:曲.vSVi階№*\*;Ш; ,*;2/二莉;к п ;…h* “ &«:* $ ::“*“<
/:.; J ・$二“;仁.*.*.,‘I кК士“.т.?#1*.. *.;*.♦“*;*.«?:
/:.; J ・$二“;仁.*.*.,‘I кК士“.т.?#1*.. *.;*.♦“*;*.«?:
<table>
<?php
// Выбираем из базы данных фотографии в каталоге, / / который определяется переменной $id^parerit
$query = "SELECT * FROM photo
WHERE id_catalog 二 $id parent AND
hide =,show1 ORDER BY pos";
$prt = mysql query ($query);
if (! $prt)
echo Herror : r, .rnysql_error () /*<br>r,;
echo $query;
puterror("Ошибка при обращении к блоку "Фотогалерея0");
}
// Если в текущей группе фотографий имеется хотя бы одна
// фотография - формируем таблицу с фотографиями
if (mysqX_nuun_rows ($prt) > 0) {
// Вспомогательная переменная для вывода
// фотографий по 5 штук в строке таблицы $td = 0;
〃 Выводим заголовок таблицы while($par = mysql_fetch_arrav($prt)} {
// Формируем переменную $ image, отвечающую за вывел
// уменьшенной копии изображения, которая является ссылкой 〃 на фотографию большого размера
if(!empty($par[1 small1]} && Spar['small']!=”-" && file_exists($par[1small,]))
$ image = "<a href=f rr. $par [ fbig 4 .rr 1 target=_blank>
<img src=,f.$par [1 small1 ] . "></a>"; }
else $ small = "Нет";
// Если значение временной переменной равно 0,
// выводим тег начала строки таблида <tr> if (Std к 0) echo "<tr>";
// Выводим фотографию
echo °<tdxtable border=0 width^lOO^xtr align=center> <tdxpxb>".$par [ ^ame* ] . "</bx/p></tdx/tr> <tr>
vtd>$ image</td> </tr></table></td>1';
// Увеличиваем значение временной переменной $td $td++;
// Если временная переменная $td принимает значение,
// равное 5, следовательног строка завершена и необходимо // вывести завершаоций тег </tr>, а значение самой // переменной обнулить If (§td == 5} (
echo "v/tc11; $td = 0;
</table><br><br>
В отличие от файла photoes.php (см. листинг 12.3) системы администрирования, файл photos.php выводит таблицу, содержащую 5 фотографий в строке. Кроме этого, все SQL-запросы содержат условие "hide = ,5how'", позволяющее выводить только доступные для отображения фотографии.
Глава 13
Система отправки сообщений с сайта
В этой главе будут разработаны приложения для отправки почтовых сообщений с сайта. Глава разбмта на три раздела: в первом разделе рассмотрены принципы работы электронной почты, во втором — разрабатывается приложение для отправки простых сообщений (без вложений) и в третьем 一 разработан класс для отправки сообщений с вложениями.
Введение
в работу электронной почты
Для работы с электронной почтой используются прикладные протоколы POP, SMTP и IMAP, базирующиеся, как и все протоколы прикладного уровня, на семействе протоколов TCP/IP.
Протокол SMTP (Simple Mail Transfer Protocol ——простой протокол передачи почты) предназначен для передачи сообщений между почтовыми серверами Интернета.
Почтовый сервер отвечает за прием всех сообщений, поступающих на определенное доменное имя (к примеру, для Иванова —— на имя chem.ru). Для каждого пользователя на сервере заводится специальный файл (почтовый спул), в который помешаются сообщения в ожидании, когда пользователь соединится с сервером, чтобы их получить. Сервер также отвечает за рассылку писем, поступивших от его пользователей, конкретным адресатам. Предварительно при помощн службы DNS сервер выясняет, какой сервер в сети отвечает за прием сообщений для каждого конкретного адресата (например, сообщения с адресами somebody@chem.ru принимаются сервером mail.chem.ru), после чего он устанавливает с этим сервером связь по протоколу SMTP.
Протокол POP (Post Office Protocol — почтовый протокол) предназначен для того, чтобы предоставлять пользователю доступ к пришедшим для него на
сервер почтовым сообщениям, т* е. осуществляет связь между компьютером пользователя и почтовым сервером, на котором располагается почтовый ящик пользователя.
Протокол IMAP (Internet Message Access Protocol — протокол доступа к со* общениям в Интернете), как и протокол POP, используется для доступа пользователей к почтовому серверу. IMAP —— более сложный протокол, чем POP, и позволяет пользователю хранить и каталогизировать информацию непосредственно на сервере. По своей сути IMAP является расширенной версией протокола POP3.
Изначально электронная почта предназначалась только для пересылки текстовых сообщений на английском языке. Это связано с тем, что протоколы SMTP н POP, будучи очень простыми как в реализации» так и в эксплуатации, работают только с такими сообщениями. Однако в настоящий момент электронная почта может передавать не только текст, но н самые разнообразные вложения произвольного формата и содержания. Достигается это путем конвертации файлов вложений в текстовый формат. Этой процедурой занимается почтовая программа пользователя, отправляющего сообщения (почтовая программа получателя производит обратное преобразование).
[ Замечание )
Под текстовыми сообщениями понимаются сообщения, состоящие из строк ограниченной длины, причем каждая строка состоит из алфавитно-цифровых символов базового набора ASCII и знаков препинания, кодируемых 7-битовыми кодами. Символы с 8-битовыми кодами (к примеру, кириллица) в этот набор не входят.
Почтовые агенты
Основные функции электронной почты реализуются при помощи трех почтовых агентов:
口 пользовательский почтовый агент MUA (Mail User Agent);
транспортный агент или агент передачи сообщений МТ A (Mail Transfer Agent);
口 агент доставки сообщений MDA (Mail Delivery Agent).
В ведении пользовательского агента (MUA) находятся получение и доставка почтовых сообщений. Транспортный агент (МТА) функционирует на сервере, и его задачей является маршрутизация сообщений. За помещение сообщений в почтовый ящик пользователя ответственна другая программа —— агент доставки сообщений MDA. Также встречается аббревиатура MRA (Mail Retrieval Agent)—— агент получения сообщений. Этот термин не является общепринятым и употребляется в отношении почтовой службы, осуществ-
ляющей вьгемку сообщений из почтового ящика на удаленном сервере и передачу их пользовательскому агенту (MUA) по протоколу POP или IMAP. Рассмотрим функции программ-агентов более подробно.
Пользовательский почтовый агент — MUA
Пользовательский почтовый агент выполняет следующие функции:
口 получение сообщений с почтового сервера;
создание сообщений, включение в ннх вложений н передача для обработки транспортному агенту;
口 каталогизация почтовых сообщений, их хранение и удаление;
口 обработка сообщений: предоставление текста в требуемой кодировке, распознавание и извлечение вложений;
некоторый дополнительный сервис, например, предоставление адресной книги.
Почтовыми агентами являются такие программы, как The Bat, Outlook Express, Netscape Messenger и др. В качестве локальных пользовательских агентов в UNIX популярны программы mail и pine.
:Замечание )
Тенденцией последнего времени стало выполнение функций почтовых агентов Web-браузерами.
Транспортный агент — МТА
Транспортный агент, по сути, является маршрутизатором сообщений и ему поручено выполнение следующих задач.
口 Анализ и преобразование адресов и заголовков почтового сообщения (разбор переадресации (foi-warding), псевдонимов (aliases), списков рассылки; преобразование форматов адресов в том случае, если транспортный агент (МТА) является шлюзом между двумя почтовыми системами; маскировка почтового домена отправителя (masquerading), к примеру, для сокрытия устройства сети организации; установка служебных заголовков в сообщении).
口 Определение агента доставки для каждого сообщения и передача сообщений их агентам доставки.
Управление очередью сообщений,
口 Взаимодействие с DNS с целью определения почтового сервера адресатов*
Возврат сообщений, которые по тем или иным причинам невозможно доставить адресату.
В UNIX-подобных операционных системах наиболее распространенным транспортным агентом является программа sendmail. В последнее время набирают также популярность qmail и postfix.
〔 Замечанье Q
Наиболее распространенным РОР-сервером является Qpopper, а IMAP-сервером —Cyrus.
〔 Замечание )
На серверах под управлением ОС Windows функционируют такие почтовые серверы, как Microsoft Exchange и Netscape Messaging Server, однако по сравнению с sendmail и qmail на данный момент они уступают им в безопасности.
Агент доставки — MDA
Существует несколько типов агентов доставки, различающихся способом доставки почтовых сообщений.
Tun local выбирается, если письмо направлено на почтовый ящик, находящийся на этом же сервере. Доставка производится добавлением сообщения в соответствующий почтовый спул. К примеру, в UNIX агентом доставки типа local является программа mail, запущенная как "mail -d ivanov*'. В этом случае агент доставки добавит сообщение письма к файлу /var/mail/ ivanov.
Тип SMTP выбирается, если письмо адресовано почтовому домену, обслуживаемому другим (удаленным) сервером. Доставка производится путем пересылки письма транспортному агенту удаленного сервера по протоколу SMTP. Очень часто агент доставки типа SMTP интегрируется в программу транспортного агента. Примером такой интефации может служить сервер sendmail.
Тип prog выбирается в том случае, если письмо должно быть обработано какой-либо программой. При доставке производится вызов той программы, на вход которой подается содержимое письма.
Протоколы SMTP,
POP и IMAP
SMTP
Протокол SMTP используется для пересылки сообщений от пользовательского агента транспортному, а также между различными транспортными агентами. Обычно SMTP-сервер прослушивает 25-й порт.
В зависимости от роли, выполняемой при передаче почтового сообщения, серверы подразделяют на следующие типы:
口 инициатор;
конечный сервер;
口 ретранслятор;
почтовый шлюз.
От инициатора сообщение поступает в сеть.
( Замечание )
В качестве инициатора может выступать и почтовый клиент.
( Замечание )
SMTP был разработан в 1982 гм чтобы заменить FTP при передаче сообщений электронной почты от одного сервера к другому.
Конечный сервер является конечной точкой в иепи серверов и доставляет сообщение в лотовый ящик адресата.
Ретранслятор сочетает в себе функции как сервера, так и клиента, поскольку он принимает сообщения от другого ретранслятора или инициатора (выступая как клиент) и осуществляет дальнейшую пересылку этих сообщений (выступая как сервер).
Почтовый шлюз предназначен для преобразования формата сообщения, если оно пересылается между почтовыми системами с разными форматами.
Пример SMTP-сеанса
SMTP является строчно^ориентированным протоколом. После того как установлено соединение между клиентом и сервером, сервер ожидает ввода клиентами команд в текстовом виде. Ниже приведен пример простого SMTP-сеанса, в котором пользовательский агент (MUA) для отправки письма с компьютера maks.chem.com соединяется с транспортным агентом (МТА) на компьютере dot.phys.net:•
220 dot.phys.net ESMTP Sendmail 8.9,3/8.9.1;
Sun, 11 Jan 2004 15:30:25 +0300 (MSK)
HELO maks. chem. com
250 dot.phys * net Hello [264.168.192.2]f pleased to meet you
MAIL EMM: kuznetsov^chem. ru
250 kuznetsov@chem.ru*.. Sender ok
RCPT TO: simdyanov^phys.ru
250 simdyanov@phys.ru... Recipient ok
RCPT TO: golyshev@sof11ime,ru
250 golyshev@softtiwe.ru... Recipient ok
DATA
354 Enter mail, end with ".” on a line by itself
From: Maksim Kuznetsov <kuznetsov@chem. ru>
To: Igor Simdyanov <simdyanov@phys.ru>
Cc; Sergey Golyshev <golyshev@softtime.ru>
Subj ect: Important Note
Hello, programmers!
We have to discuss our plans.
Maks.
250 Message accepted for delivery
QUIT
221 dot.phys.net closing connection
Этот пример является типичным SMTP-сеансом между клиентом и сервером. Клиент посылает серверу команды следующего вида:
КОМАНДА аргумент
На эту команду сервер отвечает, возвращая клиенту строку, содержащую трехзначный код ответа, после которого следует текстовый комментарий. К примеру:
250 dot.phys.net Hello [264*168.192.2], pleased to meet you
Коды ответа, начинающиеся с 2, являются положительным ответом, с 3 — промежуточным положительным ответом. Коды, которые начинаются с 5, являются сообщением об ошибке.
:Заменанме )
Таблица числовых кодов ответа SMTP-сервера приведена в разд. 13.2.2 данной главы.
Как ВИДНО ИЗ первой строчки (220 dot.phys.net ESMTP Sendmail 8.9.3/s. 9.1;), транспортный агент (МТА), с которым произошло соединение, поддерживает расширенный протокол SMTP — ESMTP (Extended Simple Mail Transfer Protocol). После соединения с MTA посылается команда helo. В качестве аргумента этой команде передается мня домена, с которого осуществляется соединение:
HELD maks.chem.com
Для того чтобы сообщить серверу, кто является отправителем сообщения, посылается команда mail from, после которой следует двоеточие н электронный адрес отправителя:
MAIL FROM: kuznetsov^chem.ru
Затем следует команда rcpt то, после которой указывается адрес получателя данного сообщения:
RCPT ТО: simdyanov@phys« rut
Получателей может быть несколько, в этом случае применяются дополни* телъные команды rcpt то, число которых равно количеству получателей. После этого серверу посылается команда data, аргументом которой является пересылаемое сообщение. Концом сообщения считается точка, расположенная на отдельной строке:
DATA
Subj ect: Iiuportant Note
Hello, programmers I
We have to discuss our plans.
Maks,
Завершение диалога с сервером и разрыв соединения осуществляются командой quit.
POP
Протокол POP используется для получения сообщений электронной почты, а точнее, для пересылки пмеем с почтового сервера на компьютер пользователя. В настоящий момент используется третья версия этого протокола (POP 3), и далее везде, где говорится о POP, будет подразумеваться именно эта версия. При работе POP-сервер прослушивает порт 110. Ниже приведен пример POP-сеанса, в ходе которого происходит получение электронной почта.
Пример РОР-сеанса
+ОК POP3 chem.com v2000 server ready
USER kuznetsov
+OK Password please
PASS password
+OK kuznetsov has 2 messages
LIST
+OK 2 messages
1 345
2 678
RETR 2
Return-Path: <www.navolge.ru@nt3.mtw.ru>
Received: by pochta.mtw.ru (Postfix, from userid 8170)
id 929EA304301; Fti, 26 Dec 2003 15:00:33 +0300 (MSK)
From: Maksim Kuznetsov <kuznetsov^chem.ru>
To; Igor Simdyanov <simdyanov@phys.ru>
Cc: Sergey Golyshev golyshevSaofttime.ru
Subject: Important Note
Date: Fri, 26 Dec 2003 15:07:52 +0300
Message-ID: <001101c3cba8$e3127dd0$cffc763e@nt3>
Hellof programmers!
We have to discuss our plans.
Maks *
DELE 2
+OK Message 2 has been deleted
QUIT
+OK Connection closed.
POP-сеанс, как и SMTP-сеанс, начинается с подключения к серверу. При успешном выполнении команды ответы РОР^сервера начинаются с +ок, при ошибочном — С -ERR.
POP-сеанс состоит из 3 стадий: авторизации, транзакции и обновления. При подключении к серверу пользователь находится в состоянии авторизации. Идентификация пользователя происходит с помощью команд user и pass. Если авторизация прошла успешно, пользователь переходит в состояние транзакции^ в котором он может получать и удалять сообщения. Команда list предназначена для вывода епмека сообщений. В приведенном выше примере в почтовом ящике сервера находятся два сообщения. Для просмотра каждого конкретного сообщения серверу посылается команда retr, при этом в качестве аргумента этой команды указывается номер сообщения. Для удаления сообщений предназначена команда dele, аргумент которой представляет собой номер удаляемого сообщения. При использовании команды dele физического удаления сообщения не происходит, оно лишь помечается как предназначенное для удаления (восстановить сообщение, т. е. снять с него метку об удалении можно с помошью команды rset). Полностью сообщение удаляется при поступления команды quit, которая завершает состояние транзакции и инициирует состояние обновления. В этом состоянии РОР-сервер уничтожает сообщения, предназначенные для удаления, и закрывает сеанс.
IMAP
Протокол IMAP (Internet Message Access Protocol) также, как и протокол РОР} предназначен для того, чтобы удаленный почтовый клиент мог получить доступ к своим сообщениям, хранящимся на сервере. Как правило, для работы IMAP-сервер использует порт 143*
IMAP — значительно более сложный протокол, чем POP. Основное различие между протоколами POP и IMAP заключается в организации хранения сообщений и способе доступа к ним* При использовании протокола POP сообщения загружаются с сервера и хранятся на жестком диске клиента. При этом с сервера сообщения удаляются, если только не настроить программу почтового клиента так, чтобы они оставались на сервере. При работе с IMAP сообщена兄 остаются на сервере в любом случае, поскольку на локальный клиентский компьютер пересылки сообщений не происходит.
Структура сообщения электронной почты
Сообщение электронной почты независимо от его содержания представляет собой простой текстовый файл, состоящий из заголовков и тела сообщения.
( Замечание )
Базовый формат сообщения электронной почты описан в RFC-2822.
К примеру:
Return-Path: <www.navolge.ru@nt3.mtw*ru>
Received: by pochta.mtw.ru (Postfix, from userid 8170)
id 929EA304301; Fri, 26 Dec 2003 15:00:33 +0300 (MSK)
Received: from nt3.nthosting*ru (unknown [62,118,252.207])
by pochta.mtw.ru (Postfix) with ESMTP id 34F9D30433C
for <kuznetsov@navolge.ru>; Frir 26 Dec 2003 15:00:33 +0300 (MSK)
Received: from mail pickup service by nt3.nthosting.ru with Microsoft
SMTPSVC; Fri, 26 Dec 2003 15:07:52 +0300
From: <www♦navolge.ru^pochta.mtw.ru>
To: <kuznetsov@navolge.ru>
Subject: request for
Date: Fri, 26 Dec 2003 15:07:52 +0300
Message-ID: <001101c3cba8$e3127dd0$cffc763e@nt3>
I want having a e-mail address very much. Please, register me!
Заголовки электронной почты представляют собой строки, разделенные символами \г\п. Каждый заголовок начинается с новой строки и состоит из
ключевого слова, после которого следует точка с запятой, и данных. Ниже подробно рассмотрено, какие заголовки могут встречаться в сообщении.
Тело сообщения является обычным текстом.
Заголовок Return-Path
Заголовок Return-Path добавляется последним из участвующих в доставке сообщения транспортным агентом (МТА) и представляет собой путь к отправителю, по которому сообщение будет возвращено, если его не удастся доставить.
Заголовок Received
Заголовок Received добавляется каждым SMTP-сервером, через который проходит сообщение. Это делается, чтобы проследить, через какое количество транспортных агентов (МТА) прошло сообщение перед тем, как попасть к адресату.
Транспортные агенты (МТА), через которые проходит сообщение, иногда называют также транзитными участками или хопами (от англ, hop — расстояние, преодоленное без перерыва). Количество транзитных участков подсчитывается для того, чтобы избежать зацикливания, когда письмо проходит по кругу через несколько одних и тех же транспортных агентов. Большинство SMTP-серверов отклоняют сообщения, содержащее более 25 полей Received.
Заголовок Message-ID
Заголовок Message-iD записывается сервером, создающим сообщение, и содержит глобально уникальный идентификатор сообщения электронной почты.
Обязательные заголовки
Некоторые из полей заголовков электронной почты должны обязательно присутствовать в каждом сообщении. Такими полями являются: Date, From, То.
Заголовок Date
Этот заголовок содержит время, в которое пользователь отправил сообщение. К примеру:
Тие# 10 Aug 2004 21:38:19 +0400
В этом поле присутствуют:
口 день недели: тие;
口 локальное время транспортного агента (МТА): 10 Аид 2004 21:38:19;
口 часовой пояс транспортного агента (МТА): +0400.
Замечание )
Запись +0400, которой указывается часовой пояс, означает "на четыре часа1' к востоку от Гринвича.
Замечание )
В заголовке Date неявным образом сообщается также действительное время транспортного агента (МТА)( получаемое вычитанием от локального времени транспортного агента (МТА) величины смещения от Гринвича к востоку. То есть в приведенном примере действительное время составляет 10 Aug 2004 12:38:19.
Заголовок From
Заголовок From представляет собой адрес электронной почты отправителя. В этом поле можно выставить несколько адресов, разделенных через запятую.
Заголовок То
Поле то содержит адрес основного получателя сообщения. Если получателей несколько, то их адреса разделяются запятыми.
:Замечание )
Некоторые пользовательские агенты разделяют получателей сообщения точкой с запятой, поэтому при создании почтовых приложений имеет смысл преобразовывать обнаруженные точки с запятой в запятые.
Заголовок Вес
Поле вес (Blind Carbon Сору — слепая копня) содержит адреса других получателей того же самого сообщения. Отличие от необязательного поля Сс (см. ниже) состоит в том, что это поле не отображается в заголовках других получателей. Иначе говоря, ни один из пользователей, получивших копию сообщения, не будет знать, что оно послано еще кому-то.
:Замечанье )
Вместо поля То может присутствовать поле Вес, которое тоже относится к обязательным заголовкам. В сообщении должно присутствовать либо поле То, либо Вес.
Необязательные заголовки
Заголовок Сс
В поле этого заголовка помешаются адреса получателей, которым рассылается копия сообщения, посланного по адресу, указанному в поле то. В отличие от поля Вес, это поле отображается в заголовке сообщения, т. е. все получатели данного письма будут в курсе, кто еще его получил, кроме них.
Заголовок Яер/у-То
В поле этого заголовка содержится адрес, на который должен посылаться ответ, т. е. при нажатии на кнопку Ответить почтовый агент в качестве почтового адреса использует этот заголовок. Если он отсутствует, то сообщение отправляется по адресу, указанному в поле From.
Заголовок Subject
Поле Subject представляет собой заголовок сообщения электронной почты, который еще называют темой сообщения.
Клиентские поля заголовка
Приложения, используемые для отправки сообщений электронной почты, могут определять некоторые собственные поля заголовка, к примеру, поле x-Mailer, являющееся сегодня стандартом де-факто для болыиинства программ, выполняющих функции пользовательских агентов. В поле x-Maiier отображается имя программы, при помощи которой было сгенерировано сообщение. К Примеру, запись X-Mailer: Microsoft Outlook Express 6.00.2600.0000 означает, что почтовой программой-клиентом на компьютере пользователя является Microsoft Outlook Express.
I Замечание )
Клиентским полям заголовка предшествует префикс Х-, по которому они отличаются от остальных (стандартных) полей.
В качестве другого примера можно привести поле x-MSMaii-Priority, сообщающее О приоритете сообщения, К примеру: X-MSMail-Priority: Normal.
Отправка простых сообщений
В этой части мы рассмотрим два способа отправки сообщений: с помощью стандартной PHP-функции mail о, предназначенной для отправки сообщений электронной почты, и с помощью обмена заголовками с удаленным SMTP-сервером.
Функция mail для отправки сообщений электронной почты
Отправка почты средствами PHP осуществляется при помощи функции maiio: bool mail(string to, string subject, string body
[,string extra_headersl 【,string extrajparametersI)
Эта функция принимает следующие аргументы:
□ to (адрес электронной почты получателя);
门 subject (тема сообщения);
口 body (текст сообщения);
口 extra_headers (дополнительные заголовки, которые можно задать в сообщении);
口 extra_parameters (дополнительные параметры, которые можно задать в сообщении).
( Замечание )
Поля subject и body для темы сообщения и его тела могут быть пустыми строками.
Рассмотрим несколько примеров работы с функцией mail и разработаем скрипт для отправки сообщений. Простой пример использования функции приведен в листинге 13.1.
¥^y:^^f笈£〔!йКТ 鐘^I^SSSjA^i^^'f^^^^^S^SJ'Vf餐K^il^ST"'先t^fitKSi&^f^^Sy幣У!冶?]'""'^^'"噌贲Е±*^?Й^蕾瓶■!^й^:&''Ц^Н嘴}軍旌彳緑; Листинг 13.1. Простой пример использования функции stall
<?php
mail (**kuznetsov@softtinie.ru*\ "Привет!", '* 1 строка\п2строка\n3строка** ) ?>
В этом примере на адрес kuznetsov@softtime.ru будет отправлено сообщение с темой привет и текстом
1 строка
2строка
3строка
( Замечание )
Для совместимости с ОС Windows каждую строку в почтовых сообщениях необходимо заканчивать символом \г\п.
В листинге 13.2 показан пример использования необязательного параметра extra_headers, содержащего дополнительный заголовок From.
,*^^г,i^4^M^i^KK^^^^l^y^l*"'^^^^^W^^*li^l"[*F^y・r^^^『:^yi^?^>w,^^''Ji^^^^'^^^^'***'**:''*"'f"*”ll^^*"'*'*^!g'*^i>^'*^"''*'*"l*^*'&'^l|'^^J<<" ;'"f",^^^|%2 :Листинг 13・2・ Пример использования функции mail с дополнительным
"From: admin@softtinie. ruM) ?>
Если требуется указать несколько дополнительных полей, они соединяются в одну строку символом объединения (точкой), например, как в листинге 133
罰L留i^^fW"LL■ ■ ••.—-.••'* ^'^;?^^^^11^当洋g跨里潜干找单笋?談*晔》饕隣,罗У纪w■尊?窗产■:%?统联学营.............у!^?^- ;Листинг 13.3. Пример использования функции mail с дополнительным
;заголовком
<?php
$msg = "Содержимое гшсьма";
mail("kuznetsov^softtime.гии, "Привет!", $msg,
•'From; admin@ {$_SERVER (* SERVER__NA№1 ] }\r\n「 "Reply-To: admin@{$_SERVER['SERVERJ4AME']}\r\n". "X-Mailar: PHP/"・ phpversion());
В этом примере в поля From н Reply-то подставляется адрес вида а(1тт@имя_сервера. При этом имя сервера возвращается из суперглобального массива $_server. В пользовательском поле x-Mailer в качестве программы -отправителя указывается PHP той версии, которая установлена на сервере. Версия PHP возвращается с помошью функции phpversion{).
Необязательный параметр extra__parameters, пример использования которого приведен в листинге 13.4.
广嚴:崙戸火■”;" ・:"" "兴"М у里咒㈱準彎"f可丁工.崇弋燮7笃里'先4演':『;';:『""…:/":生学崖V'累不f1" "*?'',塁:;…丁…гy ;/‘?/?豐相 i Листинг 13.4* Пример использования функции mail с дополнительным
j заголовком■
Листинг 13.5. Форма для отправки сообщений электронной почты
♦,&•・*^**;,♦・**・.*“、、”;■»■♦'♦・♦**♦;*■%■:■*・*»士 ■ .“**,『♦!“♦«**、・**;',*♦"”**'*・,・,,♦、*■*・**аkvkvJee**/.?*iii♦ **к1;-#^*・*“■ а ■ Jil*u***,“・.♦*;»*,*',#“;《*.**.*♦“♦*■ r.V^V^/UN** ・,
<HTML>
<HEAD>
<Т1ТЬЕ>0тправка сообщениж /Т ITLE>
</HEAD>
<BODY>
<H3> <? print "<center>n; ?> <font color=#lE90FF> Отправка сообщения
</font></H3>
<center>
<table width=I border=0>
<fom action^simple_jnail.php methods=post>
<input type=hidden name=action valtie=post>
<tr>
width=50%>To:
</td>
<td
al ign=rightxinput type=text патентаil_to maxlength=32
value=l<? echo $mail_to; ?> *>
</td>
</tr>
<tr>
<td
width=50%>Subject:
</td>
<td
align=right><input type=text name=mail_sobject maxlength=64 valu+У? echo $mail_subject; ?>,>
</td>
</tr>
<tr>
<td
со1зрап=2>Сообщение: <br>
<textarea
cols=50 rows=8 патентаi 1 jnsg><? echo $mail_msg; ?>
</textarea>
</td>
</tr>
<tr><
td colspan=2><input type=submit value=1Отправить1>
</td>
</tr>
</form>
</table>
</center>
</BODY>
</HTML>
Внешний вид HTML-формы показан на рис. 13丄
引 Отправка сообщения - Microsoft Internet Explorer
□ |х
Рис. 13J. Форма для отправки сообщения электронной почты
Код обработчика этой формы содержится в файле siniple_mail.php и приве ден в листинге 13.6.
阳1当修附宅1倒睁器缙‘ь,^,'"'-,!"',W 睪扌*>'瓚随£学号禮?患■**3炉匕》皆黑 第爭鎖・нT竺安晋弋漑潜跟紡壮岂昌¥.岁ту.维注簪叫諫I解
Листинг 13.6. Файл slmple_malLphp.]
;各黨;*;脑・"就汁而揃⑦杀,U“t: W*;M・:.:H*,;¥**;;* 二您#”a八К;*工'^;;«ik福冕室«'ihnwMnn^1>7• • iLi
v?php
faction = $_POST [ "action11 ]; if (!empty($action)) {
if (1 empty ($mail__to))
{
// проверяем правильность заполнения с помощью регулярного выражения if (!pregjnatch(*7 [0-9a-z_]+@[0-9a-z_A\.]+\.[a-z]{2,3}/i", $mail_to)) (
$ action =
echo ("Введите адрес в виде somebody@server.com11); exit ();
$mail_subject = tгim {$maiL_subject);
$wail_msg = trim($mail_jnsg);
$niail_to = substr ($_POST[Hmail_to"] /0^32);
$mail_to = htmlspecialchars (stripslashes ($mail__to));
$mail_subject = substr t$_POST (Mmail_subjectr,], 0, 64);
$mail_subject = htiulspecialchars (st rips lashes ($mail_subject)); $mail_msg = substr ($_POST [ "mail_msgH ], Or 1024);
// формируем сообщение
if (nail ($mail__to, $mail_subject, $mailjmsg)) {
echo (4Сообщение успешно отправлено11); } else <
echo(нПри отправлении сообщения произошла ошибка**);
else
{
echo("Введите адрес получателя"};
}
?>
Замечанье )
Часто при отправке сообщений с сайта в форме для отправки сообщений при^ сутствует только поле для ввода сообщения. В системе же администрирования этого блока содержится поле для ввода адресов, на которые будут приходить сообщения с сайта. Адреса при этом хранятся в файле или базе данных.
:З.мечанме )
Часто при отправке сообщений с сервера необходимо преобразовать кодировку сообщений. Для этого следует воспользоваться функцией convert_ayr_string ().
Функция mail о будет работать только в том случае, если на машине, где выполняется скрипт с ее использованием, установлен почтовый транспортный агент (МТА). Если у вас на компьютере нет какого-либо запущенного МТА либо удаленного SMTP-сервера* п редоставляю те го доступ к вашей ма山мне. то функция mail работать не будет. В состав практически любого дистрибутива UNIX входит sendmaii, выполняющий его функции, в то время как в операционной системе Windows для этого требуется установка стороннего транспортного агента (МТА).
Свойства локального почтового агента задаются в разделе [mail function] файла настроек php.ini. Этот раздел файла выглядит следующим образом (листинг 13.7).
tteyVi!_W-"^***^VV^"'"''^'^'^'*^Sy,l,**'TT^''^ft'',?^,*'"''4V**,,,,'t*,Vt**^*'*'*'t***,,''*'"'''''';,",*_,,-"J-'^,'*,,,'*'***'?*^A,*'*'"A*,,>, ****** t*,*,:■*,'*^*C^■i,*'',***,^,^"■!'^■,*^■^l'!,i,^,f*■<'*Ч,•* j Листинг 13.7. Раздел [mail function] файла настроек php.ini
"■Г*л<««»я*ая<ваав/'||«|'|«1«|'|*|Т?ИН^/|1|№« VI чТм}&У»«Л,/7?«'в|П||Ч»Ъ*а«**Г»Ь#«>1****'Зт«»«**1ГгАг¥Т||111^1/^1«ЬкЪЪЪЬЪЬЪ»Ъ«« + «*Ь-Хь1^Ь'«^*к Л 1«й««Да|/в««#*чЛвГ«4« ■>■«'■« «tf.riltfinilikfiiBbaRabV-a'nb^^^tftfiM^f [mail function] ;For Win32 only. SMTP = localhost ;For Win32 only,
sendmail__f rom = me^example. ссж ;For Unix only.
;sendmail_path =
;mail. force_extra_paramaters =
Первые две строки имеют смысл только при использовании на Windows- платформе и устанавливают удаленный SMTP-сервер, которым вы можете воспользоваться для отправки сообщений (первая строка устанавливает SMTP-сервер, а вторая ——адрес отправителя). Для того чтобы сообщения могли отправляться, удаленный SMTP-сервер должен поддерживать ретрансляцию, иначе письма будут возвращаться с сообщением об ошибке.
Отправить сообщение с локальной машины можно, не имея ни одного установленного почтового сервера и не прибегая к функции mail. Для этого необ-
хоинмо разработать приложение SMTP-клиента, которое будет соединяться с любым удаленным SMTP-сервером. В PHP отсутствуют функции для работы с SMTP-серверами, поэтому в следующем разделе этой птавы будет разработана собственная фикция для работы с удаленным SMTP-сервером.
1322. Функция для работы с удаленным SMTP-сервером
В этом разделе будет создана функция для отправки сообщений электронной почты, которая будет соединятся с удаленным SMTP-сервером и организовывать SMTP-сеанс, во время которого будет происходить отправка сообщения. Код функции приведен в листинге 13.8.
揚 y” :审.у…二.:?. ;т^у m,:K:N±邕
:Листинг 13.8.
f£^KK"KlVr*>al**a%4vvA'* ...
<?php
// функция отправки сообщения: открывает сокет, ведет диалог с сервером, // записывает данные, закрывает сокет
function send($server, $七。,$from, $subject='"\ $msg, $header3=”“)
{
// формируем поля заголовка
$headers="To: $to\nFrom: $fron\nSubject: $subject\nX-Mailer: My Mailer\n$headers u;
// соединяемся с сервером по порту 25, при этом переменная $fp
// содержит дескриптор соединения
$fp = fsockopen($server, 25, &$errno, &$errstr, 30);
if (!$fp) die("Server $server. Connection failed: $errno, $errstr“); // если соединение npoumo успешно, производим запись данных в сокет, // т. е. открываем наш SHTP-сеанс с удаленным сервером $server fputs($fp7"HELO $server\nH); // здороваемся с сервером // посылаем поле from
fputs($fp,HMAIL FROM: $from\n");
// посылаем поле To
fputs($fp, MRCPT TO: $to\nr,);
// посылаем поле Data
fputs ($fp,,rDATA\nn);
// посылаем сообщение, которое содержится в переменной $msg
fputs ($fp,M$msg\r\nMJr.n. ”\工\口”);
〃 посылаем заголовки fputs($fp,$this^>headers);
if (strlen(^headers))
fputs($ fp,M $header3\nM);
〃 завершаем SMTP-сеанс fputs ($fp/*\n.\nQUIT\nu);
// завершаем соединение fclose($fp);
// отправка сообщения
send('wx2.yandex.ru*,
//
почтовый ретранслятор сервера yandex
lmail0yandex.ru*,
//
кому
*mail0softtime.ru*,
H
от кого
1HelloJ 1
//
тема
1Привет!');
//
сообщение
В начале функции происходит соединение с почтовым ретранслятором удаленного SMTP-сервера по 25 порту с помощью функции fsockopenf), работа с которой была подробно рассмотрена в главе 4. Затем серверу отправляются SMTP-команды, которые приведены выше в этой главе. Текст сообщения должен заканчиваться строкой на отдельной странице, поэтому точка обязательно должна присутствовать в строке отправки текста сообщения: fputs <$fpr H$msg\r\nAr\nH);
Узнать, какое имя имеют почтовые ретрансляторы конкретного SMTP- сервера, можно при помощи следующего скрипта, выводящего список почтовых ретрансляторов заданного сервера и их приоритет (листинг 13.9).
注门"了^^^审事炉覺嘗^^^^"岁工洋^^里瞪務^^^^^^^^;,,„蜀м"骂簿学^『jj堂 勺:洋…:飞 i Листинг 13.9. Получение списка почтовых ретрансляторов
<?php
// некоторый адрес электронной почты $email=,rmail0yandex. ru";
// разбиваем адрес на массив из двух элементов: имени почтового ящика
// и имени почтового сервера
$email_arr = explode (,,0П , $email);
// заносим в переменную $host имя почтового сервера
$host = $email_arr[Ц;
// далее определяем список почтовых ретрансляторов сервера $host
//и выводим их с указанием значений предпочтения getmxrr($host, $mxhostsarr, $weight|;
echo "Ha $ email письма могут отправляться через следующие хосты: <br>11; for ($i=0; $i < count($mxhostsarг); $i++) {
echo (*f$mxhostsarr [$i ] = $weight [$i] <br>");
} ?>
В этом скрипте используется функция getmxrr, возвращающая список почтовых ретрансляторов. Синтаксис функции: string getnixrr(string hostname# array mxhost,【,array weight])
Эта функция принимает в качестве аргумента имя хоста hostname в данном домене и заполняет массив mxhost списком почтовых ретрансляторов этого домена. Если указан третий необязательный аргумент weight, то функция заполняет его значениями предпочтения, которые возвращает ей почтовый ретранслятор. К примеру, для адреса mail@yandex,ru скрипт из листинга 13.9 вернет следующий список почтовых ретрансляторов, через которые происходит отправка писем SMTP-сервером Yandex: mx2.yandex.ru = 10 mxl.yandex.ru = 0
〔 Замечанмб )
Названия и количество почтовых ретрансляторов SMTP-серверов имеют свойство часто изменяться.
Скрипт в листинге 13.8 можно дополнить таким образом, чтобы получать ответы сервера. Для этого после строки, в которой посылается команда завершения сеанса, необходимо разместить следующий код (листинг 13.10).
,,«j^i|y г^у ^^-:^fj^jy ^У18,**^^ •^^Д^^У ^fJ^^M^^^i "'"*'" и 工^^[^jj^S1^: IЕ 般・ 'И^У2,jK^Mtf * j^S^^4j_^y : "、y*鬟л*书章优泸 *::”,* w*«颦.¥j^^4^^^^^g^^^^t± ;Листинг 13.10. Дополнение к листингу 13.8
;・.t、'・^i|^*i;;;,t •;.1>}・;;**";11・・・Г«・»・..・*・・・・Н・、・;-|й1$*‘%・;『,.Г.,т・тГ・;«:*".;11Е,;У.'^^;"г€'Ло*~*;'Й|Г・ г #***<*■ i *^*1 ^ » й 1 й ^wt #■ Л * ■'■ ^ ■ > t е ■ • ^ »» ^ ч Л *■' tVii |V ■ ri r/ifvii^^i^ • * ^Л^'Л V г/1'Л??*1 iW■ T while (1 feof ($fp))
{
echo (str_replace(n:",":Snbsp; Anbsp; ", fgets ($fp,128)}*"<br>");
Возможные коды ответа SMTP-сервера приведены в табл. 13,1.
Таблица 13.1. Коды ответов SMTP-сервера
Команда
Код ответа
Установление соединения
Успех: 220
Ошибка: 421
НЕЮ
Успех: 250
Ошибка: 421,500, 501, 504
MAIL FROM
Успех: 250
Ошибка: 421, 500, 501
RCPT ТО
Успех: 220
Ошибка: 421, 500. 501, 503, 550-553, 450-452
DATA
До отправки данных:
Успех: 354
Ошибка: 421.451,500, 501, 554
После отправки данных:
Успех: 250
Ошибка: 451,452, 552, 554
QUIT
Успех: 221
Ошибка: 500
Ответ сервера может иметь, к примеру, следующий вид (листинг 13.11).
:Листинг 13.11. Пример:Листинг 13.11. Примерответа SMTP-сервера;ответа SMTP-сервера;,****с**т・;.;・・*,,****с**т・;.;・・*,220250250250220250250250oryx.nodex.ru оryx.nodex.ruoryx.nodex.ru оryx.nodex.ru// команда// команда// команда// командаESMTP // успешное соединение с сервером nodex.ru 〃 команда HELO послана успешноMAIL FROM послана успешноRCPT ТО послана успешноESMTP // успешное соединение с сервером nodex.ru 〃 команда HELO послана успешноMAIL FROM послана успешноRCPT ТО послана успешно
:Листинг 13.11. Пример
:Листинг 13.11. Пример
ответа SMTP-сервера;
ответа SMTP-сервера;
,****с**т・;.;・・*,
,****с**т・;.;・・*,
220
250
250
250
220
250
250
250
oryx.nodex.ru оryx.nodex.ru
oryx.nodex.ru оryx.nodex.ru
// команда
// команда
// команда
// команда
ESMTP // успешное соединение с сервером nodex.ru 〃 команда HELO послана успешно
MAIL FROM послана успешно
RCPT ТО послана успешно
ESMTP // успешное соединение с сервером nodex.ru 〃 команда HELO послана успешно
MAIL FROM послана успешно
RCPT ТО послана успешно
354 go ahead // команда DATA послана успешно
250 ок 1090071704 qp 5913 // данные тоже успешно посланы
221 oryx.nodex»ru // соединение завершено успешно
Отправка сообщений с вложениями
Для отправки сообщений электронной почты с вложениями необходимо использование MIME — спецификации, расширяющей возможности стандартной электронной почты.
Замечание )
Аббревиатура MIME переводится как кмногоцелевые расширения почты Интернета0 (Multipurpose Internet Mail Extensions), эта спецификация описана в RFC 2045-2049,*
Спецификация MIME позволяет расширить возможности обычной элек- фонной почты для решения следующих задач:
口 пересылки 8-битовых текстов н 8-6мтонь1Х символов в заголовке сообщения;
□ пересылки двоичных данных любого типа (графика, аудио, видео и т. д.);
口 поддержки сложных типов сообщений (к примеру, сообщений, содержа- ших данные рахпичных типов).
Для выполнения этих задач в спецификации MIME вводятся два дополнительных типа заголовков (Content-Type Н Content-Transfer-Encoding) И заголовок MIME-Version: 1.0.
Этот заголовок определяет версию MIME (в настоящий момент 1.0) и указывает пользовательскому почтовому агенту, что данное сообщение составлено в соответствии со спсиификацией MIME и не является простым почтовым сообщением стандарта RFC2822.
Заголовок Content-т^гре определяет тип данных? которые солержатся н сообщении, а заголовок Content-Transfer-Encoding определяет способ представления этих данных.
:Замечание )
MIME-типы используются не только в электронной почте, но и в Web — для определения типов данных, пересылаемых между сервером и браузером пользователя,
Типы данных
(поля заголовка Content-Type)
Заголовок content-Туре имеет следующий формат:
Con tent^Type: тип/подтип
Для некоторых типов данных необходимо также присутствие параметров, в этом случае этот заголовок записывается в следующем формате: Content-Type: тип/подтип ; параметра значение_параметра ;
Ниже приведены наиболее распрострапенные типы данных.
□ text
Тип text обозначает текстовые данные, в том числе и 8-битовой кодировки. Распространенные подтипы: text/plain и text/html. Необяза-
»8 3ак. H8I
тельный параметр: сЬагзеъ^название^кодировки. Кодировки могут быть следующими:
us-ascii (текст, только семибитные символы);
кириллица: koiB-r, windows-1251, iso8859-5.
Если кодировка не указана, по умолчанию считается, что используется кодировка us-ascii.
Пример заголовка:
Content-Туре: text/html; charset=koi8^r;
口 audio
Тип audio описывает звуковые данные. Возможные подтипы: audio/тред, audio/x-realaudio»
Пример:
Content-Type: audio/тред
口 video
Тип video описывает видеоданные. Возможные подтипы: video/mpeg, video/quicktime.
Пример:
Content-Type: video/mpeg
image
Данные этого типа представляют изображения. Возможные подтипы: linage/gif, image/jpeg.
口 application
Тип application предназначен для обозначения двоичных данных, предназначенных для обработки какой-либо конкретной прикладной программой. Этот тип содержит большое количество подтипов, к примеру, таких как: application/msword, application/rar, application/ postscript и т* n. Если неизвестно, какой программой будут обрабатьь ваться данные, используется универсальный подтип appiication/octet- stream. К примеру:
Content-Type: application/octet-stream
О message
Тип message указывает на то, что сообщение такого типа содержит в своем теле еще одно сообщение. Основной подтип: message/rfc822.
[ Замечанье )
Указанный подтмп, как правило, используется при переадресации сообщений. В этом случае нередко используется термин инкапсуляция почтовых сообщений.
口 multipart
Тип multipart говорит о том, что сообщение является составным, т. е. состоит из нескольких частей, каждая из которых имеет свои заголовки и тело. Как правило, при работе с данным типом используется подтип multipart/mixed, говорящий о том, что каждая часть письма может содержать данные любого типа. Данный подтип имеют сообщения с вложениями.
Разделы почтового сообщения отделяются друг от друга при помощи разделителя, представляющего собой уникальный набор символов. Разделитель является значением обязательного' параметра boundary. При разграничении частей сообщение значения этого параметра предваряется двумя минусами (даже в том случае, если уникальный набор символов уже содержит в своем составе два минуса). Каждый раздел в составном сообщении должен состоять из заголовков и данных, которые отделены друг от друга пустой строкой. Строка разделителя, следующая за последней частью письма, отличается от обычного разделителя тем, что за параметром boundary следуют два дефиса, говорящие о том, что других частей в письме больше нет.
Способ представления данных (поля заголовка Content-Transfer-Encoding}
Указанный заголовок определяет, закодированы ли данные, и если да, то каким-образом.
口 Content-Transfer-Encoding: 7bit
Этот заголовок обозначает текст в кодировке us-ascii, г е. данные помешаются в тело сообщения так, как они есть.
口 Content-Transfer-Encoding: 9bit
Как видно из названия, этот заголовок определяет 8-битовый текст. Данные также помещаются в тело сообщения в том виде, в каком они есть.
口 Content-Transfer-Encoding: binary
Заголовок, обозначающий двоичные данные, помещаемые в тело сообщения, как они есть. Этот заголовок, как правило, не используется.
□ Content-Tranafer-Encoding: quoted^pruntable
Заголовок представляет собой восьми битовый закодированный текст.
口 Content-Transfer-Encoding: base64
Указанный заголовок определяет двоичные данные произвольного вида, закодированные по алгоритму Base64. Данный заголовок применяется очень часто, так как алгоритм Base64 является более экономичным, чем алгоритм, которым кодируются символы в предыдущем заголовке.
Заголовок Content-Disposition
Пеобязательньгй заголовок Content-Disposition предназначен для указания пользовательскому почтовому агенту, каким образом отображать содержимое просматриваемого раздела. Данный заголовок имеет два значения:
□ inline (автоматическое отображение содержимого раздела при его просмотре пользователем; если сообщение содержит вложенный файл, то он автоматически отобразится после текста сообщения);
口 attachment (в этом случае пользователь должен открыть вложение самостоятельно).
Класс, реализующий пересылку вложенных файлов
В этом разделе мы расширим ранее созданное приложение для отправки сообщений, разработав класс, реализующий отправку сообщений с вложенными файлами. Приложение состоит из трех файлов: index.php, sendrnaiLphp и classmail.php.
Файл index.php представляет собой HTML-форму, код которой приведен в листинге I3J2.
<HTML>
<HEAD>
<Т1ТЬЕ>0тпраБка сообщения с вложением</ТITLE>
</HEAD>
<BODY>
<НЗ> <? print ^'<center>*,; ?> <font color=#lE90FF> Отправка сообщения </fontx/H3>
<center>
<table width=l border=0>
<forin action—sendmai 1 * php methodsspost>
<tr>
<td widt^50%>To: </td>
<td align=right><input type-text name=mail_to maxlength=32 value=*<? echo $mail_to; ?>*>
</td>
</tr>
<tr>
<td width=50%>Subject: </td>
<td align=rightxinpcit type=text naine«mail_subject maxlength^64 valuer*<? echo $mail_subject; ?>*>
</td>
</tr>
<tr>
<td width=50%>Вложение:</td>
<td a 1 ign=right><input type^file name"=mail_file maxlength.=64 value-*<? echo $mail_file; ?> *>
</td>
</tr>
<tr>
<td colspan=2>Сообщение:<br>
<textarea cols=5Q rows=8 name=niail_msg><? echo $mail_insg; ?></textarea>
</t&>
</tr>
<tr><td colspan=2><input type=submit value=т Отправить'></td>
</tr>
</forrrt>
</table>
</center>
</BODY>
</HTML>
Обработчик формы реализован в файле sendmail.php (листинг 13.13).
;Листинг 13ЛЗ. Файл sendmail.php
:;Х ki\j * 工“;..心、* ;.;.*….…■«.…*;,“ iti^ii'.二二;;;.' *,”;?“”.,,* А*.',…it *、r$;+♦…;i A*.%;;;*.…*„…;;W; ;y, . L,;,“"“、,…、*.;、*■♦+ > .*;•;:,. .*?;“;•—;;;•
v?php
// Включаем класс отправки письма
include "classmai1.phpH;
//////////////////////////////////////////////////////
// Блок проверки данных
////////////////////////////////////////////////////// // Проверяем, введено ли сообщение if (!isset($_POST[^ailjnsg*]))
echo (^Введите тело сообщения'*);
exit ();
// Проверяем, введен ли адрес получателя
if (! isset ($_POST [ 'mail^o•]))
echo{^Введите адрес получателя"); exit();
}
// Проверяем корректность e-mail
if (! preg__mat ch (M / [ 0- 9a-z_] +@ [ 0-* 9a-z__A\.]Л. [а-гЦ2,3}/Iй t $mail_to))
{
echo (^Введите aj^pec в виде somebody@ server. com11); exit ();
}
$path к 0;
// Если поле вложения не пусто - копируем
// его из временного каталога в каталог /files
if (! empty ($_FILES [ 1mail_f ile * ] I * tnp_nantef ]))
{
〃 Формируем путь
$path = Hfiles/11. $_FILES (*mail_file1) [ ^ame11;
// Перемещаем файл из временной директории сервера
// в директорию /files Web-приложения
if (copy ($_FILES [ ,mail_fileI ] [ * tmp_name1 ], $path)) (
// Уничтожаем файл во временной директории unlink ($_FTLES [,linage1 ][1 tmp_name * ]); } } /〃〃〃〃〃〃〃/〃〃〃〃〃〃〃〃〃〃〃〃/〃〃/〃〃〃〃 // Блок отправки письма ////////////////////////////////////////////////////// // ООъяеляем объект класса classmail $mail = new classinail ();
// Если поле для вложений не пусто, добавляем
〃 его к письму
if(!empty($path)) $mai1->add_attachment($path);
// Формируем письмо
$mail->build message ();
〃 Отправляем письмо
$mail->send(rrax2.yandex.ru1,// почтовый ретранслятор сервера yandex
$_POST[”MiI_t。”],// кому
*ttt^softtime.ru1, // от кого $_POST [ "niail_subjectH ], // тема.
$_POST ["msg*1 ]);// сообщение
?>
Код класса, реализующего присоединение вложенных файлов, приведен в листинге 13.14.
:Листинг 13.14. Класс, присоединяющий вложенные Вайль】
<?php
///////////////////////////////////////////////////////////
// Класс для отправки e-mail с вложением
///////////////////////////////////////////////////////////
class classmail
(
var $headers;
Par $multipart;
var $Taime;
// С помощью этого массива в сообщение вкладывается нужное количество
// вложений
var $parts 二 array(};
// Метод, добавляющий MIME-заголовок в дополнение к обычному public function mime_jnail ($headers=Mn) {
$this^>headers=$headers;
}
// Метод, который открывает файл и переносит
// его содержимое в переменную, выставляя
// соответствующую кодировку (application/octet-stream)
public function add_attachment($path=H H,
$encodiпg_type="application/octet-stream,^)
{
// Если файл не существует, выходим из метода
if (! file_exists (Spath))
echo "力айл Spath не найден11; return;
}
// Открываем файл
$fp = fopen($path, "г");
if (!$fp)
{
print "Файл $path не может быть прочитан"; return;
• }
// Читаем содержимое файла во временную переменную
$file = fread($fp, filesize($path));
// Закрываем файл fclose($fp};
// файл заносим в массив parts[], элементами которого являются
// тело файла, имя файла и кодировка файла
$this->parts[]=array(nbody"=>$file, "file_nanie"=>$filename, "en8ding_typ启”=>8n8ding_type);
}
// Внутренний метод, формирующий часть письма, независимо от того, // содержит оно вложение или нет private function builcUpart($i)
I
$message_part ="”;
// выставляем кодировку encoding_type (application/octet-stream) $message_part .- ''Content-Type: ".$this~>parts[$i]{Mencoding_typeM];
// если имеется вложение, присоединяем его к письму
if ($thls->parts [$1J [11 filename'1] !="”)
$message_part .= ”; filename = \"н *$ttiis->parts [$i] [Mfile^narne*1]- "\M\n";
else // иначе присоединяем пустую строку $message part .= "\nn;
// указываем значение заголовка Content-Trans fer-Encoding $message_part .= "Content-Transfer-Encoding: Ьазе64\пм;
// указываем необязательный заголовок в значении attachment // (чтобы файлы не открывались самопроизвольно) с параметром
〃 filename
$message_jpart .==" Content-Dispositioni attachment; filename = \”'L $this->parts[$i]["file_nameM]."Х^ЛпХп";
// кодируем содержимое файла в одну большую строку и разделяем ее //на группы по 76 символов, поскольку именно такое значение // является максимально допустимой длиной строки для
// закодированных данных
$message_part .= chunk_split(base64_encode
($this->parts[$i] [нbody,,]))「\门”; return $messagejpart;
)
// Метод, формирующий письмо public function build_nxessage () (
// генерируем уникальный разделитель
$ boundary = ”一” ・md5 (uniqid (time (M ) ; // генерируем разделитель
// передаем в заголовке версию MIME
$this->headers .= "MIME-Version: 1.0\n";
〃 устанавливаем заголовок Content-Type в значение
// multipart/mixed с параметром boundary
$this->headers 产 "Content-Type: multipart/mixed;
boundary=\”$boundary\"\n";
// собираем все части письма, ограничивая каждую из
// них разделителем $boundary
for ($i = (count($this->parts) - 1); $1 >= 0; $i—)
$this->imiltipart .= ”一$boundary\口".$this->butld_part($i);
// формируем завершающий разделитель,
// говорящий о ном, что части письма закончились $this->mime = и $ thls->mul t ipa rt—$ boundary--\n";
}
// Метод отправки сообщения, аналогичный разработанному
// в листинге 13.8 методу отправки сообщений через удаленный
// SMIР-сервер
public function send($server, $to, $from, $subject=,,M, $msg, $headers=tf,*)
$headers=,,To: $to\nFrom: $from\nSubject: $subject\nX-Mailer: My Mailer\n$headersM;
// Открываем сокет по 25 порту
$fp = fsockopen(^server, 25, &$errno, &$errstr, 30);
if (!$fp)
die("Server $server* Connection failed: $errno, $errstrn);
fputs($fp, f,HELO $server\n°);
fputs($fp, HMAIL FROM: $from\n,4 ;
fputs($fp, "RCPT T。: $to\M);
fput s($ fp, ” DATA\n");
fputs($fpf и$тзд\г\пи・"・”・"\r\n”);
fputs($fp, $this->headers);
if (strlen($headers})
fputs ($fp, tl$headers\n");
fputs($fp/ $this->mime);
fputs($fp, "\n.\nQUIT\n");
// Закрзгваем сокет
fclose($fp);
J
?>
Глава 14Глава 14
Глава 14
Глава 14
Система анализа посещаемости сайта
В данной главе будет разработана система анализа посещаемости сайта — PowerCounter. Крупный корпоративный сайт не может обойтись обычным счетчиком посещений, так как администратору сайта необходимо контролн- ровать множество параметров:
口 общее число хитов и хостов для сайта в целом и каждой страницы в отдельности;
口 число посещений с поисковых систем и других ресурсов, где расположена ссылка на сайт;
О IP-адреса и время посещения сайта каждым из посетителей;
口 ключевые слова, по которым сайт был найден в поисковых системах;
口 количество роботов, посещающих сайт;
口 тип и версия операционной системы и браузера посетителя и др.
Время, когда счетчик представлял собой простейший скрипт на PHP или Perl и файл, в котором хранилось число посещений, увеличиваемое на единицу при обращении к страницам сайта, безвозвратно ушло. Теперь счетчики посещений являются серьезными Web-приложениями, требующими тщательного проектирования и разработки.
Описание системы
PowerCounter
Ниже приводятся сведения, которые должна собирать система сбора и анализа статистической информации по посещаемости сайта*
口 Количество хостов (уникальных IP-адресов), хитов (общее число загрузок страниц) н засчитанных хнтов (общее число загруженных страниц без учета переходов со страниц сайта, для которого производится анализ) как по сайту в целом, так и по каждой странице.
Статистика по используемым посетителями операционным системам и браузерам.
Информация по IP-адресам и времени последнего посешения сайта каждым из посетителей.
口 Количество посещений сайта роботами наиболее известных поисковых систем (Rambler, Google, Yandex и Aport),
Информация по количеству посетителей, пришедших на сайт с поисковых систем (Rambler, Google, Yandex и Aport), а также поисковым запросам, используемым ими.
口 Количество посещений сайта со страшш, на которых расположены ссылки на сайт.
Информация должна предоставляться как по каждой странице сайта, так и для всего сайта в целом. Количество посещений выводится в виде таблицы для 5 временных интервалов: за текущий день, за вчерашний день, за 7 дней, за 30 дней и за все время сбора статистики.
Перечисленные выше требования будут реализованы в системе PowerCouiUer, описываемой в данном разделе.
Система PoweiCounter содержит служебную информацию о посещаемости сайта, и ее пользователями являются администраторы сайтов или их владельцы. Рассмотрим интерфейс системы. При входе на страницу администрирования первой открывается страница Хосты и хить! (рис. 14.1).
При открытии этой страницы в первый раз выводится таблица, содержащая информацию о количестве хостов, засчитанных хитов и всех хитов для всех страниц сайта за пять временных периодов (сегодня, вчера, за неделю, за месяц, за все время).
Заголовки столбцов таблицы являются гиперссылками. Нажатие на ссылку Сегодня или Вчера приводит к открытию страницы Хосты и хиты с группировкой по часам, на которой отражено распределение хостов н хнтов по часам в течение текущих или прошедших суток соответственно (рмс. 14.2).
Переход по гиперссылке За 7 дней таблицы хостов и хитов (см. рис. 14.1) приведет к открытию страницы Хосты и хиты за последние 7 дней, на которой отражено распределение хостов и хитов по дням недели (рис. 14.3).
Рис. 14.3* Страница Хосты и хиты за последние 7 дней
Статистика выводится в виде таблицы. Каждая строка таблицы предоставляет статистику за один из семи дней. Заголовки строк являются гиперссылками, переход по которым приводит к открытию страницы с почасовой статистикой за выбранный день.
Ссылка За 30 дней (см. рис. 14.1) открывает страницу, на которой отражена статистика по хостам и хитам за прошедшие 30 дней. Формат этой страницы полностью аналогичен формату страницы Хосты и хиты за последние 7 дней.
Ту же самую информацию о количестве хитов и хостов, но не для всего сайта, а для отдельно взятой страницы можно получить» выбрав нужную страницу в системе PowerCounter. Для этого необходимо перейти к главной странице по ссылке Главная страница счетчика в левом верхнем углу.
Главная страница счетчика (рис. 14.4) содержит таблицу со списком всех страниц сайта, подключенных к системе PowerCounter В первом столбце таблицы представлены адреса страниц в виде гиперссылок. Чтобы посмот-
реть статистику посещения не по всему сайту, а по отдельной странице, необходимо перейти по гиперссылке с ее названием. В результате откроется страница Хосты и хиты (см. рис. 14.1), а название выбранной страницы отобразится в левом верхнем углу.
Помимо адреса страницы, участвующей в сборе статистикиt таблица на главной странице счетчика содержит еще три столбца. Второй столбец содержит хиты для страниц за все время сбора статистической нн4юрмацни. Третий столбец содержит время последнего обращения к странице. Последний столбец содержит гиперсеылку Удалить, которая необходима для удаления страницы из системы PowerCounter. Это требуется для удаления из системы “мертвых” страниц, которые могли исчезнуть, например, в результате редизайна сайта.
Каждая страница системы PowerCounter содержит меню, которое состоит из 6 пунктов, являющихся гиперссылками:
口 Хосты и киты;口Поисковыероботы;
口 Системы и браузеры;□Поисковыезапросы;
□ 1Р адреса;口Ссылки
Страница Хосты н хиты была рассмотрена выше. Перейдем к рассмотрению страниц, открывающихся при переходе по другим ссылкам.
Рис. 145 Страница Системы и браузерь!
Переход по гиперссылке Системы и браузеры открывает одноименную стра- ницу, на которой представлена информация по используемым посетителями браузерам и операционным системам (рис. 14.5).
Отслеживаются три типа операционных систем: Windows, различные варианты Linux. UNIX и Macintosh. Если операционная система посетителя не попала ни в одну из этих групп или не была определена, то она заносится в группу Другие.
Замечание )
К хитам в группе Другие относятся также роботы поисковых систем и менеджеры закачек.
В группе Браузеры отслеживается 3 наиболее популярных на сегодняшний день браузера: Internet Explorer, Netscape н Opera. Если посетитель использует другой браузер или тип браузера не установлен, он заносится в группу Другие.
Переход по гиперссылке IP адреса в меню системы PowerCounter открывает одноименную страницу, на которой отображена информация об IP-адресах посетителей, зарегистрированных за последние сутки (рис. 14.6).
Информация предста&ггена в виде таблицы. Первый столбец содержит IP- адрес посетителя. Во втором столбце отображается доменное имя для данного 1Р (если имеется возможность его определить). Третий содержит общее количество посещений с данного адреса, зарегистрированное системой. Последний столбец содержит время последнего обращения, IP-адреса в первом столбце представлены в виде гиперссылок, переход по которым приводит к странице Информация о IP адресе с подробной информацией об IP-адресе (рис, 14.7), Информация об IP-адресе приводится на основе данных, получаемых от сервиса whois (см. разд. 4.4).
Рис. 14.7. Страница с информацией об IP-адресе
Переход по гиперссылке Поисковые роботы в меню системы открывает одноименную страницу, на которой представлена информация о посещении сайта роботами различных поисковых систем (рис. 14,8). Отслеживаются четыре наиболее популярные в 'рунете*' поисковых системы: Yandex. Rambler、 Google и Aport.
1 '^3|||u«<K<lBtilR роботы -
hUcrtrxrfl Internet Е>ф4игег
В1ЯЕ31
Win 3«u g>u
/igeMM Ьи*с СПРМКА
.Aj0*ci|剪依5中vmw’Kftk^n^EERMv^d^o^rcboUdp^JMQn*
Система стй-гнс-гмги PowCounle» рлр*6йт*ид м полд*!р>«нвч*т^я IT-сгудинй <StjfiT»ffii*>- ; •■;-,融.;匚&|"」
( нсгеми статистики
PoWerCoiintcr 2 0 7Ддмнн帆IQ梃立直辿他弋価三ваииуь— н \工綿I。匸―&Rj»wC0umei
Весь сдЛг
Д^и нэчалй регистрации днннык' ЖЗ 10^4 00^0:00 Прошло' 2『1 дней
1•净UTlal]_耳日色.
,且国叩产Д&33叩*/事护晔L[蜒赞蚂些世;加啾№!3您龍3
Поисковые роботы
Payout
g.SMR . 8切川《0 3f 7лн«й
■ 3■30期■,.
За 2 ■”■«•
Rnmbtar
10(79%) И (M W)非1&幽
I6IIQ3筋
Зg胤
G0新中■
I 軍 8H)19(7.0%)册 0也
1333(70%)
却I⑴均
Упм<1вж
1фЗ%)Q(P(Wi)4090.8%)
113100%)
Apotl
1(0.8%)0(0 叫М(М 闹
113td 峭
司力僧幽
Другие
Н4 駆《%) 圉 EQ4)3047 但„)
13738 6 ⑦
47277(73.74)
Прсцемт от о&щего чмела хитов
3
L !丨■•№—京
Рис. 14.8. Страница Поисковые роботы
、 Замечание )
К роботам поисковых систем (spiders) относятся пользовательские агенты, собирающие информацию в интересах поисковых систем. Ссылки на ресурс в поисковых системах появляются после посещения его поисковыми роботами.
Роботы, посетившие сайт и не относящиеся ни к одной из перечисленных поисковых систем, заносятся в группу Другие.
В группу Другие также попадают менеджеры закачек.
Страница Поисковые запросы (рис. 14.9), открывающаяся при переходе по соответствующей ссылке в меню, является одной нз самых интересных страниц системы PowerCounter.
С помощью этой страницы можно отслеживать, с каких поисковых систем и по каким поисковым запросам посетители приходят на сайт. Страница состоит из двух таблиц. Строки первой (верхней) таблицы соответствуют поисковым системам Yandex, Rambler, Google и Aport. Столбцы таблицы соответствуют пяти верменным интервалам: "Сегодня'*, "Вчера”,"За 7 дней", ”3а 30 дней*' и "За все время". На пересечении строк и столбцов приведено
число переходов для поисковой системы за определенный временной интервал. Числа являются гиперссылками, переход по которым приводит к выводу поисковых запросов, по которым посетители пришли на сайт. По умолчанию выводятся поисковые запросы лля всех поисковых систем за мСегодняг\
Рис. 14.9. Страница Поисковые запросы
Рассмотрим подробно содержимое первой (верхней) таблицы. Статистика сгруппирована для пяти временных интервалов: "Сегодня", "Вчера”, "За 7 дней*', "За 30 дней" и "За все врем订"и представлена в соответствующих столбцах таблицы. Заголовки столбцов являются гиперссылками. Переход по гиперссылкам Сегодня и Вчера приводит к открытию страницы Поисковые запросы за сутки, которая показывает распределение переходов на сайт с поисковых системы в течение текущих или прошедших суток соответственно (рис. 14.10).
Рис. 14.10. Страницы Поисковые запросы за сутки
Переход по гиперссылкам За 7 дней и За 30 дней (см* рнс* 14.9) приводит к открытию страниц Поисковые запросы за последние 7 дней (рис. 14.11) и Поисковые запросы за 30 дней соответственно.
Формат представления данных на этих страницах одинаков. Они отличаются только количеством дней, для которых показывается статистика. Данные страницы показывают количество переходов с поисковых систем, сфуппи-
рованное по дням. Первый столбец таблицы содержит гиперссылки, переход по которым приводит к открытию посуточной статистики переходов с поисковых систем за выбранный день.
Система стчт^с-нчм 户ow*rCt>uiifer y"p*8tmw* н мдд,就叫椎&¥穴肩 П"-/л>дивй * SoflTinii?*」丄,±「Система стчт^с-нчм 户ow*rCt>uiifer y"p*8tmw* н мдд,就叫椎&¥穴肩 П"-/л>дивй * SoflTinii?*」丄,±「Алммнмстписс^аннЕ t 縞т»8铀切止炉 心 са融L снсл■斤以育 F'GyefCquntarАлммнмстписс^аннЕ t 縞т»8铀切止炉 心 са融L снсл■斤以育 F'GyefCquntarС истема статистики
Система стчт^с-нчм 户ow*rCt>uiifer y"p*8tmw* н мдд,就叫椎&¥穴肩 П"-/л>дивй * SoflTinii?*」丄,±「
Система стчт^с-нчм 户ow*rCt>uiifer y"p*8tmw* н мдд,就叫椎&¥穴肩 П"-/л>дивй * SoflTinii?*」丄,±「
Алммнмстписс^аннЕ t 縞т»8铀切止炉 心 са融L снсл■斤以育 F'GyefCquntar
Алммнмстписс^аннЕ t 縞т»8铀切止炉 心 са融L снсл■斤以育 F'GyefCquntar
PowerCounter 2,0,7
EfPth СПЙТ山fitt^JillUbMUK.虹也 3EfPth СПЙТ山fitt^JillUbMUK.虹也 3W 式 MxjJflSE斤 k!W 式 MxjJflSE斤 k!А*,нз-^алз 晔нт*屮” jvhh“k, 200JJD2$ 3XNJ:P0 Припаю 271 4H喃
EfPth СПЙТ
山fitt^JillUbMUK.虹也 3
EfPth СПЙТ
山fitt^JillUbMUK.虹也 3
W 式 MxjJflSE斤 k!
W 式 MxjJflSE斤 k!
Нэ 1тиЛ гтрзнишг 叔"«-эжтч онди】' успрадоланм* ncwCKOEfen запросов с помскоа^к《,”:т«ы во ‘」*с*мНэ 1тиЛ гтрзнишг 叔"«-эжтч онди】' успрадоланм* ncwCKOEfen запросов с помскоа^к《,”:т«ы во ‘」*с*мРис. 14.11. Страница Поисковые запросы за последние 7 днейРис. 14.11. Страница Поисковые запросы за последние 7 дней
Нэ 1тиЛ гтрзнишг 叔"«-эжтч онди】' успрадоланм* ncwCKOEfen запросов с помскоа^к《,”:т«ы во ‘」*с*м
Нэ 1тиЛ гтрзнишг 叔"«-эжтч онди】' успрадоланм* ncwCKOEfen запросов с помскоа^к《,”:т«ы во ‘」*с*м
Рис. 14.11. Страница Поисковые запросы за последние 7 дней
Рис. 14.11. Страница Поисковые запросы за последние 7 дней
Поисковые iiuipocu за последние 7 дней
Последняя ссылка в меню системы PowerCounter называется Ссылки. При переходе по ней открывается страница, с помощью которой можно отслеживать переходы на сайт с других сетевых ресурсов (рис, 14.12),
На странице расположена таблица, строки которой соответствуют адресам сайтов, а столбцы —— пяти временным интервалам. Например, после попадания сайта в интернет-каталог можно узнать, как часто по этой ссылке приходят посетители, добавив алрес этого и нтер н ет- катал о га в систему PowerCounter. Это можно осуществить через страницу Добавление реферера, открывающуюся при нажатии на ссылку Добавить (рис. 14.13).
В поле ввода Реферер следует ввести адрес сайта, переходы с которого необходимо отслеживать, и нажать кнопку Добакить. Адрес сайта появится в таблице сайтов (см. рис. 14.12). Чтобы прекратить отслеживание переходов с какого-либо сайта —— его следует удалить из таблицы. Сделать это можно, нажав на ссылку Удалить в последнем столбце таблицы сайтов (см. рис. 14.12).
Рис. 14.12. Страница Ссылающиеся страницы
Разработка системы PowerCounter
Для построения системы потребуется несколько таблиц в базе данных MySQL. Так как необходим учет статистической информации для каждой страницы сайта, число которых не ограничено, необходима таблица pages, в которой будет храниться информация о каждой из страниц, участвующих в сборе статистической информации, Данная таблица будет состоять всего из двух полей:
口 id^page — первичный ключ таблицы, снабженный атрибутом дито_ INCREMENT;
name — относительный путь к странице.
SQL-оператор create, создающий данную таблицу, приведеи в листинге 14.1.
广“…二....w: ".*,*■*”•I 索J:JW**,T …'A1,, ” •…… …*…••>/.•■••竺,■:•”,………”,к…””:** г?」.,,,*,,,.,,> 屮:炉 .......^..r.ч r »t • »j>■ ■ •«
Листинг 14.1. Создание таблицы pages
CREATE TABLE pages (
id__page INT(IO) NOT NULL AOTO_INCREMENT, name TINYTEXT,
PRIMARY KEY (id_p&ge)
)TYPE=^yISAM;
Таблица pages может быть снабжена дополнительным целочисленным полем id_site на тот случай, если планируется использовать PowerCounter для сбора н анализа статистики посещений нескольких сайтов. В этом случае потребуется дополнительная таблица sites, первичным ключом которой будет выступать id_site.
Замечание )
Как уже говорилось в главе 4, при построении счетчика загружаемых с сервера файлов FileCounter подсчет хитов может производиться за счет увеличения одного числа при каждом обращении к ресурсу или же занесением новой записи в таблицу при каждом посещении. В первом случае экономится значительный объем памяти, необходимый для хранения всех записей^ во втором случае появляется дополнительная функциональность за счет предоставления динамики посещаемости во времени.
Следующая таблица, ip, предназначена для хранения хитов и имеет в своем составе семь полей:
id ip — первичный ключ таблицы, снабженный атрибутом autc_
INCREMENT;
口 ip ——поле для хранения IP-адреса посетителя;
putdate — время посещения;
id_page ——порядковый номер страницы, к которой относится заносимый в таблииу хит;
browsers ——поле типа enum, хранящее тип браузера посетителя;
systems —— поле типа нитом, хранящее тип операционной системы посетителя, или принадлежность к поисковой системе, если хит произведен роботом;
口 searches ——поле типа enum, хранящее информацию о поисковой системе, с которой на страницу idjage перешел посетитель.
SQL-оператор create, создающий данную таблицу, приведен в листинге 14.2.
J^J^r ・ft■守 Sf'y *•••■ " :,,,* 中、、Л У * .…:….…” •!,!■« ■■■ Jt»^;F;^;Y W・ry ”轉M ‘";♦紹""'У^ЧЦЙ5a吋《* "尸:/ V'W'V/y^ Y ¥ W* **,:,上:…丁 •■ у :Листинг 14.2* Создание таблицы ip
iftil..... V;二•ft.»JE^1!4Trt,wHnlWT^rarW1v^Tlfrt'.aAb-rt ; rft*.-.vr^7.\ 工;7京善鼻*6击“兴-片》:卉*审韜*扌门\¥./.力|?才4;7評£;¥/匾:.,,;.『%〉,;靠;;T. .<»Y.. Л;Й'»¥»лЫ: ;<;';¥:;■.;;■;; f; ;\;M『;
CREATE TABLE ip《
id_ip INT (11) NOT NULL AUTO二工NCREMENT, ip TINYTEXT NOT NULL『
putdate DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
id“age INT (20) NOT NULL DEFAULT ”1
browsers ENUM(1none L
*msie I 'opera*, Netscape*) NOT NULL DEFAULT 『none', systems ENUM (f none1,
Windows I
*unix*,
•macintosh1f 'robot_yandex*, •robot_google* # ■ robot__rambler', 'rotoot_aport') NOT NULL DEFAULT 'none I searches ENUM(гпопеT, 'yandex I 1google I 'rambler', 'aport I
*own_site') NOT NULL DEFAULT rnonefr PRIMARY KEY (idip)
)TYPE^MylSM;
Существуют две альтернативы для хранения IP-адресов: числовой формат и текстовый. I P-адреса посетителей в поле ip хранятся в текстовом формате, например, *'123.123.45.87**. С одной стороны, текстовый формат занимает больше места и времени на обработку при выборке, но с другой стороны, он позволяет не производить преобразования IP-адреса при выводе в окно браузера и хранить маски подсетей: п213.177.119.238/192* 168.3.Юб, 10.0*0.1П. При использовании типа int для поля ip посещения из больших сетей, использующих один 1 P-адрес для выхода в Интернет, воспринимались бы системой как посещения с одного хоста.
! Замечание )
Если производительность Web-приложения снижается до недопустимого уровня, одним из способов его увеличения является перевод IP-адресов из текстового формата в числовой.
Как видно из листинга 14.2, пале browsers может принимать четыре значения: 口 none — в том случае, если не предоставляется возможности определить тип браузера лосетитля;
口 msie — данное значение поле принимает, если в качестве браузера носе- титель использует Internet Explorer;
opera — соответствует Web-браузеру Opera;
netscape — данное значение относится к Web-браузеру Netscape.
( Замечание )
Значение для браузера Netscape приведено здесь скорее как дань традиции и для демонстрационных целей, так как в настоящий момент данный браузер практически не используется пользователями Интернета. В Российской Федерации его процентное отношение к другим браузерам составляет 0,2%.
〔Замечание )
Если в статистике необходимо учесть другие браузеры, поле browsers необходимо расширить значениями для новых браузеров.
Поле systems имеет двойное назначение: с одной стороны, оно хранит тип операционной системы посетителя, а с другой, если хит относится к роботу, оно определяет принадлежность к поисковой системе. Данное поле может принимать 8 значений:
口 none — данное значение поле принимает в том случае, если тип операционной системы или поискового робота не удовлетворяет остальным семи значениям;
口 windows ——данное значение поле принимает, если посетитель в качестве операционной системы использует Windows;
口 unix — данное поле соответствует иМХ-псщобным операционным системам (главным образом Linux);
口 macintosh — соответствует системам Macintosh;
robot^yandex — данное значение поля systems соответствует роботу поисковой системы Yandex;
robot_googie ——данное значение соответствует роботу поисковой системы Google;
口 robot_rambler — данное значение соответствует роботу поисковой системы Rambler;
口 robot_aport — данное значение соответствует роботу поисковой системы Aport
I 3 aMeмание)
Учет систем Macintosh тоже своеобразная традиция, так как посетителей, использующих данную систему для просмотра Web-ресурсов, еще меньше чем посетителей, использующих браузер Netscape. Процентное отношение данной операционной системы к другим составляет 0,1%.
Поле searches хранит информацию о том, является ли данный хит переходом со страницы поисковой системы, и может принимать 6 значений:
попе — поле searches принимает данное значение в том случае, если хнт не относится к переходу со страниц ни с одной учитываемой поисковой системы и не является переходом с сайта, для которого производится сбор статистической информации;
口 yandex — поле searches принимает это значение при переходе посетителя на сайт со страницы поисковой системы Yandex;
google — поле searches пpини^faeт данное значение при переходе со страниц поисковой системы Google;
口 rambler — поле searches принимает данное значение при переходе со страниц поисковой системы Rambler;
口 aport — поле searches принимает данное значение при переходе со страниц поисковой системы Aport;
口 own_site — поле searches принимает данное значение, если переход является переходом с собственных страниц сайта.
Поле searches в таблице ip необходимо для подсчета числа засчитанных и незасчитанных хитов. В системе PowerCounter к засчитанным будут относиться хиты, поле searches которых принимает первые пять значений из
приведенного выше списка. Если посетитель перешел с одной страницы сайта на другую, такой хит будет относиться к незасчитанным.
Замечание )
Как будет показано ниже, средства PHP позволяют определять так называемый реферер, строку, содержащую сетевой адрес ресурса, с которого посетитель пришел на данную страницу. Данная строка может быть пустой, если посетитель набирает адрес ресурса в строке браузера или посетителем является робот.
Помимо определения принадлежности хита к поисковым системам, система PowerCounier должна предоставлять информацию по ключевым словам, по которым ресурс был найден посетителем. Хранение такой информации будем осушествлять в таблице refferer, состоящей из семи полей:
id_refferer — первичный ключ таблицы, снабженный атрибутом AUTO_INCREMENT;
口 name ——реферер ——URL ресурса, с которого пр»1шел посетитель;
putdate — дата посещения;
ip — IP-адрес посетителя;
口 id paga — порядковый номер страницы, к которой относится заносимый в таблицу хит;
口 searches — поле типа enum, хранящее информацию о поисковой системе, с которой на страницу id_page перешел посетитель (поле аналогично полю searches таблицы ip).
SQL-опиратор create, создающий таблицу refferer, приведен в листинге 14.3,
Листинг 143 Создание таблицы refferer
CREATE TABLE refferer (
id^refferer INT(16) NOT NULL AUTO_INCREMENT# name TINYTEXT NOT NULL,
putdate DATETI№ NOT NULL DEFAULT *0000-00-00 00:00:00', ip TINYTEXT NOT NULL,
id“age 工 NT (8) NOT NULL DEFAULT 'U', searches ENUM{'noner,
'yaiidex I
,google I
1 rambler'『
■ aport I
1 own^site1) NOT NULL DEFAULT『none', PRIMARY KEY (id_refferer)
)TY?E=MyISAM;
Можно заметить, что таблицы refferer и ip денормализованьц так как поля таблицы refferer повторяют многие ноля таблицы ip. Денормализация, имеющая здесь место, требуется шя того, чтобы при работе с текстовыми значениям» рефереров избежать ресурсоемких многотабличных запросов. Разница между таблицами ip и refferer заключается в том, что в таблицу ip заносятся все хиты, а в таблицу refferer заносятся только те хиты, для которых строка с реферером не пустая и не является переходом с ресурса, для которого производится сбор статистики.
Внутри таблицы refferer информация также дублирована, во-первых, адрес, с которого посетитель перешел на ресурс, сохранен в поле name, во- вторых, переход с поисковых систем отмечается в поле searches. Здесь поле name необходимо для определения ключевых слов, по которым ресурс был найден в поисковой системе, а также для определения числа переходов с ресурсов, имеющих ссылку на сайт, для которого производится сбор статистики. Поле searches позволяет быстро подсчитать общее число переходов с поисковых систем. Поэтому, несмотря на то, что информация дублирована и. следовательно, требует лля своего хранения больше места, Web-приложение выигрывает в скорости работы.
Последняя таблица — links — храпит информацию о ресурсах, имеющих ссылку на сайт, для которого производится сбор статистической информации. Таблица имеет ива поля:
口 idelinks ——первичный ключ таблицы, снабженный атрибутом дито_ INCREMENT;
□ name ——сетевой адрес (или его часть) ресурса, где расположена ссылка.
SQL-оператор, создающий таблицу links, приведен в листинге 14.4.
*:;.♦;; >»< ♦儿““二.Л *7 “3:
CREATE TABLE links (
id_links SMALLINT(ll) NOT NULL AUTO_INCREMENT, name TINYTEXT,•
PRIT4ARY KEY (id_links)
)TYPE=MyISAM;
После создания базы данных системы PowerCounter можно переходить к ее разработке. До этого разработка всех Web-приложений начиналась с систе-
мы администрирования, здесь сначала будет рассмотрен сам счетчик, и лишь затем его система адм инистрирова н и я. Это связано с простым правилом: сначала разрабатыкается часть Web-приложения, позволяющая заполнить базу данных, а потом все остальное. Если в рассмотренных ранее Web- приложениях для заполнения базы данных требовалась система администрирования, то здесь за заполнение базы данных ответствен сам счетчик, а систем ма адм и н истрирован и я представляет результаты в удобном для анализа виде.
:Замечание )
Далее в листингах для удобства восприятия будет опущен несущественный HTML-код, связанный, в первую очередь, со стилевым оформлением Web- приложения. Полная версия данного Web-приложения находится на прилагаемом к книге компа*ст-диске, а также доступна по адресу http://www.softtime.ru/ info/downfoads.php. *
Система PowerCounter имеет в корневом каталоге два файла config.php н count.php и директорию admin, в которой сосредоточены файлы системы администрирования. Файл config.php (листинг 14.5) содержит конфигурационную информацию, и в отличие от ранее рассмотренных файлов подобного типа не содержит код соединения с базой данных, которое осуществляется в count.php.
.«:,:,■ у、:. *,‘j'^^^T’[:‘w: у*]" ^^:" "■ f*』、工?? ” *、,",^^ :i^^^『.^^^^w ・ I:: f,*,j11 ^^^^^^^* * *«fu’yT^^^^^f "+",*,4¥開$,[ * i*#
i Листинг 14.5. Файл config.php
tj.-rt 士忠:斯注'.;,„坏M••諦I、%玄0$汾*佇》爲噴 W訐•**.■.,.“. г.;; I .*“;£、『Л*%詳;+*e; ;;;;△. r..vu>«. л ■>>>■>>'•> Niimm*;."启虑G?;$m* 工W2『;;r轴需?^SvA*Lr*;;“.d
<?php
// Основные переменные
// Имя сервера базы данных, например,
// $dblocation = итуsql28»noweb.run
// сейчас выставлен сервер локальной машины
$dbl о cation = ** Localhost11;
// Иьдя базы данных на хостинге или локальной машине $dbname — "count";
// Pto пользователя базы данньо<
$dbuser = "root";
// и его пароль
$dbpasmwd = ”,
// Число выводимых в таблицах строк
// на главной странице и странице п1Р-адреса и хосты** $pnumber = 30;
// Способ определения IP-адреса посетителя
// 0 подходит для большинства хостингов, в том числе
563563
563
563
I/ для использования на локальной машине $obtip = 0;
ip-адрес посетителя не $REMOTE_ADDR, к НИМ ОТНОСИТСЯ,ip-адрес посетителя не $REMOTE_ADDR, к НИМ ОТНОСИТСЯ,// На некоторых хостингах // заносится в переменную 〃 например, www.nadex.ru // $obtip = 1;
ip-адрес посетителя не $REMOTE_ADDR, к НИМ ОТНОСИТСЯ,
ip-адрес посетителя не $REMOTE_ADDR, к НИМ ОТНОСИТСЯ,
// Текущая версия системы $version = ”2.0.7”;
Файл config.php содержит параметры для соединения с базой данных MySQL: сетевой адрес сервера (^dbiocation), имя базы данных ($dbname), имя пользователя ($dbuser) и его пароль (Sdbp&sswd). Кроме того, в данном файле можно установить число строк ($рпшпЬег) в таблицах системы администрирования и способ определения I Р-адерса ($obtip).
Замечанье )
В подавляющем большинстве случаев переменной $obtip следует передать значение 0. Если IP-адрес посетителя помещается в переменную нттр_ CLIENTIP, то данной переменной следует передать значение 1.
Последняя переменная файла config.php ($version) содержит текущую версию системы PowerCounter,
Файл count.php является движком счетчика, который осуществляет сбор информации с последующим занесением ее в базу данных (листинг 14.6). Включение страницы в систему сбора и анализа информации по посещениям осуществляется включением в тело страницы посредством оператора require_once скрипта count.php.
;Листинг 14.6, Файл count.php;Листинг 14.6, Файл count.php
;Листинг 14.6, Файл count.php
;Листинг 14.6, Файл count.php
<?php
include "config,php";
// Форьдируем строчку c ip
if ($obtip == 0) Sip = $_SERVERLREMOTE_ADDR」;// По умолчанию
if($obtip = 1) $ip = urldecode(getenv(HTTP_CLIENTIP)); // ww.nodex* ru
// Если.подсеть доступна, добавляем ее через слэш
$forward = getenv(HTTP _X_FORWARDED_FOR);
if (!empty($forward) & & ($forward !^ JSERVER[»REMOTE_ADDR ‘ J))
$ip - $ip. "/'* .$forward;
// Строка с реферером - URL страницы, с которой посетитель // пришел на сайт
$reff = gde8de($_5ERVER[”HTTJREFERER”]);
// Соединяемся с сервером базы данных
$dbcnx = 0mysql_connect($dblocation, Sdbuser, $dbpasswd); if($dbcnx) I
// Выбираем базу данных
if (@mysql_select__db ($dbname, $dbcnx))
{
// Для записи информации в базу данных необходимо выяснить
// первичный ключ записи в таблице pages, cooтветствующей текущей // странице.
$pgs = mysql_query("SELECT id_page FROM pages WHERE name= * $PHP_5ELF*n); if ($pgs) {
// Выясняем первичный ключ (id_page) те1олцей страницы
if (mysql_num_rows ($pgs) > 0) $id_page « mysql_result ($pgsF 0);
// Если данная страница отсутствует в таблице pages // и нм разу не учитывалась - добавляем ее в таблицу. else
$query = "INSERT INTO pages VALUES (0, '$PHP_SELF‘)”;
mysql_query($query);
// Выясняем первичный ключ только что добавленной // страницу
帛id^pagm = mysql_insert_id();
// Определяем строку USER_AGENT
$useragent = $_SERVER['HTTP_USERJGENT'];
$browser = 'none';
// Выясняем тип браузера посетителя
if (strpos (Suseragent, "MS工E”}1— false) $browser 工 ^ie»;
if(strpos($useragent, "Opera")!= false) $browser = 1 opera1;
if(strpos($useragent, ^Netscape*1}!== false) $browser 二 *netscape*;
// Выясняем тип операционной системы посетителя
$os = 1none1;
if(strpos($useragent, if(strpos($useragent, I I strpos($useragent, I I strpos($useragent, if(strpos($useragent,if(strpos($useragent, if(strpos($useragent, I I strpos($useragent, I I strpos($useragent, if(strpos($useragent,"Win») "Linux"') "Lynx”) "Unix")"Win») "Linux"') "Lynx”) "Unix")!= false)!= false !— false != false)!= false)!= false !— false != false),rMacintoshrt) != false),rMacintoshrt) != false)/ / Выясняем принадлежность к 〃 Поисковый робот Rambler if(substr($useragent, 0, 12)/ / Выясняем принадлежность к 〃 Поисковый робот Rambler if(substr($useragent, 0, 12)// Поисковый робот Google if(substr($useragent, 0, 9) // Поисковьй ро&от Yandex// Поисковый робот Google if(substr($useragent, 0, 9) // Поисковьй ро&от Yandex$os = 1windows1;$os = 1windows1;$os = 1 tmix1;$os = 'macintosh1;$os = 1 tmix1;$os = 'macintosh1;поисковым роботампоисковым роботам— HStackRambler")$os = 1 robot rambler*;— HStackRambler")$os = 1 robot rambler*;"Googlebot'1)"Googlebot'1)$os = 'robotgoogle';$os = 'robotgoogle';if (substr ($useragent, 0, 6) = "Yandex'1) 〃 Поисковый робот Aportif(substr($useragent, 0, 5) = "Aport") $search = 1 none1;if (substr ($useragent, 0, 6) = "Yandex'1) 〃 Поисковый робот Aportif(substr($useragent, 0, 5) = "Aport") $search = 1 none1;$os = 1 robot_yandexт;$os = 1 robot_yandexт;$os = 'robot_aport *;$os = 'robot_aport *;/ / Вменяем принадпеясность к/ / Вменяем принадпеясность кпоисковым системампоисковым системамif(stгроз($refff if(strpos($reff, if(strpos($reff, if(strpos($reffr if(strpos($reff /if(stгроз($refff if(strpos($reff, if(strpos($reff, if(strpos($reffr if(strpos($reff /"yandex'1)) "rambler1')) "google")) "aport'1))"yandex'1)) "rambler1')) "google")) "aport'1))$search $search $search $search$search $search $search $search'yandex1;” rambler';'google';'aport';'yandex1;” rambler';'google';'aport';$_SERVER ["SERVER NAME'1 ])) $search = ^wn^site1 ;$_SERVER ["SERVER NAME'1 ])) $search = ^wn^site1 ;
if(strpos($useragent, if(strpos($useragent, I I strpos($useragent, I I strpos($useragent, if(strpos($useragent,
if(strpos($useragent, if(strpos($useragent, I I strpos($useragent, I I strpos($useragent, if(strpos($useragent,
"Win») "Linux"') "Lynx”) "Unix")
"Win») "Linux"') "Lynx”) "Unix")
!= false)
!= false !— false != false)
!= false)
!= false !— false != false)
,rMacintoshrt) != false)
,rMacintoshrt) != false)
/ / Выясняем принадлежность к 〃 Поисковый робот Rambler if(substr($useragent, 0, 12)
/ / Выясняем принадлежность к 〃 Поисковый робот Rambler if(substr($useragent, 0, 12)
// Поисковый робот Google if(substr($useragent, 0, 9) // Поисковьй ро&от Yandex
// Поисковый робот Google if(substr($useragent, 0, 9) // Поисковьй ро&от Yandex
$os = 1windows1;
$os = 1windows1;
$os = 1 tmix1;
$os = 'macintosh1;
$os = 1 tmix1;
$os = 'macintosh1;
поисковым роботам
поисковым роботам
— HStackRambler")
$os = 1 robot rambler*;
— HStackRambler")
$os = 1 robot rambler*;
"Googlebot'1)
"Googlebot'1)
$os = 'robotgoogle';
$os = 'robotgoogle';
if (substr ($useragent, 0, 6) = "Yandex'1) 〃 Поисковый робот Aport
if(substr($useragent, 0, 5) = "Aport") $search = 1 none1;
if (substr ($useragent, 0, 6) = "Yandex'1) 〃 Поисковый робот Aport
if(substr($useragent, 0, 5) = "Aport") $search = 1 none1;
$os = 1 robot_yandexт;
$os = 1 robot_yandexт;
$os = 'robot_aport *;
$os = 'robot_aport *;
/ / Вменяем принадпеясность к
/ / Вменяем принадпеясность к
поисковым системам
поисковым системам
if(stгроз($refff if(strpos($reff, if(strpos($reff, if(strpos($reffr if(strpos($reff /
if(stгроз($refff if(strpos($reff, if(strpos($reff, if(strpos($reffr if(strpos($reff /
"yandex'1)) "rambler1')) "google")) "aport'1))
"yandex'1)) "rambler1')) "google")) "aport'1))
$search $search $search $search
$search $search $search $search
'yandex1;
” rambler';
'google';
'aport';
'yandex1;
” rambler';
'google';
'aport';
$_SERVER ["SERVER NAME'1 ])) $search = ^wn^site1 ;
$_SERVER ["SERVER NAME'1 ])) $search = ^wn^site1 ;
/ / Заносим всю собранную информацию в базу данных Squeryjnain = "INSERT INTO ip VALUES (
NULL,
fipl
W\0r
$id_page#
,「browser I '$051
1$search1)”;
@my宫ql_query ($query_n)ain);
// Если имеется реферер, заносим информацию о
// нем в отдельную таблицу
if (lempty ($reff) && $srch != "own_site'f)
(
$query_reff = r,INSERT INTO refferer VALUES ( NULL, '$reff‘,
19 Зак П8:
now(),
$id_page,
* $search*)”;
@mysql_query ($query__reff);
Как видно из листинга 14.6 скрипт начинается с формирования строки с IP-адресом, который размещается во временной переменной $ip. Данная переменная формируется из элемента супер глобального массива 帛_server и remotE—ADDR * ], к которому, если доступна маска подсети, добавляется через слэш содержимое переменной окружения нттp_x_forwarded_for. Во временную переменную $reff помещается значение реферера, извлекаемое из элемента $_server [ "http_refererh ], который предварительно обрабатывается функцией uridecode о. Это требуется для того, чтобы расшифровать в URL символы вида "%##'*, которые используются для передачи недопустимых в URL символов. Например, символ пробела заменяется символом %20. После применения функции uridecode о все такие конструкции будут заменены соответствующими символами.
После этого при помощи mysql__connect() осуществляется соединение с базой данных, если соединение проходит успешно, при помощи функции mysql_select_db ()выбирается база данных, в таблицы которой будет произведена запись хита.
Для размещения записей в таблицах ip и refferer необходимо знать значение первичного ключа записи таблицы pages (id_page), соответствующей текущей странице. Получить его можно при помощи SQL-запроса “select id_page FROM pages WHERE name^1 $PHP_SELFf Так как СКрИПТ COUOt.php включается в страницу, для которой производится сбор статистики посредством оператора requiгеопсе, глобальная переменная $php_self вернет имя текущей страницы, а не count.php. Далее, как и для счетчика загруженных файлов FiieCounter, рассмотренного в главе 4, возможны два случая: запись для страницы с таким именем уже существует в таблице pages, и SQL-запрос вернет значение первичного ключа, или подсчет хита для данной страницы производится впервые, и для данной страницы в таблице pages записей еще нет. В последнем случае при помощи SQL-запроса •'insert into pages values (0, '$PHP_SELFi 0) м запись будет добавлена в таблицу, а присвоенное ей значение первичного ключа вернет функция mysql_insert__id ().
После этого производится анализ строки, возвращаемой браузером клиента, который сервер помешает в элемент суперглобального массива $_server['http_user_agentt]. Из данной строки извлекается тип браузера и операционной системы посетителя, которые помещаются во временные переменные $browser и $os соответственно. Эти переменные используются далее для формирования SQL-операторов insert. Строка $_server [1 http__user__agent 1 ] также анализируется на принадлежность к роботам поисковых систем Yandex, Google, Rambler и Aport.
Переход со страниц поисковых систем фиксируется поиском их названий в строке $reff. Результат заносится во временную переменную ^search. Если реферер содержит фрагмент, совпадающий с адресом текущего ресурса ($ _server「serveR—Name”]), переменная $search получает значение ”o*n_site\ а хит получает статус незасчитанного.
В конце скрипта в переменных $query_main и $query_reff формируются SQL-операторы insert, которые передаются в качестве аргумента функции mysqi_query()t в результате чего в таблицах ip и refferer появляются новые записи. Запись в таблицу refferer добавляется лишь в том случае, если переменная $reff, содержащая реферер, не является пустой и не принадлежит переходу посетителя с одной страницы сайта на другую.
Далее будет рассмотрена система администрирования PowerCounter, файлы которой расположены в отдельной директории admin и имеют следующие назначения:
口 addreffererfbrm.php ——HTML-форма для добавления в систему адресов страниц, на которых расположены ссылки на сайт;
口 addrefferer.php — обработчик формы addreffererfbrriLphp;
addresses.php —— файл, ответственный за формирование страницы '1Р- адресаи;
I clear_bd.php — скрипт "обнуления0 счетчика, уничтожает все записи в таблицах;
config.php — скрипт соединения с базой данных счетчика;
口 days.php — страница, отображаюшая динамику распределения хостов, общее и засчитанное количество хитов по дням;
delpage.php — скрипт удаления страницы из системы сбора статистики;
delrefferer.php ——скрипт удаления адреса страницы, на которой расположена ссылка на сайт;
口 hours.php —— страница, отображающая динамику распределения хостов, общее н засчитанное количество хитов в течение суток;
index.php — главная страница системы администрирования;
ip.php — скрипт, предоставляющий информацию о географическом положении и лице, на которого зарегистрирован IP-адрес (подробно рассмотрен в главе 书;
口 main.php ——файл, ответственный за формирование страницы Хиты и хосты;
menu.php — меню системы администрирования;
refferer.php — файл, ответственный за формирование страницы Ссылки;
口 robots.php —— файл, ответственный за формирование страницы Поисковые роботы;
口 searchquery.php —— файл, ответственный за формирование страницы Поисковые запросы;
口 searchdays.php ——страница, отображающая динамику распределения числа запросов с поисковых систем по дням;
口 searchhours.php — страница, отображающая динамику распределения числа запросов с поисковых систем в течение суток;
口 systems.php 一 файл, ответственный за формирование страницы Системы и браузеры;
口 timejntei-val.php —— вспомогательный файл, в котором хранятся границы временных интервалов, за которые выводится статистическая информация. По умолчанию выставлены следующие временные интервалы: "Сегодня**, "Вчера", н3а 7 дней”,"За 30 дней1' и "За все время";
口 topcounter.php — "шапка'* страниц системы ад ми нистри рован ия;
utils.php ——вспомогательный файл, в котором расположены общие функции, используемые скриптами системы администрирования.
Так как файл config.php, рассмотренный выше в листинге 14.5, не устанавливает соединение с базой данных, а содержит лишь конфигурационную информацию, в системе алминистрирования используется свой файл config.php, который на основании этой информации осуществляет соединение (листинг 14.7)*
.…* ■,臂空f У "V УН :£{ £ ** у Г у :* :‘1 瑞产•*立3tf方M】W* ^^|*”;¥《y^gjj^^y^¥~^11y■.j^^y j^Stj^j^叶,<^y^^5|認.^t# '长**tyj 界闇产券学辻"上E^tf 丁 ■ M4^*y.rM*y~WUP j Листинг 14.7. Файл conftg.php
S**・*、・'u*>r**; *、•***“♦<!;$,*••/・、、*”»*,*,;A,,*,A**;****r***f**・・*#・*・・《jt・ct*・・・・・*»*』・h*^*,*K****』*«・*・,・《・*,《齐r*afgt*V«*"**«・*4>*・*”g**W・*?*・****y*******・*v***”*•告"•.f,・r・rr*F・>i **** + +,
<?php
include T* * * /config. phpM;
// Устанавливаем соединение с базой данных
$dbcnx = @mysql_connect($ctblocation,$dbuser# $dbpasswd);
if (!$dbcnx) {
echo "<P>B настоящий момент сервер базы данных не доступен г поэтому корректное отображение страницы невозможно.</Р> ц;
exit ();
}
// Выбираем базу данных
if (! @mysqJ__s曰lect_db ($dbname, $dbcnx) ) {
echo "<P>B настоящий момент база данных не доступна, поэтому корректное отображение страницы невозможно * </Р>и;
exit ();
}
// Небольшая вспомогательная функция, выводящая сообщение
//в окно браузера и остана вливающая выполнение скрипта
function puterror($msg)
{
echo M <p>$msg</p> °;
echo n<b>Error: ”.mysql_error 0."<b>";
exit ();
Главная страница администрирования выводит список файлов, участвующих в системе сбора статистики (см. рис. 14.9). За ее формирование ответствен файл index.php, содержимое которого представлено в листинге 14,8.
??,.,".{**',<V'?,1,-SJ?^JAl,,!;,.,,.4,'M'"*V''',"^i*S**V*f!V**"V-'!,??,.,".{**',<V'?,1,-SJ?^JAl,,!;,.,,.4,'M'"*V''',"^i*S**V*f!V**"V-'!,? Листинг 14.8* Файл Index.php.,*Х .“;•;;;•;*,.* iVi \°»'Л iVeV* iVri ft *$♦*.*",;;;■ ; 3 3 ♦ •、“、Г* ” * /? Листинг 14.8* Файл Index.php.,*Х .“;•;;;•;*,.* iVi \°»'Л iVeV* iVri ft *$♦*.*",;;;■ ; 3 3 ♦ •、“、Г* ” * /''.;,■」- 1 敲:二 Wh ■■'■ *3; * *%Y/Xr.. *'“”;***〃 r;X**?“ ♦[.***方;” “・―,宮;X#7ir •+* ♦, .“*扌-工''.;,■」- 1 敲:二 Wh ■■'■ *3; * *%Y/Xr.. *'“”;***〃 r;X**?“ ♦[.***方;” “・―,宮;X#7ir •+* ♦, .“*扌-工
??,.,".{**',<V'?,1,-SJ?^JAl,,!;,.,,.4,'M'"*V''',"^i*S**V*f!V**"V-'!,
??,.,".{**',<V'?,1,-SJ?^JAl,,!;,.,,.4,'M'"*V''',"^i*S**V*f!V**"V-'!,
? Листинг 14.8* Файл Index.php
.,*Х .“;•;;;•;*,.* iVi \°»'Л iVeV* iVri ft *$♦*.*",;;;■ ; 3 3 ♦ •、“、Г* ” * /
? Листинг 14.8* Файл Index.php
.,*Х .“;•;;;•;*,.* iVi \°»'Л iVeV* iVri ft *$♦*.*",;;;■ ; 3 3 ♦ •、“、Г* ” * /
''.;,■」- 1 敲:二 W
h ■■'■ *3; * *%Y/Xr.. *'“”;***〃 r;X**?“ ♦[.***方;” “・―,宮;X#7ir •+* ♦, .“*扌-工
''.;,■」- 1 敲:二 W
h ■■'■ *3; * *%Y/Xr.. *'“”;***〃 r;X**?“ ♦[.***方;” “・―,宮;X#7ir •+* ♦, .“*扌-工
<?php
// Данные переменные определяют название странида и текст-подсказку.
$title = 'Статистика посещений по страницам сайта';
$pageinfo = 'Ниже перечислены страниц^!, которые участвуют в статистике. По гиперссылкам можно получить детальную статистику по каадой отдельной странице *;
// Включаем заголовок страницы include ”topcounter.php”;
// Извлекаем из строки запроса значение параметра раде —
// номера страницы.
if (errpty ($_GET Г page * ])) $раде ^ 1;
else $раде = $_GET[1page1];
// Начало отсчета, данная переменная используется для формирования
// условия LIMIT в SQL^sanpoce, помещенного в переменную $query_hits
$ begin ^ ($page - 1) *$рптлпЬег;
// Определяем порядок сортировки таблицу страниц
if (eirpty ($_GET [ * order1 ])) $orderstr = "name";
else $orderstr 二"num DESC” ;
// Определяем общее число страниц, участвующих в статистике; если
// это число больше $pnuniber, будут выведены только первые $pnumber
/ / страниц, а на другие страницы будут выведены ссылки
$num = mysql__query (нSELECT count (*} FROM pagesм);
if(!$num) puterror("Ошибка при обращении к таблице страниц");
// Выводам ссылки на другие страницы
$total = mysql^result ($num, 0);
$пш\Ьег = (int) ($ total / $pnumber);
if{(float)($total/$pnumber) - $number 1= 0) $number++;
for($i = 1; $i <= $number; $i++)
{
if ($number = $i)
{
if($page — $i)
echo ”[”・<($i - 1) *$pnumber + 1) . n-$total]11;
else
echo "<a href*= index. php?
page=M *$i・ “ordem" ♦ border."> [M.
(($i - 1)*$pnumber + 1).H-$total1</а>и;
else
{
if($page = $i)
echo ”[".({$i - 1) *$pnuinber + 1) . н-г*. $i*$pnumber J*] *';
else
echo **<a href=index. php?
pagei. $i. "&Qz:dar=" ♦ $ order ・">["・
(($i - 1) *$pnumber + 1) . **-**.$1*$рп1Ш1Ьег. H]</a>M; )
}
// Выводам таблицу с адресами страниц, участвующих в сборе
// статистикиf и общее количество хитов для каждой из
// страниц.
?>
<table><tr><td>
<table>
<tr>
<td>
<а href=index.php?page=<? echo $page ?>
title="Сортировать таблицу по имени страницп>Страница
</а>
</td>
<td>
<а hrefsindex < php?page=<? echo $page ?>Аогдег=*1
titles,,CopTMpoBaTb таблицу по количеству посещенийri> Количество посещений
</a>
</td>
<td^ei^cTBMe</td>
</tr>
<?php
// Формируем и выполняем SQL-запрос, извлекающий из таблиц ip и pages / / количество хитов для каждой из страниц за все время сбора // статистики
$query_hits = "SELECT ip.id_pagef
pages.name, count(ip.id_ip) AS num FROM ip, pages
WHERE ip, id__page = pages. id_page
GROUP BY ip.id^page
ORDER BY $orderstr
LIMIT $begin, $pnumberH;
$pgs = mysql_query ($query_hits);
if(!$pgs) puterror("Ошибка при обращении к таблице страниц");
while ($page = mysql_fetch__array ($pgs))
{
echo "vtr>
<td><a href=main*php?id_page=" .Spageridjage1 ]・">
http://".$_SERVER["SERVER_NAME"]・ $page[*name'].M</a>
</td>
<td>" • $page「num1 ]. "</td>
<td><a href^delpage, php?id_page=M • $page [' id__page' ] J title®1 Удаление устаревших (мертвых) страниц из системы подсчета1>Удалить </а>
</td>
?>
</table>
</td><td>
<р с1азз=Ье1р>Для получения статистики только по выбранной странице щелкните на ее имени в таблице. Если страница не будет выбрана, то статистика будет представлена для <а href-=main. php>Bcero сайта</а>.
</р>
</td></tr></table>
<?php
echo "<p><a href^clear_bd.рЬр>Очистить базу данных</а></р>м;
?>
Как видно из листинга 14.8, файл index.php начинается с включения Ишалки" страницы topcounteEphp (см. листинг 14.9), которая выводит верхнюю часть страницы и устанавливает соединение с базой данных.
Так как в таблице выводится только ограниченное количество строк, которое определяется переменной $pnumber из config.php (см. листинг 14.5), то формируются ссылки на другие страницы. Для этого из таблицы pages извлекается общее число страниц, участвующих в сборе статистической информации, которое помещается в переменную stotai.
После вывода ссылок на другие страницы формируются заголовок н тело таблицы с адресами страниц и соответствующим им числом хитов. Заголовок таблицы содержит две ссылки: Страница н Количество посещений, переход по которым позволяет отсортировать содержимое таблицы либо по адресу, либо по числу посещений. Это достигается присвоением временной переменной $orderstr, используемой далее для формирования SQL-запроса на выборку, значений ^паше" и т num desc” соответственно.
:Заменанме )
базы данных на тот случай, если страница выводится из системы сбора информации.
Завершает файл ссылка на скрипт clear_bd.php, производящий обнуление счетчика.
Каждый файл, выводяший информацию в окно браузера, содержит в своем составе оператор include, включающий "шапку” страницы topcounter.php. Рассмотрим этот файл подробнее (листинг 14*9).
:Листинг 14.9. Файл topcounter.php
<html>
<head>
<title><?php echo $title; ?></title>
<?php
// Уста на вливаем соединение с базой данных requ.ire__once {"config.php");
// Извлекаем из строки запроса значение параметра раде
if(isset($_GET[*id_page,])) $id_page = $_GET[1id_page1];
else $idjpage ="”;
// Извлекаем название текущей страницы из таблицы pages
if (! eirpty ($ id__page))
(
$ query = "SELECT name FROM pages WHERE id__page = $idqg£”;
$pgs = mysql_query ($query); if($рдз)
while ($page = mysql_fetch_array ($pgs))
$namepage = "http :〃”. $_SERVER「SERVER_NAME”] .$page「name'];
}
} else $namepage:=1 Весь сайт1;
?>
</head>
<body>
<?php
// Выводим дату начала регистрации данных и число прошедших с начала
// регистрации дней
$dat = mysql query (^SELECT UNIX_TUMESTAMP (MW (putdate)) AS data
FROM ipr,);
if ($dat)
$date = mysql_fetch_array ($dat);
echo "Дата начала регистрации данных:<b>
". date {"Y-m-d — $date「data *]) .M</b>n;
printf (11 Прошло: <b> %d </Ь>днейп, ((time () -$date [ Mata1 ]) /3600/24));
} else put error (г,Сйпибка при обращении к таблице IP-адресов. .. и);
echo "<а href='index.php1 title=*Главная страница счетчика * > Главная страница счетчика</Ьх/а>и;
if (!empty($page)) echo и<а href«main.php> Статистика для всего сайта</а>и;
echo "</р>";
include "menu.php“;
?>
<hlx?php echo Stitle ?></hl>
<p><?php echo $pa^feinfo ?></p>
Если странице, в которую включен файл topcounter.php, в качестве параметра передано значение первичного ключа страницы id_page, то ее название извлекается из таблицы pages и выводится в окно браузера. Если данный параметр не установлен, выводится надпись о том, что информация отображается для всего сайта в целом. После этого скрипт выводит дату начала сбора статистической информации, для чего к таблице ip осуществляется запрос, извлекающий запись с минимальным значением поля putdate, что соответствует первой записи в таблице. Полученное время (в секундах с I января 1970 года) преобразуется при помощи функции printf и выводится в окно браузера.
Завершает файл topcounter.php включение файла menu.php (листинг 14.10), содержащего меню системы администрирования.
^^к 黨 y岁哪 “ ^:v•,(•^r',^■■■■v^•""曾中‘整二*мA 产iv^**Hpg|^^N::二・ r “门罩q^w 皆'芳投r&之啰ум1学里峭№色¥斐等y:g :Листинг 14.10. Файл menu.php
<a href=main.php?id_page^<?php echo $id_page; ?»Хосты и хиты</а>
<a href=systerns .php?id__page=<?php echo $id__page; ?»Системы и браузеры</а>
<a href=addresses.php?id_page=<?php echo $id_page; ?»IP адреса</а>
<a href=robots.php?id_page=<?php echo $idjpage; ?»Поисковые роботы</а>
<a href=searchquery. php? id_j>ages=<?& $idjpage; ?»Поисковые эапросы</а>
<a href=refferer *php?id_page=<?php echo $id_page; ?»Ссылки</а>
будет предоставляться только по странице сайта с данным первичным клю- ном, если этот параметр пуст — для всего сайта в целом.
На главной странице (см. листинг 14.8) напротив каждой из страниц имеется ссылка на файл delpage.php, позволяющий удалить стран и цу из системы статистики. Это необходимо в том случае, если ранее участвовавшая в сборе статистики страница удалена с сайта, и необходимо удалить информацию о ней из базы данных PowerCounter. Содержимое файла delpage,php представлено в листинге 14.11.
^^jj^^^ ISiSfifil^MjftMfii^fty^1。-': *f '•”¥'""*,.寧3:卄身,理噸出班陶gw^^/^^y^^yj/翼产屮:凸:密抬/в. w〕・ м« ••• м* 力]單:j中 /."#聾±M+Y、****:*.:::幫桀* ”二 у” ” ”,,:£[ Листинг 14.11. Файл delpage.php
* ・Х『С« лг»«П^#4г««Л>»«'*г«:кТ{ъ*4«'№ГА««ГЛ.гЛ >n;/r>ir4nv->*^ii>*^ 'Bjnt-'t «f ■ ;i ■ Ji;» rt i¥> «V» « * V a 1 ■ > Vi I * 4 f»»i • ^«'< t. » Д /a nt in: ;. r.vH. ■■■Ill (VlfK^a Л А?;*;、k i if (;i7<rV> v«v« !
<?php
// Скрипт удаления страницы из системы статистики
include ''config.php";
// Удаляем записи из таблиц ip и refferer
$delete_ip = “DELETE FROM ip
WHERE id_page = 11. $_GET [ * id_j)age •];
$delete_refferer = "DELETE FROM refferer
WHERE id_page ^ ”•$_GET[1id_page'];
if (mysql_query ($delete_ip) && mysql_query ($delete_ref ferer))
{
// Удаляем запись из таблиц pages
$delete_pages = "DELETE FROM pages
WHERE id_page = n.$_GET[1idjpage1];
if (mysql_query($delete_pages))
{
echo "<HTML><HEAD>
<META HTTP-EQUIV=1 Refresh1 8NTENT='O; URl^index.php*> v/HEADX/HTML>";
)else puterror ( "Ошибка при удалении информацииf,);
} else puterror ("Ошибка при удалении информации");
?>
Обнуление счетчика производится полной очисткой таблиц ip, refferer и pages, осуществляемой скриптом, который расположен в файле clear_bd.php (листинг 14.12).
> ;,岳.雷寻")欣,点■在■ 石:> ;,岳.雷寻")欣,点■在■ 石:»^^^^^№1^^ш^мш^ ц^1yllчд^цд^уд^^у, *^1 ^** *• ^^^驚土 ■** ^^^ДД^^Д^^^^^^^^^^^^^Д^.,? W*j!号肾,^JVV,屮修£,塁51 注《*у* *:工;*?*?聋?踹g£營岑^^^g :Листинг 14.12. Файл clear_bd.php
> ;,岳.雷寻")欣,点■在■ 石:
> ;,岳.雷寻")欣,点■在■ 石:
<?php
// Устанавливаем соединение с базой данных
include f,config.phpM;
/ / Формируем SQL-запросы на очистку таблиц базы данных
$query_ip = “DELETE FROM ip”;
$query_refferer = "DELETE FROM refferer";
$query_pages = "DELETE FROM pages";
// Выполняем SQL-запросы
if(mysql_query($query_ip) &&
mysql_query($ querypages) & & mysql_query($query_refferer)) {
// Осуществляем а в тоьза тиче ский переход на главную страницу
// адаинистрирования
echo "<HTML><HEAD>
<META HTTP-EQUIV= Refresh1 CONTENT=*0; URL-index.php*> </HEAD></HTML>f,;
} ?>
Далее будут рассмотрены сервисные страницы системы PowerCounter, ссылки на которые расположены в файле menu.php (см. листинг 14.10), За формирование первой страницы Хиты и хосты (см. рис. 14.1) несет ответственность файл main.php, код которого приведен в листинге 14.13.
第au夕^&^&‘肆.:岁 " 711УЛ,уу "л*1, "",,",金 步二牡 У *,*%h 寸經甘',驾'.热йЙ3■号专步号 *”;,*.: •? " уt 需:丝比 w#w: "*^U*a最,* 立沙 zmw ."号卅閒1史学*岂 :Листинг 14.13. Файл main.php^L
<?php
// Шапка страницы
require__once (0topcounter. phpn);
// Включаем массив временных интервалов
require__once (fltime_interval. phpn);
// Библиотека функций системы PowerCounter
require__once ("utils .php");
// Формируем три массива с хостаъли ($host), засчитанными хитами <$hit) и
// общим числом хитов ($total) по 5 временным интервалам, определенным
// в файле time_interva1.php for($1=0; $i<5; $i++)
list($host[$i], $hit[$ib
$total[$i])二 show_ip_host($time[$i][rbegin1], $time[$i][* end1], $id_jsage);
} ?> <table> <tr>
<td> </td>
<td><a href=1hours.php?day=O&id_page=<?= $id_pags;?>,>Сегодня</a></td> vtdxa href= * hours,php?day=l&id_page-<?= $id_page;?>•>B4epa</a></td> <td><a href^1 days. php?id_page=<?« $id__page; ?>1 >3a 7 даей</а></Ь(1> vtdxa hr£f='day3.php?id“age=<?= $id_page; ?>&daystst=30f>
3a 30 flHew</ax/td> <td>3a все epeMH</td>
</tr>
<tr>
<td>Xo сты< / td>
<?php
fоreach($host as $value) echo и <td>$value</1d>n;
?>
</tr>
<tr>
<td>Засчитанные xwTbi</td>
<?php
foreach ($hit as $value) echo l'<td>$vaiue</td>M;
?>
</tr>
<tr>
<td>Bce xHThi</td>
<?php
foreach ($total as $ value) echo M<td>$value</td>r,;
?>
</tr>
</table>
Файл main.php начинается с включения ''шапкн” страницы ——topcounter.php, файла time_intervaLphp (листинг 14.14), где объявлен двумерный массив $time, хранящий границы временных интервалов, для которых следует выводить статистическую информацию, а также файла utils.php, в котором со-
средоточены общие функции системы. В частности, в файле utils.php находится функция show_ip_host {)» используемая для подсчета хостов, общего и засчитанного числа хнтов.
Jj^/r懐幫出^!^&'^^**^^紫翊世算&¥"」以."мч»("鶴Dx4^^^j^ytr]”/ ч*! ^«.^',, • - "w 丫聲畢"м2::¥";?:,[**弋瑞*i*y *二T 乃号*:т,y*+**:e[*:.." r 叶*2?'"卦工¥卓 ;Листинг 14Л4 Файл timejnterval.php
iWTT7?l№ft^j£r.$dG」.; + .*%**“.r**.;“、***卞;“* “:,i*. Г,....%;;;..**〉;就^Ali Гт* ;“”・”.q*|^i».;\*.ii,**Y**i;?;『/'|1;, „;«;、;*■**、;.丿W.**,,,.',*I,*«;.*;;,“““Yh ;;.}..、*.'.,“.爲“*騎./
<?php
// Массив границ временных интервалов
// 11 Се го дня11
$time[Q] ['begin1] = 1;
$time [0][^end1] = 0;
// "Виера"
$time[l][Tbegin1] = 2;
$time [1 ] JencT ] = 1;
// "3a 7 дней"
$time[2] fbegin1] = 7;
$time[2][1 end1] = 0;
// "3a 30 даейи
$time[3][1begin1] = 30;
$time[3] Lend']二 0;
// 11 За все время'1
$time[4][rbeginr1 = 0;
$time[4] [^nd*] = 0;
Файл timejnterval.php содержит смешанный двумерный массив $time, первый индекс которого является числовым, а второй — ассоциативным. Значение первого индекса изменяется от 0 до 4, определяя границы иля пяти временных интервалов: *'Сегодням, "Виера”,"За 7 дней**, "За 30 дней** и "За все время*'. Второй индекс принимает два значения: begin и end для начала и конца интервала соответственно. Значения begin к end —— это не календарная дата, а количество суток, прошедших до настоящего момента (число дней, которое необходимо вычесть из текущей даты). Поэтому в пределах каждого из интервалов значение элемента begin должно быть больше или равно значению элемента end. Так, для интервала “Вчера” начало ($time [1] [ ^egin1 ]) соответствует дате, которая была два дня назад, а конец ($time[i] [•end* ]) интервала — дате, бывшей один день назад (см* рис. 14.1). На рис. 14.14 цифрами внизу указано, сколько дней необходимо вычесть из текущей даты, чтобы получить значение временного интервала.
Текущаядата
Количество дней назад О
Рис. 14.14. Границы временных интервалов
I Замечание )
Значение 0 элемента begin массива $time интерпретируется немного иначе, оно соответствует самому первому дню сбора статистической информации, а не текущему моменту, как для элемента end.
Границы временных интервалов, заданных в файле timejntei^val.php, используются в качестве аргумента функции show_ip_host о, которая принимает три параметра: $begin, $end и $id_page, содержащих начало, конец временного интервала и первичный ключ страницы, для которой производится подсчет. Функция возвращает массив, состоящий из трех элементов, соответствующих числу хостов, засчитанному, и общему числу хитов.
С Замечание )
Переменная $id_page инициализируется в topcounter.php, где ей присваивается значение параметра id_page, извлеченное из строки запроса.
В файле main.php функция show_ip_host () вызывается в цикле for для всех пяти временных интервалов. Результаты работы функции сохраняются в трех массивах: $host, $total и $hit — для хостов, общего и засчитанного числа хитов соответственно. Далее в файле main.php происходит вывод результатов работы функции, который оформляется в виде HTML-страницы, столбцы которой соответствуют временным интервалам, а строки — хитам и хостам. Строки формируются в цикле foreach, который пробегает массивы $host, $total И $hit.
Рассмотрим подробнее функцию show_ip_host(), код которой приведен в листинге 14Л5.
««шуи1^ше|му'^у^е*ш«Ш1^ь,Л,Щ?44Г,г^у"^е V','ti""','*ve-f=,'*',,"j,№?v*,.^-,'Vf'*^/,*^*'r:to*"Ji.MtV V;,*4,fMl'1^,y***4*V-*#-***
:Листинг 14.15. Функция show^ip^host
}^№4^ТняС^йпч^а^^'^> a/V^Vivi di/ftC/jrJ"; ...;二《上'&*;:*:• *△ ■,*;;匕":'黄、'*寸;:+*|>;+% ;.«,?.<Vi5;域 »<-;>• •i¥i^7.<4>«'» Ji'f'Xa 4 * * * iinViJil ■ A'-i>'»'i «Л'-'/Ь > imK?«'^!'■ ■ »■» »*• ■'.•»■ » »1»i'• 'i 而令髭、Н“;、"Т:
<?php
// функция возвращает массив из трех переменных:
// количество хостов,
// количество засчитанных хитов и // общее количества хитов.
// $begin - число дней, которое необхожмо вычесть из текущей даты,
// для того чтобы получить начальную точку временного интервала
// $end - число дней, которое необходимо вычесть из текущей даты,
// для того чтобы получить конечную точку временного интервала
〃 $id_page - первичный ключ записи таблицы pages,
// соответствующей странице сайта
function show !p host($beginr $endr $id_page)
// Эта переменная (id_page) определяет, осуществляется ли запрос к
// конкретной странице или ко всему сайту.
if (empty (Sid page)) $tmp_page = *'";
else $tmp_page = ” AND id_page=$ id^_page**;
// Запрос на статистику за временной интервал, определиемый
// параметрами $begin, $end
$tmpl = "putdate < DATE^FORMAT(NOW(),1%¥-%m-ld 23:59:59’} 一INTERVAL fend’ DAY";
if ($begin 工 0) $tmp2 ="”;
else $tmp2 = ” AND putdate >= DATE_nDRMAT(NOW()r f%Y-%m-%d 23:59:59’)- INTERVAL 1$begiП1 DAY”;
$end = $tmpl.$tmp2.$tmp_page;
// Общее число хитов
$query_total = "SELECT COUNT(*) FRCM ip WHERE n.$end;
// Засчитанные хиты
$query_hit = "SELECT COUNT{*) FROM ip
WHERE searches != 1 own site* AND °.$end;
// Подсчитываем число IP-адресов (хостов)
$quer^host = nSELECT COUNT (DISTINCT ip) FROM ip WHERE "・$end;
// Осуществляем запросы к базе данных
$tot = mysql^query($query_total);
$pht = mysql_query ($query__h.it);
$hst = mysql_query {$query_host); if($tot && $pht && $hst)
// Если запросы выполнены удачноr
// получаем результат
$totalhist ^ mysql_result($totf 0);
$hits= mysql_result ($phtz 0};
$hosts= mysql_resu.lt ($hstf 0);
return array($hosts, $hits, $totalhist);
}
else puterror ("Ошибка при обращении к таблице 1Р-адресовп);
В начале функции происходит формирование огранмннваюгцнх условий в SQL-запросах, которые размещаются в трех временных переменных: $tmp_page, $tmpl и $tmp2. Если параметр $id_page содержит не пустое значение, переменная $tmp_page получает значение" AND id_page^$id_pageH, которое ограничивает выборку в SQL-запросе записями, которые относятся к странице сайта с данным значением первичного ключа. Во временные переменные $tmpi и $tmp2 помещаются условия по полю put date, хранящему время обращения посетителе!.
!Замечание )
Условия по полю putdate составлены таким образом, чтобы временные мн- тервалы начинались и заканчивались в 00:00 часов.
Сформи1Х)ванные условия группируются во временную переменную $end, которая используется далее для формирования SQL-запросов. Подсчет общего числа хитов сводится к подсчету числа строк, удовлетворяюших условиям $end, при помоши встроенной функции MySQL count (). При подсчете засчитанных хитов к условиям во временной переменной $end добавляется условие "searches != 'own_site'”, позволяющее исключить хиты, полученные в результате перехода со страниц сайта, для которого осуществляется сбор статистической информации. Для подсчета числа хостов используется конструкция "count(distinct ip)". Здесь ключевое слово distinct требует, чтобы все значения поля ip были уникальными* SQL-запросы на выборку хостов, общего и засчитанного числа хитов помещаются в переменных $query_host, $query_totai и $query_hit соответственно. После чего каждая из них передается в качестве аргумента функции mysqi_query о, и в случае успешного выполнения результат, полученный при помощи функции mysqi_resuit(), возвращается в виде трехчленного массива.
Временные интервалы в заголовке таблицы, формируемой при помощи скрипта niain.php, являются гиперссылками. Переход по ссылкам Сегодня и Виера позволяет получить распределение хитов и хостов по часам в течение суток, а по ссылкам За 7 дней н За 30 дней ——распределение хитов и хостов по дням в течение недели н месяца (см. рис. 14.3). За предоставление информации по распределению хитов и хостов по дням ответствен файл days.php, содержимое которого представлено в листинге 14.16.
^fflj|^a^yi^a^^g|^y^a:K* ^Т'^УЖУте^!^1^Ч*^^••■• “ "第璽学у »о,喈:窮臘聲'岁斗堂 УХ^УУ^^^^^^Ж^
Пнстмнг 14.16. Файл days.php
<?php
// Если число дней не передано, считаем, что вывод
// осуществляется за 7 дней
if(empty($_GET[^aystst1])) $_GET[Maystst*] =7;
// Шапка страницы
include "topcounter.phpn;
// Библиотека функций
require_once("utils.php");
?>
<table>
<tr>
<td> </td>
<td>XwTbi< / td>
<td>3aсчитанные xMTbi</td>
<td>XocTH</td>
</tr>
<?php
// Формируем три значения с хостами ($host), засчитанньм! хитами ($hit) и
// общим числом хитов ($total) по 5 временным интервалам, определенным
// в файле time_interval* php
for{$i=0; $i<$_GET[,daystsf]; $i++) {
list($host, Shit, $total) = show_ip_host($i+1r $i, $_GET[,id_page*]);
echo "<tr>
<tdxa href= * hours . php?day=$i&
idjpag卞”.$_GET [1 id_page,]."■>
$i ди. наэад</а>
</td>
<td>$total</td>
<td>$hit</td>
<td>$host</td>
</tr>°;
echo **</tablesn;
?>
Файл days.php принимает два параметра id」page и daystst. Первый параметр передается всем страницам системы и был рассмотрен выше. Второй параметр ——daystst ——сообщает скрипту days.php, за какое число дней необходимо вывести таблицу с распределением числа хостов и хитов.
Замечанье )
По умолчанию параметр daystst принимает значение, равное 7, что соответствует недельному интервалу.
Вывод таблицы осуществляется в цикле for, пробегающем значения от 0 до daystst. Для вычисления числа хостов, общего числа хитов и засчитанных хитов используется рассмотренная выше функция show_ip_host () (см. листинг 14.15). Здесь границы временных интервалов берутся не из массива $time, а вычисляются непосредственно в иикле.
、戈::理 ^"■V^aaffl^W""'"'室探理翩開整""海?身 建皆?峭?挚,財炉阻礬、戈::理 ^"■V^aaffl^W""'"'室探理翩開整""海?身 建皆?峭?挚,財炉阻礬В первом столбце таблицы для каждого дня выводится ссылка, переход по которой позволяет получить иннамнку распределения хитов и хостов в течение данных суток (рис. 14.2). За представление данной информации несет ответственность файл houi-s.php, код которого представлен в листинге 14.17*
、戈::理 ^"■V^aaffl^W""'"'室探理翩開整""海?身 建皆?峭?挚,財炉阻礬
、戈::理 ^"■V^aaffl^W""'"'室探理翩開整""海?身 建皆?峭?挚,財炉阻礬
¥嘴您潜飞案肾■■沟汐然中.哪$跡谭遒强阳‘学羯^^
Пистинг 14.17* Файл hours.php
<?php
II Шапка страницы include Htopcounter. phprr;
// Библиотека функций require_once (**utils.php");
〃 Выводам таблицу
?>
<table>
<tr>
< td>4acbi< / td> <td>XnTbi</td>
<td>3aсчитанные хиты</td>
<td>XocTH< /td>
</tr>
<?php
for($i=0;$i<24;$i++)
{
// Формируем три массива с хостами ($host), засчитанными хитами($hit) // и общим числом хитов ($total) по 5 временным интервалам, // определенным в файле time_interval.php
list($host, $hit, $total)=
show__ip_host_hour($i+l, $i, $_GET(*day1], $_GET[1xd^page,]); echo "<”>
<td>*\$i.TT</td>
<td>$total</td>
<td>$hit</td>
<td>$host</td>
</tr>°;
}
echo ,l</table>n;
Структура файла hours.php аналогична структуре файла days.php (см. листинг 14.16), но вместо функции show_ip_host() применяется show_ip_host_hour(), которая также размещается в файле utils.php. Функция имеет следующий синтаксис:
array show_ip_host_hour($begin, $end, $day, $id_page);
В отличие от функции show_ip_host()она принимает 4 параметра, первые 2 из которых — “мело часов, которые необходимо прибавить к началу суток, чтобы получить границы временного интервала, третий параметр, $day, задает число дней, которые необходимо вычесть из текущей даты, чтобы получить сутки, для которых вычисляется статистика. Четвертый параметр $id_page имеет то же значение, что и для функции show_ip_host().
Функция show_ip_host_hours() практически аналогична функции show_ip_host(), за исключением переменных $tmpi и $tmp2, которые несут ответственность за ограничение выборки по полю putdate. Значения этих переменных представлены в листинге 14.18.
;Листинг 14.1В. Значения переменныхi
y<Tik«V»l»y»Z»Z*eftft ••^••••■•■•'•••••SfBlSft»«*t'Z^eee'elt'ftii'*ae«.T*lATeerT^«eieftVrt^ifi'ft'#fcA#»'i4'e»e/eeeVi7«#eV#i<r*_eeJ^«'ieJei»*'iB'i«'iO^«i'teeTifeA'e
<?php
if($begin = 0) $tanp2 ="“;
else $tmp2 = ” AND putdate >^
date_format (now()- interval '$day, day, 'AYHmTd 00:00:00') + interval 'Send' hour";
$tmpl = Mputdate <
date format(now()-
interval '§d&y‘ day, 叫Y-%mTd 00:00:00*) + interval 1$begin* hour";
?>
Следующая сервисная страница, которая будет рассмотрена, это Системы и браузеры (см. рис. 14.5). За формирование этой страницы ответствен скрнпт, расположенный в файле sysiems.php (листинг 14.19).
黎罩I : ^^^装3*?微冷^声? *濁注^":*唄当龍诳賞岁^&^母制貝/^^^^*嶼^^|關:.:"1"二"!"*^*二屮 嚟也右辈'|• s火 峙学岁Mq^^^jy^.Jf* 笛号 /当'V***.二*"",*『:
:Листинг 14.19. Файл systems.php
<?php
// Шапка страницы
include ntopcounter.phpn;
// Библиотека функций require_once("utils.php");
// Формируем массив с названиями операционных систем
$os[Twin*] = "Windows";
$os[*lin*] = "Linux & Unix";
$os[*mac*] = "Macintosh”;
$os [ * os * ] = мДругие1*;
// Формируем массив с названиями браузеров
$br[*ier] = иInternet Explorer";
$br (*net * ] = riNetscape ”;
$Ы[торгЧ = "Opera";
$br [ *br • ] = ”ДрУ™в”;
// Включаем массив временных интервалов require_once ("time_interval. php");
// Вычисляем количество хитов для браузеров и операционных
// систем по 5 временным интервалам, определенным в файле
// time_intexval * php for($i=0; $i<5; $i++) {
list($hit[-win1! [$i], $hit['lin・][$iL $hitLmac'J [$i】, $hit[,os,][$i], ShiWie,H$i】, $hit[*netr][$i],
$hit[,opr,]($i].
$hit[tbr,)[$i],
$total[$i]) = system_info($time[$i]fbegin1], $time[$i][*end*), $id_page);
} ?> <table>
<tr>
<td> </td>
<td>CeroflHH</td>
<td>B4epa</td>
<td>3a 7 дней</Ь<1>
<td>3a 30 №ei<</td>
<td>3a все время" td>
</tr>
<tr><td>Onepационные системыс/tdx/tr>
<?php
// Формируем блок иОперационные системы" foreach($os as $key => $name) (
echo "<tr>";
echo "<td>$name</td>";
for($i=0; $i<5; $i++) echo 'Ч!^*'.sprintf (M%d (%01*lf%s) ",
§hit["$key")[$i],
$hit[*f$keyM] [$i]/$total [$ij *200,
*%*) J<— echo "</tr>";
} ?>
<trxtd>Bpay3epH< / tdx/tr>
<?php
// Формируем блок "Браузеры" foreach($br as $key ==> $name) {
echo УА”;
echo °<td>$naine</td>rr;
for($i=0; $i<5; $i+-h) echo ”<t»”・sprint貝Td (%01.1f%srS
$hit[H$keyn][$i],
$hit[H$keyM][$i]/$total[$i]*100, *%•).n</td>H;
echo "</tr>";
echo ,,</table>";
Организация файла syslems.php несколько сложнее, чем main.php. В начале скрипта объявляются два ассоциативных массива: $os и $Ьг, хранящие названия операционных систем и браузеров соответственно. Для вычисления хитов, приходящихся на определенный тип браузера или операционной системы, используется функция system_info(), которая имеет следующий синтаксис:
array system_info < $begin, $endf $id_page)
Функция принимает три параметра, значение которых аналогично параметрам функции show_ip_host() (см. листинг 14.15), и возвращает массив, состоящий из 9 элементов, в которых разметается число xhtobs приходящееся на: 口 операционную систему Windows;
口 UNIX-подобные операционные системы;
операционную систему Macintosh;
口 остальные операционные системы;
口 браузер Internet Explorer;
браузер Netscape;
口 браузер Opera;
□ остальные браузеры;
口 общее число хнтов.
Первые 8 параметров размешаются в двумерном смешанном массиве $hit, первый ассоциативный индекс которого совпадает с индексом массивов $os и $br, а второй числовой индекс соответствует номеру временного интервала из массива $time, расположенного в файле timejnterval.php. Общее число хитов помешается в отдельный одномерный массив $total* Такнм образом, массив $hit разделен на две части, соответствующие операционным системам и браузерам. Эти части выводятся - при помощи двух циклов foreach, пробегающих ранее объявленные массивы $оз и $br. В каждом из циклов расположено по циклу for, которые пробегают от 0 до 4 включительно, в соответствии с чмслом временных интервалов в системе PowerCounter. В результате на странице выводится таблица, состоящая из двух блоков, за вывод каждого из которых несет ответственность один из циклов foreach. В свою очередь каждый из блоков состоит из четырех строк, за вывод которых несут ответственность циклы for.
Каждая из ячеек таблицы содержит число хитов и процентный вклад операционной системы или браузера в общее число хнтов. Вывод форматируется при помощи функции sprintf().
Далее будет рассмотрена функция system_info(), используемая для вычисления числа хитов, приходящихся на тот или иной браузер или операционную систему. Код функции приведен в листинге 14.20.
f'^^^иу^'.удул^ту 密yriFjL 鄒 iy^y^"W.竇颦,^5,W盟W 冲 Ч^АУ:塗摆^SUHT"?-二"W'^WaN/y^V^yawxwfiynrry/rgg
ЛИСТИНГ 14.20. Функция syatem_info ();
>«»v*tfA-r«f «#«e«^««#**«>«#««tf«>>««««#a<«>r>»«««4<««<*«riii«vr**«av^*** + *««'«*«»4I)**,r»r**^ti>iOibvH«J>>**'»**4<«««4«4«4B«««««>4>«ei Aetafebktbbiitbv^ibii^btbottbviitvviivb.ferooolilvviit***^*^"^**^***»»»***
<?php
// Функция возвращает массив из девяти переменных
// с информацией о числе посетителей, использующих
// ту или иную операционную систему или браузер.
// $begin - число дне", которое необходимо вычесть из текущей даты,
// для того чтобы получить начальную точку временного интервала
// $end - число дней, которое необходимо вычесть из текущей даты,
// для того чтобы получить конечную точку временного интервала
// $idjage - первичный ключ записи таблицы pages# соответствукяцей
// странице сайта
function system_info($begin, $end, $id_page) {
// Эта переменная (id^page) определяет, осуществляется ли запрос к
// конкретной странице или ко всему сайту*
if (empty ($id_j>age)) Strop =
else $tn^)= ” AND icVpage = $id__page";
// Запрос на статистику за временной интервал, определяемый
〃 параметрами 帛begin, $end
if ($begin == 0) $5p2 = 心;
else $tmp2 = ” AND putdate >= DATE_FORMAT(NOW(), 1 %Y-%m-%d 23:59:594 - INTERVAL '$begin' DAY";
$tmpl = ” AND putdate < ОАТЕ_ЕОЕШАТ (NOW (),1 %Y-%m-%d 23:59:594 一 INTERVAL fend, DAYn;
// Формируем SQL-эапросы
$begins = "SELECT COUNT(*) FR(M ip WHEHE systems = ”;
$beginb = ”SELECT COUNT(*) FROM ip WHERE browsers =";
$end = $tmpL. Stinp2.$tnip;
$query [1 win* ] = ^begins.11 rwindows1 n.$end;
$ query [ ^in1 ]工 ^begins.111 unix1 n. $end;
Squery['mac'] = $begins.M'macintosh1n.$end;
$ query [ 'oth,]二 $ begins.* none' ". $end;
$query[ rieT ] = $beginb.11 *msie *f,. $end;
$query[*oprr] = $beginb.H1opera *".$end;
$query[fnet1] = $beginb.и * netscape1",$end;
$quety[1 robT] ^ 串beg土nb.”,none1"*$end;
/7 Выполняем SQL-запросы foreach($query as $os => Rvalue) {
$hit[”$os”] - mysql_query($value); if (!$hit[n$osMJ)
puterror('*Ошибка при обращении к таблице IP-адресов");
$hits ["$os,f] = mysql_result ($hit ["$os"b 0);
}
$total = $hits[^th*] + $hits[•win1] + $hits[1lin1] + $hits[*mac*];
//Во избежание деления на ноль проверяем общее число хитов if($total — 0) $total = 1;
// Возвращаем массив значений
return array($hits[* win* ], $hits[1lin1], $hits['mac1 ]f $hits[*оЪЬ*]f $hits[,ie,], $hits[lnettb $hits[,opr,L $hits['rob1], 5total);
} ?>
Здесь, так же как н в случае с функцией show__ip_host О, во временной переменной 帛end формируются временные и страничные условия SQL- запроса. Кроме того, в функции используются две дополнительные временные переменные $begins и $beginb для операционных систем и браузеров соответственно. Формируемые SQL-запросы помешаются в ассоциативный массив $query. Далее в цикле foreach происходит выполнение SQL- запросов и помещение результатов в ассоциативный массив $hits, элементы которого возвращает функция. Значение для общего числа хитов не извлекается из базы данных, a hwuhcji兄етс$1 как сумма хитов всех операционных систем. Так как это значение используется далее в знаменателе для вычисления процентного вклада браузеров и операционных систем, в случае, если его значение получается равным нулю, ему присваивается единица.
Следующая сервисная страница, IP адреса, предоставляет информацию по IP-адресам посетителей за последние сутки (см. рмс. 14.6). За формирование этой страницы ответствен файл addresses.php, код которого приведен в листинге 14.21.
^^^^.“"XdLHwi^^y产必,”:¥*?,•*■* 牡吋 iyy»ta・尹ЛУМ:修*炉涉上中盟囲学.史.炉丝》卄”丁々《” •':yv*;wf:・&・M¥\
:Листинг 14.21. Файл addresses.php
曰•'•/й^К|йлгМ»^нМРК^?етЯ^ЛТО?1ч|К^А|г!и)Лк*1 ;L 閱就'?駄图h'&乐爵州落;r:・ & ,*;■ »«.$ 痂 М%“\ ;**;>"*;;;;;沾;&』;小祈雷,鼻&*3:
<?php
// Шапка страницы
include topcounter *phpK;
// Запрашиваем уникальные IP-адреса, отсортированные по времени, //из базы данных
if(empty($_GET[1page1])) $page = 1;
else $page = $_GET['page *];
II Вычисляем начало вывода
$begin = ($page - 1) *$pnttmber;
if(!empty($id_page))$tmp - ” AND id_page = "»$id_page;
else $5p =
// Определяем число посетителей с уникальными IP-адресами
// за последние сутки
$query ^ "SELECT COUNT(DISTINCT ip) EROM ip
WHERE putdate > NOW() - INTERVAL 1 DAY $tmp”;
$nuni - mysql_query ($query);
if(!$num) puterror(^Ошибка при обращении к таС^лице IP-адресов");
$total = mysql__result ($num, 0);
// Определяем число страниц
$nurober = (int)($total/$pnuwber);
if ((float) ($total/$pnuinber) -$nuniber != 0) $ number++;
〃 Выводим ссылки на другие IP-адреса
for{$1 = 1; $i<=$number;乳++)
{
if($number = $i)
{
if($page = $i)
echo "W$i - 1) *$pnuniber + 1) ."-$total]";
else
echo ”<a href^addresses *php?
id__page=n. $ id_page,n &page=,r. $ 1.M >
(n. ( ($1 - 1) *$рпшпЬег + 1) .,,*$total]</a>l4
}
else
if($page = $i)
echo ”「・(($i - 1) *$pnumber + 1) ..$i*$pnumber. H] M;
else echo м<а href=addresses.php?
id_page=°. $id__page. "&page=".$i» n> [M. (($i - 1) *$pnumber + 1) . ,I-H.$i*$pnuinber. и]</а>"; } } // Выводим сами IP-адреса ?>
<table>
<tr>
<td>IP-a,npec</td>
<td>XocT</td>
<td>Bcero посещений</td> <td>Пocлeднee обращение</Ёс!> </tr> <?php // Формируем и выполняем SQL-запрос, извлекающий уникальные IP-адреса //из таблицы ip эа последние сутки $query = "SELECT DISTINCT(ip), MAX(putdate) AS putdate, COUNT(id_ip| AS hits FROM ip
WHERE putdate > NOW() - INTERVAL 1 DAY $tmp GROUP BY ip ORDER BY putdate DESC LIMIT $begin, $ртжЬ虹"; $ips = mysql_query ($query);
if (!$ips) puterror (^Ошибка при обращении к таблице IP-адресов11); while ($ip = mysql_fetch_array ($ips)) (
$pos = strpos($ip[tip,]^ '7ri>;■
switch ($pos) { case (NULL): echo "<tr>
<td>
<a href=,ip>php?ip=M »$ip( *1рт ] , Ml>°.$ip[ ^p1 ] • "</a> </td>
<td>" . gethostbyaddr ($ip [1 ip1 ]) . "</td>,^;
break;
default:
echo **<tr>
<td>
<a href=f ip.php?ip=,, .substr ($ip[' ip1 ],0,5рз) • ”‘>
■\$ip[»ip4 *"
</a>
</td>
<td>". gethostbyaddr (substr (帛ip [ * ip' ], 0, $pos)) . "</td>";
}
echo H<td>rr.$ip[ fhits*]. H</td><td>H >$ip[1 putdate' ] . ”</td>";
}.
echo T'</table>H;
Скрипт выводит таблицу, содержащую $рпшпЬег строк и 4 столбца. На 1Р・ адреса, которые не убрались на первой странице, выводятся ссылки.
В первом столбце приводится 1Р・адрес, являющийся гиперссылкой на скрипт ipphp, подробно рассмотренный в главе 4 (см. листинг 4.9), во втором столбце приводится хост, возвращаемый функцией gethostbyaddr(), в третьем столбце содержится общее число обращений с данного хоста и в последнем столбце ——время последнего обращения.
Замечание )
Параметр Spnumber определен в файле config.php (см. листинг 14.7).
■Ч»*••*.• •■• • • • "• •*■»**•!* '■Ч»*••*.• •■• • • • "• •*■»**•!* '*”*♦,к >__・:«,中.,2..,,,••”11y・•・」》■!注,,*t*[[* 岁鹏・,*;::C fi岳:Et;沛用EnT羸黑;韜一£愿%*i里福*”*♦,к >__・:«,中.,2..,,,••”11y・•・」》■!注,,*t*[[* 岁鹏・,*;::C fi岳:Et;沛用EnT羸黑;韜一£愿%*i里福Следующая сервисная страница Поисковые роботы предоставляет информацию о числе посещений ресурса роботами четырех наиболее известных поисковых систем: Yandex, Google, Rambler и Aport (см. рис, 14.8). За формирование этой страницы несет ответственность файл robois.php, содержимое которого представлено в листинге 14.22.
■Ч»*••*.• •■• • • • "• •*■»**•!* '
■Ч»*••*.• •■• • • • "• •*■»**•!* '
*”*♦,к >__・:«,中.,2..,,,••”11y・•・」》■!注,,*t*[[* 岁鹏・,*
;::C fi岳:Et;沛用EnT羸黑;韜一£愿%*i里福
*”*♦,к >__・:«,中.,2..,,,••”11y・•・」》■!注,,*t*[[* 岁鹏・,*
;::C fi岳:Et;沛用EnT羸黑;韜一£愿%*i里福
:Листинг 14.22» Файл robots.php
Si'^irirab-e'^iji|ifB'4,eebB'« *»■■*»/*'•■■'■ ■*'■■■/*•'•'•'••■'« ■♦^••'^•«■■■■'■■•'••■■в^ив.в'ив.''."■-■■■■■■ ■■■■«ill* fc^B»*'«e***»Vii»<»f>.
<?php
// Шапка страницы
include и topcounter. php11;
// Библиотека фушший
require_once(Hutils.php");
// Фораиируем массив с названиями роботов поисковых систем $rob[1 ram*] = ^Rambler";
ЗгоЬС^дод1] = "Google'1;
$rob[*ynd*]二"Yandex";
$rob[,apt,] = "Aport ”;
$rob「oth1 ] = "Другие ”;
// Включаем массив временных интервалов require_once (11 time interval. php");
// Вычисляем количество хитов для поисковых роботов систем // по 5 временным интер валам г определе иных в файле // time_interval. php for($i=0; $i<5; $i++)
list ($hit [ ^am1 ] [$i],
$hit['gW】①],
$hit「ynd'] [$i], $hit[faptf ] [$i], $hit[»oth4 [$i],
Stotal[$i]) - robots($time[$i][1begin*], Stime [$i] J end", $id_page);
}
?>
<table>
<tr>
<td>Po6oTbi</td>
<td>CeroflHH</td>
<td>B4epa < / td>
<td>3a 7 дн邑宜</td>
<td>3a 30 дне兑</td>
<td>3a все время</2>
</tr>
<?php
// Формируем таблицу
foreach($rob as $key => $name)
echo n<tr><td>$name</td>M;
for($1-0; $i<5; $i++)
(
echo »<td>*\sprintf(n%d (%01.1f%s)H, $hit[*,$key"] [$i]r
$hit [и$кеу^'] [$i] /$total t$U *100f '%')."</td>w;
echo M</tr>**;
}
?>
</table>
Здесь, так же как и в файле systems.php (см. листинг 14.19), формируется ассоциативный массив $еоЬ с названиями роботов поисковых систем. В цикле for, пробегающем от 0 до 4 включительно, в соответствии с числом временных интервалов, заданных в файле timejnterval.php, вычисляется число посещений роботами поисковых систем при помощи функции robots, которая имеет следующий синтаксис:
array robots ($begin, $end, $id__page)
Функция принимает 3 параметра, значение которых аналогично параметрам функции show_ip_host () (см. листинг 14.15), и возвращает масснв, состоящий из 6 элементов:
口 число посещений роботом поисковой системы Rambler;
число посещений роботом поисковой системы Google;
число посещений роботом поисковой системы Yandex;
口 число посещений роботом поисковой системы Aport;
число посещений роботами, принадлежность которых установить не удалось;
口 общее число роботов.
Хиты, относящиеся к отдельным поисковым роботам, заносятся в двумерный массив hit, первый ассоциативный индекс которого совпадает с индексами массива $rob, а второй числовой индекс соответствует номеру временного интервала из массива $time. Общее число роботов помещается в отдельный одномерный массив $ total. Далее в двойном цикле (foreach и for) выводится таблица, столбцы которой соответствуют временным интервалам, а строки — поисковым системам. Функция robots () определена в файле utils.php, ее код представлен в листинге 14.23.
;Листинг 14.23. Функция robots ОI г?« A J?Sv^wffi ■ й????^;、..■.-.>...••..■... U”・4n\*,:tf»i*wC5v**;*;Листинг 14.23. Функция robots ОI г?« A J?Sv^wffi ■ й????^;、..■.-.>...••..■... U”・4n\*,:tf»i*wC5v**;*史粗空■F對做T:?單,崂乎士1 刃产д里廿*”产金,*н*";.干:炉却^^^^ 毗制^^]皆,,,:器""二笠*: ■у■三/弊,u V • '"* '/L^^^^y-j^K^^^^j1 都^^j£M^f^^/
;Листинг 14.23. Функция robots О
I г?« A J?Sv^wffi ■ й????^;、..■.-.>...••..■... U”・4n\*,:tf»i*wC5v**;*
;Листинг 14.23. Функция robots О
I г?« A J?Sv^wffi ■ й????^;、..■.-.>...••..■... U”・4n\*,:tf»i*wC5v**;*
<?php
// Функция возвращает массив иэ шести переменных: // Число роботов Rambler
// Число роботов Google
// Число роботов Yandex
// Число роботов Aport
// Другие роботы
// Общее число посещений
// $begin - число дней, которое необходимо вычесть из текущей даты, // для того чтобы получить начальную точку временного интервала // Send - число дней, которое необходимо вычесть из текущей даты, // для того чтобы получить конечную точку временного интервала // $id_page - первичный ключ записи таблицы pages, соответствующей // странице сайта function robots ($begin, $end, $id_j)aga) {
// Переменная id__page определяет, осуществляется ли запрос к конкретной 〃 странице или ко всему сайту, if($idjpage = "”) $tmp ="";
else $tmp = ” AND id_page=$ id_pagert;
// Запрос на статистику за временной интервал, определяемый
// параметрами $begintend
if($begin == 0) $tmp2 ="”;
else $tmp2 = ” AND putdate >= DATE_FORMAT(NOW(|, 1%Y-%m-%d 23:59:59')- INTERVAL 1$begin1 DAY”;
$tmpl ^ " AND putdate < DATE_FORMAT(NOW(), 1 %Y-%m-%d 23:59:59')- INTERVAL ^end1 DAY";
// Формируем SQL-запросы
$begin = "SELECT COUNT(*) ЕЮ4 ip WHERE systems = ”;
$end = $tirpl * $tmp2. $tmp;
$query[1ynd1] = $begin「',robot_yandex'”.$end;
$query[ф ram*) = $begin.H * robot^rambler1n.$end;
$ query「gog' ] = $begin * " * robot__goagle * " . $end;
$ query [ ■ apt' ] = $begin.,M robot_yandex'"・ $end;
$query[*oth*] = $begin.и *none1M.$end;
// Выполняем SQL-запросы
foreach($query as $robot => Svalue)
{
$hit ^^robot11] = mysql_query($value);
if(!$hit[”$ robot"])
puterror (''Ошибка при обращении к таблице ХР^адресов11);
$hits [и$robot**] ^ mysql_result (Shit [n$robot11], 0);
$ total = array__sum($hits);
// Во избежание деления на 0 проверяем значение общего
// числа хитов $total
if($total в 0) $total = 1;
return array($hits t * ram* J r Shits(*додг]x $hits[rynd*], $hits [1 apf ], ^hitsfoth*], $ total);
?>
Структура данной функции аналогична структуре ранее рассмотренных функций этого же типа. В ассоциативный массив $query помещаются SQL- запросы, производящие выборку числа хитов от роботов поисковых систем, при этом для формирования строки запроса используются две временные переменные: $begin и $end для начала и завершения скрипта. Далее в цикле foreach запросы передаются на выполнение функции mysqi_query(), возвращающей дескриптор запроса, по которому при помощи функции mysql_resuit <) извлекается результат, помещаемый в ассоциативный массив $hits. Общее число роботов получается суммированием значений всех элементов массива $hits при помощи функции array_sum(). Если значение общего числа роботов $totai равно нулю, переменной $totai присваивается значение единицы, для того чтобы избежать далее деления на ноль.
Следующая сервисная страница Поисковые запросы выводит две таблицы (см. рис. 14.9). Первая содержит число переходов со страниц четырех наиболее известных поисковых систем: Yandex, Google, Rambler и Aport, а также общее число запросов со всех поисковых систем для пяти временных интервалов: "Сегодня^, "Вчера", "За 7 дней", “За 30 дней" и "За все время". Каждая из цифр является ^пересылкой, переход по которой определяет состав второй таблицы ——поисковых запросов. Так, переход по ссылке, расположенной в ячейке на пересечении столбца За 7 дней и строки Yandex, выведет во второй таблице поисковые запросы с Yandex, по которым посетители нашли ресурс за последнюю неделю. Переход по ссылке, расположенной в ячейке на пересечении столбца За все время н строки Все, выведет ключевые слова, по которым был найден ресурс для всех четырех поисковых систем за все время сбора статистики.
「Замечание )
По умолчанию во второй таблице выводятся ключевые слова для всех поиске* вых систем за текущие сутки (пСегодням).
За формирование страницы Поисковые запросы несет ответственность файл searchqueiy.php, содержимое которого представлено в листинге 14.24.
Листинг 14.24. Файл searchquery.php
iв. Ak-jJ-.-. ."; ..'• . Л "*;,.;;..;;:.;“• *if点$蜕W^it d;JTifi芻 >?>>ia>-7i nd ;¥ • fi •«t'?M « Л . • * itilifiitiif^iit, . < A .■»?». “.界,*%;;;,*■*;
<?php
〃 Шапка страницы include "topcounter・ php";
// Формируем массив с названиями поисковых систем $sch(Rambler1] = "Rambler";
$sch[* google1] = "Google”;
$sch [ * yndex “ = "Yandex11;
$sch[,aport 口 = "AporL;
$sch[* total*] = "Bee";
// Включаем массив временных интервалов reguire_once (Htime_interval. php1');
// Библиотека функций require_once(nutils.php");
// Вычисляем количество хитов для поисковых систем
// по 5 временным интервалам, определенным в файле // time_interval.php for($i=0; $i<5; $i++)
{
list($hit[1 rambler1][$i]f
$hit[,yndex1][$i]f
$hit [ • aport1 ] [$1L
$hit[google4 [$i],
$hit[ftotal1] [$i]) = search($time[$i] [^ginMf $time [$i]['end1], $id_page);
?>
<table>
<tr>
<ис!>Поисковые CMCTeMbi</td>
<td><a href^searchhours *php?day^=O&id_page=<?php echo $id_page; ?» Сегодня
</a></td>
<tdxa href=searchhours.php?day=l&id_page=<?php echo $id_page; ?» Вчера
</ax/td>
20 Зак. H8l
<td><a hrеf»searchdays.php?daystst=7&id_page^<?php echo $id_page; ?» 3a 7 дней
</ax/td>
<td><a href=searchdays.php?daystst=30&id_page=<?php echo $id_page; ?» 3a 30 дней
</ax/td>
<td>3a все BpeMH</td>・
</tr>
<?php
// Формируем таблицу с числом обращений с поисковых систем
foreach($sch as $key => $name)
{
echo "vtr align=right>”;
echo "vtd align^left>$name</td>n;
for($i=0; $i<5; $i++)
echo **<td>
<a href=searchquery.php?begine,*.$time[$i][*begin*]."&
end=".$time($ij[*end4•"&srch=$ key>
,l.$hit{1'$key,t] [$i] .H
</a>
</td>H;
echo ,l</tr>ii;
}
?>
</table>
<?php
if (empty ($_GET['begin'】)[I
empty ($_GET[f end1]) II
s^ty( $_GET [ ■ sr ch1 ]))
{
$_GET[1begin*] = 1;
$_GET[ ^nd*]= 0;
$_GET[,srch,] = "total";
}
〃 Вызываем функцию aearchquery(), которая формирует таблицу
// с ключевыми словами
searchquery($_GET「begin*], $_GET['end1], $_GET['srch']);
?>
Для формирования первой таблицы, точно так же как и на предыдущих страницах, в начале скрипта формируется массив $sch с названиями поисковых систем. После чего в цикле for происходит заполнение двумерного массива $hit, хранящего число посещений с поисковых систем. Для этого используется функция search(), принимающая три параметра: два для границ временных интервалов и первичный ключ страницы, аналогично вышерассмотренным функциям. Функция search{)возвращает массив из 5 элементов:
口 число переходов со страниц поисковой системы Rambler;
О число переходов со страниц поисковой системы Yandex;
□ число переходов со страниц поисковой системы Aport;
О число переходов со страниц поисковой системы Google;
口 общее число переходов со всех поисковых систем.
Далее, как и на предыдущих страницах, происходит вывод таблицы в двойном цикле (foreach для строк и for для ячеек), В отличие от других рассмотренных ранее таблиц, здесь каждый элемент таблицы является ссылкой вида:
searchquery.php?begin=begin&end=end&srch=key
Такая ссылка позволяет передать скрипту searchquery.php параметры begin, end и arch, определяющие границы временных интервалов и тип поисковой системы. Значения параметров begin и end извлекаются из элементов массива временных границ stime (см. листинг 14.14). Если переход к скрипту searchqueiy.php осуществляется не по ссылке из таблицы, и, следовательно, параметры в строке запроса не передаются, то параметры получают значения по умолчанию: временной интервал соответствует текущим суткам (Сегодня), а параметр srch равен total, что соответствует выборке ключевых слов для всех поисковых систем. Полученные из строки запроса (или установленные по умолчанию) параметры передаются функции searchquery (), которая формирует и выводит таблицу с ключевыми фразами, которые использовали посетители при поиске в поисковых системах.
Обе функции search ()н searchquery ()расположены в файле utils.php. Код функции search()представлен в листинге 14.25.
先鯉I縫上^^•方逻圮fi%翌У 出世1遇^fi^^tf^摊崎Й圜胸匕第*潘*'-"、**^У ^^^**"父金*¥ 遍
;Листинг 14.25. Функция search ()
濡 *««7Л■ V«вii■ ^■ I««■ IiVi44^^vM*A''^■ ■ *ifc* ■■ *#;・;*H,‘
<?php
// Функция возвращаетмассив из пяти переменныхi
// ЧислопереходовсостраницпоисковойсистемыRambler;
// ЧислопереходовсостраницпоисковойсистемыGoogle;
// ЧислопереходовсостраницпоисковойсистемыYandex;
// Число переходов со страниц поисковой системы Aport;
// Общее число переходов со страниц всех поисковых систем.
// $begin - число дней, которое необходимо вычесть из текущей даты,
// для того чтобы получить начальную точку временного интервала
// $end - число дней, которое необходимо вычесть из текущей даты,
// для того чтобы получить конечную точку временного интервала
// $id_page - первичный ключ записи таблицы pages, соответствующей
// странице сайта
function search($begin, $end, $id_page)
{
// Переменная id_page определяет, осуществляется ли запрос к конкретной // странице или ко всему сайту.
if (empty ($id^page)) $tmp_page 匸
else $tmp_page = " AND id_page ^ $id_page";
// Запрос на статистику за временной интервал, определяемый
// параметрами $begin, $end
if($begin = 0) $tmp2 = ”";
else $tmp2 = ” AND putdate >= DATE__FORMAT(NOW()r '%Y-+m-*d 23:59:59»)- INTERVAL 『$begin『 DAY”;
$tmpl = "AND putdate < DATE_FOBMAT (NOW {), *%Y-%m-%d 23:59:591 - INTERVAL 'Send,DAY'1;
// Формируем SQL-запросы
$begin = r,SELECT COUNT(*) FROM refferer WHERE searches =”;
Send a $tmpl.$tn^2,$tmp_page;
$query[1ynd*] = $begin."*yandex' ”.$end;
$query[fram'] = $begin•”'rambler' ".$end;
$query [ * gog * ] ^ $begin.11 * google * "・$end;
$query (1 apt * ] = $begin.11 *aport1 "*$end;
// Выполняв SQL-запросы
foreach($query as $search => $value)
{
$hit["Ssearch"] = mysql_query($value);
if (>$hit [^search11])
puterror("Ошибка при обращении к таблице 1Р-адресовм);
$hits [ll$searchH] ^ mysql^result ($hit (11 $ search"] f 0);
}
$total ^ array sum ($hits);
// Во избежание деления на 0 проверяем значение общего $total
if($total == 0) $total = 1;
return array ($hits ['ram* ] f $hits['yndL $hits [f apt * 1, $hits「gogl $ total);
}
?>
Здесь, как и в других функциях, вычисляющих число обращений за определенный временной интервал, в первой часта функции происходит формирование фрагментов SQL-запросов, Для этого используются две временные переменные: $begin “ $end, содержащие общие части для начала и завершения SQL-запросов. Запросы, вычисляющие число переходов на сайт со страниц поисковых систем, помешаются в ассоциативный массив $ query, после чего элементы массива в цикле foreach передаются функции mysqi_query(). Возвращенные функцией mysqi query()дескрипторы запроса используется для извлечения результатов при помощи функции mysqi_resuit о,Результат помещается в ассоциативный массив $hits. Число посещений со страниц всех поисковых систем осуществляется функцией array_sum (), суммирующей элементы массива, переданного ей в качестве единственного аргумента. Результат работы функции помещается в переменную $total, которая после проверки на неравенство нулю наряду с элементами массива $hits возвращается функцией в качестве результата работы*
Код функции searchquery(), несущей ответственность за формирование второй таблицы, с ключевыми словами, которые посетители использовали в поисковых системах, представлен в листинге 14,26. Таблица состоит из 5 столбцов:
口 запрос, который посетитель ввел в поле поисковой системы;
название поисковой системы;
口 время перехода посетителя с поисковой системы на сайт;
口 IP-адерс посетителя;
страница, на которую пришел посетитель.
Запрос, который посетитель ввел в окне поисковой системы, будем извлекать из URL реферера, который система PowerCounter сохраняет в таблице refferer.
:瑋季:.宙 ^^^М^h学圖注:崇产*'**"1*'?;'里钱:У 空、^^^^ВИй^ 幽^^ftMf’ i Листинг 14*26. Функция searahquery (>:瑋季:.宙 ^^^М^h学圖注:崇产*'**"1*'?;'里钱:У 空、^^^^ВИй^ 幽^^ftMf’ i Листинг 14*26. Функция searahquery (>rlvi«'ft-H''«VUWifrt,ftlT Иrlvi«'ft-H''«VUWifrt,ftlT И:3*:"*・ ni »%¥♦ J^t• о • ■■i<«¥'ii I:3*:"*・ ni »%¥♦ J^t• о • ■■i<«¥'ii I
:瑋季:.宙 ^^^М^h学圖注:崇产*'**"1*'?;'里钱:У 空、^^^^ВИй^ 幽^^ftMf’ i Листинг 14*26. Функция searahquery (>
:瑋季:.宙 ^^^М^h学圖注:崇产*'**"1*'?;'里钱:У 空、^^^^ВИй^ 幽^^ftMf’ i Листинг 14*26. Функция searahquery (>
rlvi«'ft-H''«VUWifrt,ftlT И
rlvi«'ft-H''«VUWifrt,ftlT И
:3*:"*・ ni »%¥♦ J^t• о • ■■i<«¥'ii I
:3*:"*・ ni »%¥♦ J^t• о • ■■i<«¥'ii I
<?php
// $begin - число дней, которое необходимо вычесть из текущей даты, // для того чтобы получить начальную точку временного интервала
// Send - число дней, которое необходимо вычесть из текущей даты,
〃 для того чтобы получить конечную точку временного интервала
// $idjpage - первичный ключ записи таблицы pages, соответствующей // странице сайта
function searchquery($begin, $end, $srch) (
// Запрос на статистику за временной интервал, определяемый
// параметрами $begin, Send if($begin =» 0) $tmp2 = ”"; else $tnp2 = ” AND putdate >= NOW() - INTERVAL $begin DAY”;
$皿1 = " AND putdate < NOW() - INTERVAL $end DAY”; // Расставляем and if($srch != "total") (
$qwer = "SELECT pages.name AS url,
refferer.name, putdate, ip/ searches
FROM refferer, pages
WHERE refferer.searches ='帛srch' AND refferer * id_page = pages, id__page “.$1лпр1.$5р2.”
ORDER BY putdate DESC”; } else {
$qwer = HSELECT pages.name AS url, refferer.name, putdate, ip, searches
FROM refferer, pages
WHERE refferer.searches !仁 * none * AND refferer.searches != ‘Qwn_sit£‘ AND refferer. id_j>age = pages. id_page ”.$tnpl・$tnip2・"
ORDER BY putdate DESC”;
$qwr = mysql_jquery {$qwer);
if(!$qwr) puterror("Ошибка при выполнении запроса");
if (mysql_mm^rows ($qwr) >0) {
echo "<table>";
switch($srch)
{
case 'yandex':
{
while($ip = mysql_fetch_array($qwr))
{
eregi ("text= ( L&] *) ", $ip[ ^ane* ] * n&u, $query);
if(strpos($ip(•name*],”yandpage”)!=null)
echo ,#<tr>
<td>".convert_cyr_s tring(urldecode($query[1]), ”k",
3);
else echo r,<tr><td>H .$query [1] J^/tc^'1;
echo ,,<td>Yandex</tdxtd>,l*$ip{1 putdate* J. ,,</td>,';
print_ip($ip【'ip']);
echo 'Уtdxa href=http :〃 ”. $_SERVER [" SERVER_NAME ” ]. Sipfurl»] .и>
”.$_SERVER【"SERVER—NAME”].$ip[»url1].H</a></td></tr>n; }
break;
}
case 'rambler':
(
while($ip = mysql_fetch_array($qwr))
(
eregi("words=(L&]*)", $ip[,name,]* и&", $query);
echo **<tr>
<td>”・$query[ll・"</td>
<td>Rambler</td>
<td>" • $ip [' putdate') ”</td>” ;
print_ip($ip[ * ip1 ]);
echo M<td><a href«http://H.$_SERVER[MSERVER_NAMEH].
$ip(*url*]-M>
••. $_SERVER [ MSERVER_NAME° ] ^ipfurl* ] . n</a></td></tr>H;
}
break;
case 'google':
{
while ($ip = mysql__fetch_array ($qwr})
(
eregi ("『(「&]★) ", $ip「nanie'] ."&”,fquery);
if(strpos($ip[^name1],"UTF-8M)!=null)
$tn^)5tr = utf8_w±n ($query [ 1 ]);
else $tmpstr = $query[1];
echo **<tr>
<td>r,. Stmpstr. **</td>
<td>Google</td>
<td>”.$ ip[,putdate*]."</td> ”; print__ip ($ip [”,]);
echo "vtdxa href=http://**.$_SERVER[MSERVEH_NAME° ].
$ip「url,]・">
11. $_SERVER [ "SERVER_NAME,>] . $ip [ *url1 ] . M</a></td></tr>n; } break;
}
case 'apor七':
{
while ($ip 二 mysql_fetch_array($qwr))
{
eregi (nr^( (A&] *) H, $ip( ,name, ] . "&**, $query);
echo "<tr>
<td>“. $query [1] .r,</td>
<td>Aport</td>
<td>tt#$ip[fputdate,] ・"</td>”; pr int_ip {$ ip [1 ip1 ]);
echo "<tdxa href=". $_SERVER [ "SERVER_NAMEri] ^ipfurl1] . H>
n. $_SERVER [ aSERVER_NAME,1 ] . $ip [1 url1 ] . •^/ax/tdx/t^"; } break;
}
case 'total':
{
while($ip = mysql_fetcharray($qwr))
if($ip[* searches *] — nyandexn) {
eregi (*'text®( [Л&] *) Mf $ip— name" . "&". $query);
if (strpos ($ip[ Fame' ], "yandpage11) I^null)
$query[1] = convert_cyr_string(urldecode($query[1])r
“k”, 3);
}
if (§ip [1 searches1 ] — ° rambler**) eregi ("words= ([Л & ] *)IT, $ip [ vname1 ]・"&", $query);
if ($ip[ Searches • ] = naportH) eregi ("r= (「&]★) ”, $ip[ Famd 】・"&“, $query);
if ($ip[ 'searches*] == "google**)
(
eregi {Hq=( [A&] *) ", $ip[ Fame']・"&", $query);
if (strpos ($ipUname‘ ], MUTF-8H) !=null) $query[1] = utf8_win($query[1]);
}
echo ,f<tr>
<td>H.$query[1].H</td>
<td>".$ip「searches1]•"</td>
^^".^ipfputdate1] .l,</td>1';
print_ip($ip[ 'ip* ]);
echo M<tdxa href=http: //" . $_SERVER["SERVERJ^AME"].
$ip[,urlt] .f,>
”・ $__SERVER [MSERVER_NAME°] . $ip [furl* ] . "</a></td></tr>”;
} break;
} // Конец оператора switch
} // Конец для if (mysql_nuir^rows ($qwr) >0) echo n</table>n;
} // Конец функции searchquery()
// Небольшая вспомогатель ная функция для вывода IP-адреса
function print_ip($ip_address)
$роз = stгроз($ip_address, *7И); switch ($pos)
{
case (NULL):
echo "〈七dxa href =1 ip. php?ip«". $ip_address, " •> ".$ip_address.11 v/ax/td>"
break;
default:
echo ”<tdxa href =*ip*php?ip=rt.substr ($ip_address, 0, $pos) . M r> ”,$ip_address.” </ax/td>";
} ?>
Основная задача функции searchquery() заключается в извлечении из таблицы refferer URL поисковых систем, содержащих в себе ключевые слова и извлечение самих ключевых слов. Отличительной чертой данной функции является то, что в параметрах, а следовательно, и в условиях SQL-запросов, отсутствует первичный ключ конкретной страницы id_page и инфомация извлекается для всего сайта в целом. Формирование временньгх переменных $tmpl и $tmp2, несущих ответственность за временной интервал выборки, происходит аналогично другим, уже рассмотренным функциям. SQL-запрос, помещаемый во временную переменную $qwer, формируется в зависимости от значения третьего параметра функции $srch. Если данный параметр принимает значение, соответствующее конкретной поисковой системе (^andex1, 'rambler*, 'google* ИЛИ *aport*), ИЗ таблицы refferer извлекаются записи, соответствующие только данной поисковой системе. Если же параметр $srch принимает значение 'total1, то из таблицы извлекаются все записи за исключением тех, чье поле searches принимает значения попе' и 'own^site'.
Если результат запроса ненулевой, происходит переход к управляющей конструкции switch, которая обрабатывает пять возможных значений параметра $srch ('yandex', 'rambler', 'google* 'aport' И total) Первые четыре случая, относящиеся к конкретным поисковым запросам, строятся по следующей схеме. В цикле while результаты запроса при помощи функции mysql_fetch_array()извлекаются в ассоциативный массив $ip, после чего происходит извлечение ключевой фразы из URL и формирование строки таблицы. Расшифровка строки запроса производится при помощи регуляр-
ных выражений и индивидуальна для каждой из поисковых систем, так как формат строки запроса для поисковых движков у всех систем различен.
Приведенные здесь механизмы извлечения поисковых фраз не универсальны, так как строки запроса время от времени меняются.
〔 Замечание )
Как видно из листинга 14.26 такие поисковые системы, как Yandex или Google, имеют по два варианта URL, требующих индивидуальной расшифровки.
Если параметр $srch принимает значение total', URL различных поисковых систем идут в произвольном порядке и каждая из извлекаемых строк проверяется на соответствие той или иной поисковой системе.
【 Замечание )
Для вывода IP-адреса посетителя, являю1це「ося ссылкой на скрипт ip.php, описанный выше, используется вспомогательная функция print_ip <). Так как адрес посетителя может содержать маску подсети, а скрипт определения географии и лица, на которое зарегистрирован IP-адрес, принимает только один IP-адрес, маску подсети необходимо вырезать.
Возвращаясь к первой таблице, содержащей число переходов со страниц поисковых систем, следует отметить, что заголовки страницы, как и в случае таблицы на странице Хиты и хосты, являются гиперссылками, переход по которым приводит к страницам, отражающим динамику распределения переходов со страниц поисковых систем за неделю и месяц (рис. 14Л1) для временных интервалов "За 7 дней0 и н3а 30 днейи (файл searchdays.php) и за текущие и прошедшение сутки (см. рис. 14.10) для временных интервалов ''Сегодня'* и "Вчера'* (файл searchhours.php). Содержимое файла searchdays.php представлено в листинге 14.27.
:Листинг 14.27. Файл searchdays.php
<?php
if (empty ($_GET (•daystst1 D ) $_GET [1 daystst' ] = 7;
// Шапка страницы
include ^topcounter.phpM;
// Библиотека функций
require_once("utils•php”);
// Выводим таблицу с количеством переходов со страниц поисковых систем echo n<tablextr>
<td>5nbsp;</td>
<td>Rambler</td>
<td>Yandex</td>
<td>Google</td>
<td>Aport</td>
<td>Oбщee числом/t:d>
</tr>";
// Заносим в переменные $rambler, $yandex, $aport, $google и $total // количество переходов co страниц поисковых систем Rambler, Yandex, // Aport, Google и общее чило переходов со всех систем соответственно, // по 5 верменним интервалам, определенным в файле time__interval,php for($i=0; $i<$_GET[,dayst3tf]; $i++) (
list($ramblerr
$yandex,
$aport, $google, $total) = search($i+lf §i, $jGET[1id^page•]); echo "O
<tdxa href=*searchhours.php?day=$i&
id__page=". $_GET [f idjpage * ].111 > $i да. назад</ах/Ъс1> vt d>$ ramble r</td> <td>$yandex</td> <td>$google</td> <td>$aport</td> <td>$total</td>
</tr>M;
}
echo ,f</table>n;
Файл searchdays.php принимает параметр daystst, передающий число дней, за которые необходимо вывести динамику распеределения переходов со страниц поисковых систем> После вывода шапки таблицы в цикле tor формируются ее строки. Для этого используется описанная выше функция search О (см. листинг 14.25).
Структура файла searchhours.php (листинг 14.28) во многом аналогична структуре файла searchdays,php. Лишь для извлечения числа переходов со СТраНИЦ ПОИСКОВЫХ СИСТеМ ИСПОЛЬЗуеТСЯ фуНКЦИЯ search_hour о. У этой функции появляется дополнительный параметр, задающий суткн, для которых производится вывод статистической информации.
附£ *W' ^з^£-¥ 厘冲 笫'^^^^К^^й^У^И^^^^^计'wv^y^ ^^Ж^^^^ф^^УШ^ЦГ^^ ^w**-^***""**^*'* **$、•,,!明
Листинг 14.28. Файл searchhours.php
<?php
// Шапка страницы
include "topcounter. phpf,;
// Библиотека функций
require_once("utils.php”);
// Выводим таблицу с количеством переходов со страниц поисковых систем echo "<table><tr>
<1б>Часы</td>
<td>Rambler</td>
<td>Yandex</td>
<td>Google</td>
<td>Aport</td>
<td>O6uiee 4wcno</td>
</tr>»;
// Заносим в переменные $rawbler, $yandex, $aport# $google и $total // количество переходов co страниц поисковых систем Rambler, Yandex, // Aport, Google и общее чило переходов со всех систем соответственно, // по 5 верменным интервалам, определенным в файле time interval・php for($i=0;$i<24;$i++) {
list($rambler, $yandex, $aport, $googler $ total) = search_hour ($i+l, $i, $_GET「day1 ], $_GET [* id^age * ]); echo "<tr>
<td>$i</td>
<td>$rarobler</td>
<td>$yandex</td>
<td>$google</td>
<td>$aport</td>
<td>$total</td>
</tr>n;
}
echo "</tabIe>";
?>
Последняя сервисная страница Ссылки (см. рис* 14.12) выводит таблицу с числом переходов с сетевых ресурсов, где расположена ссылка на сайт, для которого производится сбор статистической информации за пять временных интервалов, определенных в массиве $time (см. листинг 14*14). При этом администратор системы PowerCounter может самостоятельно добавлять или удалять сетевые адреса ресурсов. Информация здесь, как и в случае поисковых систем, извлекается из таблицы refferer< За формирование сервисной страницы Ссылки ответствен скрипт, расположенный в файле reflfererphp (листинг 14.29).
<?php
// Шапка страницы include "topcounter.php";
// Включаем библиотеку функций requireonce("utils.php");
/ / Включаем массив временных интервалов require_once (ntinie_interval. php”);
// Вычисляем количество хитов для поисковых систем
// по 5 временным интервалам, определенным в файле
〃 thne_interval Ф php for($1=0/ $i<5; $i++) {
$hit[$i] = refferers($time($i] [^egin*], $time($i] [•end*], $id_jsage); }
// Выводим таблицу посещения с отсл ежи в а емых ресурсов ?>
<а h.ref=addreffererform.рЬр>Добавить</а></td> <table>
<tr>
<td>Pe$>epep</td> ОСегсднм/З〉 <td>B4epa</td> <td>3a 7 OTefi</td> <td>3a 30 №eii</td>
<td>3a все BpeMH</td> < td>fleficTBHe< / td>
</tr>
<?php
// Выводим таблицу переходов с ресурсов, чьи адреса занесены в таблицу // refferer
$ref = mysql_query (11 SELECT * FROM links ORDER BY name");
if(!$ref) puterror("Ошибка при обращении к таблице ресурсов");
while (Sreff = irLysql_fetch__array ($ref)) {
echo M<tr><td> ;".$reff [ 'name* ].,l</td>'1;
for($i=0; $i<5; $i++)
{
echo n<td> "<$hit[$i] [$reff [ rname* ] ] /^/t^'1;
}
echo ”<td><a href=delrefferer,php?
id_links=".$reff[1idelinks1].">yflajiMTb</a></td></tr>n;
echo ,,</table>,1;
?>
В листинге 14.29, как и на других сервисных страницах, число посещений с других ресурсов вычисляется при помощи специальной функции refferers, которая традиционно принимает три параметра: два для границ временных интервалов и один для указания принадлежности к странице. Функция возвращает масснв, число элементов которого равно числу записей в таблице links. Так как число записей может меняться совершенно произвольно, здесь нельзя воспользоваться функцией list(). Поэтому массив, возвращаемый фунцией refferers, присваивается элементу массива Shit, который определяется числовым индексом. То есть массив $hit по-прежнему двумерный, а не одномерный, как это может показаться сначала.
:Замечание J
Если число элементов в массиве заранее известно, следует использовать функцию list () t а не конструкцию, представленную в листинге 14.29, так как она сильно снижает читабельность кода.
После заполнения элементов массива $hit происходит формирование таб- 刀ниы с числом посещений с других ресурсов. Так как число этих ресурсов также заранее не известно, к таблице links осуществляется запрос, извлекающий все находящиеся в ней записи. Здесь вместо двойного цикла foreach-for, как в предыдущих сервисных страницах, используется двойной ЦИКЛ while-for.
Код функции refferers о, используемой для формирования двумерного массива $hit, приведен в листинге 14.30.
广…у,.幫疗.…寸..:..,"”:.:.?…〈у..,.::•”•? :,••::•■•::《,...,..;“:
i Листинг 14.30. Функция refferers
// $begin - число дней, которое необходимо вычесть из текущей даты,
// для того чтобы получить начальную точку временного интервала // $end - число лней^ которое необходимо вычесть из текущей даты,
// для того чтобы получить конечную точку временного интервала
// $id_page - первичный ключ записи таблицы pages, соответствующей
// странице сайта
function refferers($begin,$end,$id_page) {
$return array = array();
// Эта переменная определяет, осуществляется ли запрос к конкретной
// странице или ко всему сайту,
if (eirpty($id_page)) $tinp_page ="”;
else $tmp page ^ " AND id_page=$idjpageir;
// Запрос на статистику за временной интервал, определяемый
// параметрами $begin, $end if($begin — 0) $tmp2 = ”";
else $tmp2 ^ "AND refferer.putdate >= DATE_FORMAT{NOW(), '%YTm-*d 23:59:59')-
INTERVAL ^begin* DAY1';
$tmpl - "AND refferer.putdate < DATE^FORMAT(NOW(), ,%Y-%m-%d 23:59:59») +
INTERVAL '$end‘ DAY";
$@nd = $tn^>_page. $tnipl. $tmp2 ; // Формируем массив запросов
$query 工"SELECT name FROM links'1;
$ref = mysql_query (SqueryJ;
if(!$ref) puterror(^Ошибка при обращении к таблице рефереров");
if (mysql_num_rows ($ref) >0) {
while($reff = mysql_fetch_array($ref))
$query[$reff(^ame1]] = "SELECT COUNT(*)
FRCH refferer
WHERE name LIKE 1 %" . $reff [ ^ame1 ] .°%1 %门d";
// Выполняем SQL-запросы из массива $query foreach($query as $name => $query) {
$hit = mysql_query ($query);
if(!$hit) puterror("Ошибка при обращении к таблице рефереров");
$ геtu rn_a г ray[$nameI = mysql_result($hit,0); }
return $return_array;
В функции refferer о осуществляется запрос к таблице links с целью извлечения записей, соответствующих ресурсам, на которых расположена ссылка на сайт. Если в таблице links есть хотя бы одна запись в цикле while, в ассоциативный массив $ query заносятся SQL-запросы на извлечение числа переходов для каждого из ресурсов в таблице link— Следует отметить, что поиск соответствия в имени ресурса в URL, сохраненных в таблице refferer, осуществляется при помощи SQL-оператора like, для которого используется шаблон '^address%'*, где address ——сетевой адрес (или его часть) из таблицы links. Симоволы ”普”,обрамляющие сетевой адрес, позволяют находить соответствие, даже если администратор системы PowerCounter ввел адрес не целиком или если адрес содержит параметры запроса.
После того как массив SQL-запросов сформирован, осуществляется их выполнение в массиве foreach, результаты которого помещаются в ассоциативный массив $return_array, возвращаемый функцией.
На странице Ссылки перед таблицей размещается ссылка Добавить, позволяющая добавить новый ресурс в таблицу links. Переход по этой ссылке приводит к форме addreffererfbnn.php, содержимое которой приведено в листинге 14.31.
:^у ^K^*Ty^w.jM;;^v,^^V^^^*,iV^V.?4'',\iytV*'.>,V'V,t,.*WV ^^; ',,??•"《;,.'匸 ” X,¥ ':•?:'卷学聲1'”" W 中,,吋工V ”、:»:.-: :^1y次工'/ :二"书31^:;'三
Листинг 14.31 Файл addreffererform-php,.二£ 二
<?php
include **topcounter • php°;
?>
<center>
<a href^refferer.рЬр>Назад</а><Ьг>
<form action^addrefferer.php method=post>
Реферер : <input type=text name=reff> Примеры softtime.ru, mail.ru<br>
<input type=submit иа1ие=”До6аннть">
</fora>
Форма содержит единственное текстовое поле reff и кнопку для отправки его содержиNfого обработчику, расположенному в файле addrefferer.php, код которого приведен в листинге 14.32.
殲"^^^^^^j^^1 ЦШ|^" '4М!'哼W'^^^^j^^^jj^jJyyXT];悖4^y^ij^l^^j^jyLY .•'i'^'tij у :>^:学■:曜*小燃守 *? '.'* 巾气尹! »• ад» ” • %}”,*¥:焼¥*&*脚 汕叱,:
i Листинг 14.32, Файл addrefferer.php
;.» 品*工"3^\・口% ・..t/fV;^^ I “磊^^;1 j*?X %,■•'•;,・ r?rt til ;■",** J; ・%jw“ ;?“H“', **;;;•*. ・c“.T “. .* ;**'♦:、;'*”:.*\^^^,;;f;. r^^*l“『” J.^^j^^a.k力^^^V^ti^m. T* £oM^^ir?№
<?php
include "config.php";
if (en^ty($_POST【『reff1]))(
echo м <p>Heдопустимое имя ссылки</р>м;
echo H<p><A HREF=1refferer.php1>Вернуться</А></р>м;
exit ();
}
$query = "INSERT INTO links VALUES (NULL, ' r\ $_POST[1 reff1]."f)H; if (mysql_query ($query))
echo m<HTML><HEAD>
<META HTTP-EQDBZ=fRefresh1 CONTENT^10; UR^refferer.php1 > </HEADX/HTML>n;
} else puterror("Ошибка при добавлении нового ресурса0);
Код, расположенный в листинге 14.32, добавляет новую запись в таблицу links. В случае удачного осуществления запроса происходит автоматический переход на сервисную страницу Ссылки.
Помимо ссылки для добавления нового ресурса, напротив каждого из сетевых адресов в таблице на странице Ссылки имеется ссылка для удаления этого ресурса из системы. За удаление отвествен скрипт, расположенный в файле delrefferer.php, который принимает в качестве параметра значения первичного ключа id_iinks удаляемого ресурса. Содержимое файла delrefTerer.php представлено в листинге 14.33.
沪",'"""*" :@k純 **^^^У ^^1^^^^^ ^^!^f孑,^у^1^守:产? .^^ ^^^^^^^^ ■:豐^^^^^^^学/:' ^^1^^**^^^1: ■,■" *^^^^|^^** w^|w * ^jf* 此m^^^^^^ • ;Листинг 1433. Файл delrefferer.php
<?php
include ” config.php”;
П Формируем и выполняем SQL-запрос, направленный на удаление // записи с первиадым ключом id_links в таблице links
$query = "DELETE FROM links
WHERE id_links=,H. $_GET [丨 id_links」J ' \
if (mysql_query($query))
echo *f<HTML><HEAD>
<META HTTP-EQUIV=1 Refresh * CONTENT»1 0; URL=ref ferer * php * >
</HEADX/HTML>ri;
} else puterror(^Ошибка при удалении ресурсаn); ?>
Здесь» так же как и при добавлении ресурса, происходит формирование н выполение единственного SQL-запроса, направленного на удаление записи с первичным ключом id_links из таблицы links*
На этом разработка системы сбора и анализа статистической информации по посетителям сайта PowerCounter завершена.
ЧАСТЬ III
РазработкадополнительныхWeb-служб
Глава 15
Система мониторинга позиций сайта в поисковых системах
Система мониторинга сайта в поисковых системах (в дальнейшем — система мониторинга) позволяет отслеживать рейтинг исследуемых сайтов в поиско' вых системах по выбранным поисковым запросам. Под рейтингом понимается позиция сайтов в результатах поиска.
Данная система не использует специальных программных интерфейсов для доступа к результатам ——она имитирует действия посетителей поисковых ресурсов н работает только с той информацией, которая представлена на НТМL-страницах поисковых систем. Такого рода системы называются интеллектуальными агентами.
Функционирование данной системы опирается на алгоритмы поисковых систем в части структуры строк запросов и формы предоставления информации на НТМL-страницах, поэтому при изменении этих алгоритмов система может работать некорректно. Для поддержания ее функционирования необходимо постоянно отслеживать изменения в структуре строк запросов к поисковым серверам и в HTML-коде страниц с результатами поиска н при необходимости вносить соответствуютие изменения в код программы. Такие изменения происходят не часто, поэтому код остается функциональным в течение длительного времени.
Часть поисковых систем запрещает обращение к своим серверам интеллектуальным агентам. В частности, поисковая система Google явно запрещает обращение к ней программным способом. А, например, Yandex предупреждает, что может запретить обращение с IP-адресов, с которых будут проводиться несанкционированные автоматические запросы. Для получения и обработки результатов поиска программными средствами Yandex предоставляет специальный программный интерфейс. Эти меры предосторожности призваны повысить зашишенность серверов поисковых систем от хакерских атак. Следует особо отметить, что разрабатываемое в этой главе приложение не несет никаких деструктивных действий и по характеру своего предназначения не увеличивает нагрузку на серверы поисковых систем, и потому ма-
ловероятно, что его применение вызовет отрицательную реакцию со стороны владельцев поисковых систем.
Данное приложение разрабатывается в качестве практического примера реализации некоторых возможностей при разработке Web-приложений. Мы не можем гарантировать неизменность алгоритмов поисковых машин к моменту» когда вы будете читать эту главу, и соответственно не можем гарантировать полную работоспособность приведенного в этой главе кода в той части, которая касается алгоритмов поисковых машин. Однако, используя разработанное здесь приложение в качестве основы, его можно адаптировать под новые варианты движков поисковых систем.
Описание системы
С точки зрения пользователя вся система мониторинга представлена на одной странице index.php (рис. 15.1). При первом обращении к ней отображается форма ввода параметров, в соответствии с которыми будет проводиться мониторинг поисковых систем.
На странице представлены три поля для ввода параметров: Поисковая система, Сайт,①раза. Поле Поисковая система предстанлено в виде выпадающего списка, в котором пользователь системы должен выбрать поисковую систему, по которой будет проводиться мониторинг. Список выбора ограничен 3 поисковыми системами: Yandex, Rambler, Apo it. В поле Сайт должен быть внесен URL сайта, положение которого будет отслеживать система по выбранной поисковой фразе, вписанной в поле Фраза. Запуск скрипта на выполнение производится по нажатию кнопки Искать.
Результаты мониторинга выводятся на той же самой странице под формой ввода параметров (рис. 15.2).
При проведении мониторинга последовательно просматриваются страницы поисковой системы на предмет обнаружения ссылки на исследуемый ресурс, URL которого был внесен в поле Сайт. Глубина поиска по умолчанию составляет 10 страниц.
Если ссылка на исследуемый ресурс была найдена, то в результатах отображаются: номер страницы поисковой системы, ссылка на эту страницу и номер позиции исследуемого сайта в результатах поиска.
Если поиск не увенчался успехом, то в результатах мониторинга выводится соответствующее сообщение.
Разработка системы мониторинга
Код системы расположен в файле index.php.
ту?7^уу 瞿-у^мг' f&U 鴇^t^^^2^胃片I^I^F 單 S*^y^y*U'” * ^^/[^М^ЧЬ^^1^^^1Mj^yAliK^}^^*^^^**;"^,^1ydШ^^й^^^^
;Листинг 15.1, Файл Index.php
<?php
//Из суперглобаль ного массива $_POST[]
// получаем значения переменных, переданных из формы
$server = $_POST[*server'];
$site = $_POST[,site,);
$phrase = $_POST[1phrase1];
// Определение констант, соответствующих поисковым системам
define(YANDEX,1);
define(RAMBLER,2);
define(APORT,3);
?>
// Форма ввода параметров мониторинга
<form method=rrpost,f>
oelect name= и server °>
<option value=<?php echo YMIDEX; ?>
? if($server=YANDEX) echo “ selected**; ?»Yandex</option>
<option value-<?php echo RAMBLER; ?>
? if ($server==RAMBLER) echo ” selected"; ?»Rambler</option>
<option value=<?php echo APORT; ?>
? if ($server^^APORT) echo ” selected"; ?»Aport</option>
</select>
<br>
Сайт: <input type=,'text" 口ame="sit:e" $土2已="40" maxlength=l,256" value=*<? echo $site ?> гхЬг>
Фраза: <input type=l,text,1 name^"phrase" size="40” max工ength="256" value=1<? echo $phrase ?>1><br>
<input type="submitM уа!ие=пИскать **>
</form>
<?
// Если все поля б форме заполнеиы, то вызывается функция // siteposition() для проведения мониторинга
if (($server !="”)&& (phrase!=п'*) && (site!=,IH)) { siteposition(^server, $site, $phrase);
На странице выводится форма ввода параметров мониторинга. Параметр action формы не указан, т. е. скриптом обработки является этот же файл — index.php. Поэтому в начале файла производится инициализация переменных, переданных из формы. Значения переменных берутся из соответствующих элементов суперглобального массива $_post. Если все необходимые данные заполнены, то вызывается функция мониторинга — siteposition С) (листинг 15.2). Функция принимает три параметра $server, $site и $phrase для типа поисковой системы, сайта и искомой фразы.
Листинг 15.2. Функция siteposition ()〜土 Q
function siteposition($server, Ssite, $phrase) {
// numberpage — переменная, определяющая номер просматриваемой страницы $numberpage«O;
// Присваиваем переменным phrase, total и numberpage значенияг
// определяемые индивиду аль но для каждой поисковой системы.
// phrase — поисковая фраза
// total — переменная, определяющая максимальное число страниц,
// по KOTOpEJM будет произведен поиск» Для разных поисковых систем
// оно разное и подобрано таким образом, чтобы число просматриваемых
// страниц составило 10 штук.
switch{^server)
{
case YANDEX:
// Обрезка пробелов в начале к конце поисковой фразы $phrase = trim($phrase};
// CRL-кодирование поисковой фразы
// неалфавитно-числовые символы заменяются
// на знак % и соответствующую 1б-ричиую последователь ноеть
$phrase = rawurlencode($phrase|;
// 16—ричный знак пробела, появившийся после применения
// функции rawurlencode О, заменяется на знак +
$endstr = str replace (rt%20n, **+**,$phrase);
$total = 10;
$numberpage = -1;
break;
case APORT:
$phrase = trim($phrase);
$phrase = rawurlencode($phrase);
$end3tr 亠 str replacef^SO", е,+ п?$рЬгазе);
$total = 10;
$nuniberpage = -1;
break;
case RAMBLER:
$phrase = trim($phrase);
// Преобразуем кодировку запроса - Rambler работает c KOI8-R
$phrase = rawurlencode(convert_cyr_string($phrase,nwnr nk"));
$endstr = str_replace {и%20н, 'Ч-'1, $phrase);
$total = 152;
$nviniberpage = -14;
break;
I
// Последователь но просма три в аем страницы поисковых систем до тех
// пор, пока не будет найдена ссылка на искомый сайт в тексте страницы // (count($arr(0]) = 0) или число просмотренных страниц не достигнет // максимального значения ($numbeграде < $total)
while ((count($arr[0]) = 0) && ($numbeграде < $total))
switch($server)
(
// Увеличивая значение переменной $nuinbeграде, определяем
// текущую просматриваемую страницу с результатами поиска
// в поисковой системе.
// Число приращения зависит от конкретной поисковой системы
case YANDEX:
$numberpage += 1;
break;.
case APORT:
$numberpage += 1;
break;
case RAMBLER:
$numberpage +^ 15;
break;
}
// Вызываем функцию geturl () r генерирующую строку запроса
// для поисковой систем. Передаваемые параметры:
// numbeграде - переменная, определяющая номер просматриваемой
// страницы с результатами поиска в поисковой системе
// endstr — поисковая фраза после кодирования для применения
〃 в строке запроса
// server — имя поисковой системы
$strurl = geturl($numberpage,$endstr,$server);
/ / В переменную text заносится содержимое страницы поисковой системы, // которое возвращается функцией openpage()
$text = openpage($strurl);
// Поиск ссылки на сайт в тексте страницы поисковой системы
// с помощью функции findsite(). Результаты поиска заносятся
//в массив агг[].
$аЕГ = findsite($site, $text);
}
// Если число элементов массива агг[] больше нуля, значив, ссылка
// на искомый сайт была найдена в тексте просмотренной страницы
// поисковой системы, и необходимо показать результаты мониторинга.
if (count ($arr [0]) >0)
switch(Sserver)
{
// В зависимости от поисковой системы формируются результаты
// мониторинга поисковой системы.
// Функция pattern() возвращает результаты параметры дая
// регулярного выражения, с помощью которого будет производиться
// поиск номера позиции сайта в тексте
// страницы поисковой системы.
626626Насть 〃/. Разработка дополнительных Web-служб
626
626
// Функция findposition() возвращает номер позиции сайта case YANDEX:
// Вывод информации о странице
echo "Ссыпка на сайт найдена на странице:".
($numberpage+l) . rt<br>H;
echo мПерейти по ссылке: <а href«•$strur1f >Яндекс</a><br>M;
// Определение и вывод позиции запроса
list($startpattern, $endpattern) = pattern(YANDEX);
echo н<Ьг>Позиция: ".
findposition($server, $site, $text, $startpattern, $endpattern);
break;,
case APORT:
// Вывод информации о странице
echo "Ссылка на сайт найдена на странице:".
($nuiriberpage+l) J<br>“;
echo 11 Перейти по ссылке: <а href®1 $strurl1 >Алорт</ахЬг>м;
// Определение и вывод позиции запроса
list($startpattern, $endpattern) = pattern(APORT);
echo **<Ьг>Позиция: ".
findposition($server, $site, $text, $startpattern, $endpattern); break;
case RAMBLER:.
// Преобразовываем ссылку для решения проблем с кодировками
$phrase « rawurlencode(convert_cyr_string(
rawurIde code ($phrase), ”k”,%"));
$endstr = str_replace(n%20H,"+°,$phrase);
$strurl=geturl($numberpage,$endstr,$server);
// Вычисляем страницу, на которой находится запрашиваемый сайт $numbeграде = $nuroberpage/15;
$numberpage = (int)$numbeграде;
if($numberpage * 15 = 0 && $numberpage>15) $numberpage++;
echo nСсылка на сайт найдена на странице:”
.($numbei7page+l) J<br>”;
// Выводим ссылку на страницу
echo ^Перейти по ссылке: <а href=1$strurl1>Рамйлер</а><Ьг>";
// Определение и вывод позиции запроса
list($startpattern, $endpattern) = pattern(RAMBLER);
echo п<Ьг>Позиция: n.findposition($server, $site, $text, $startpattem, $endpattem);
// Так как ссылка на искомый сайт найдена, то просмотр страниц // поисковых систем в цикле While можно прекратить break;
else
{
echo "На 100 первых страницах ссылка на сайт отсутствует^;
В первом условном операторе switch происходит подготовка поисковой фразы для передачи ее поисковой системе. При помощи функции rawuriencode о происходит преобразование фразы в безопасный формат для передачи в строке запроса. Все неалфавитно-числовые символы заменяются на знак % и соответствующую шестнадцатеричную последовательность. После чего знак 电20, соответствующий пробелу, заменяется на +, интерпретируемый поисковыми системами как логическое
Далее в цикле while происходит поиск страницы, на которой имеется первое вхождение искомого сайта. Условием выхода нз цикла является нахождение сайта или достижение 10-й страницы поисковой системы. В каждом из циклов происходит вызов функции geturi() (листинг 15.3), генерирую* щей строку запроса для соответствующей поисковой системы. Функция принимает три параметра:
$numberpage — определяет номер страницы поисковой системы;
$endstr — поисковая фраза после преобразования в безопасный режим для передачи через строку запроса;
$server — имя поисковой системы.
Функция возвращает строку запроса в соответствии с форматом поисковой системы для открытия страницы номер $numberpage.
""……:蕊:二...:...1—.::""……:蕊:二...:...1—.::;■■ *;;M>^^4JJ\:" • *iw:::y.:,▲*,• * w vm»*" • *: **:*лщ :Листинг 15.3. Функция geturl ()
""……
:蕊:二...:...1—.::
""……
:蕊:二...:...1—.::
function geturl (Snurnbe граде, $ endstr, $ server) {
if($server 一 YANDEX) return Mhttp://www,yandex*ru/yandpage?
q^8188361б76p=$n1ДIrlberpage^iag^d&rpt=rad&qs^text%ЗD,l .$endstr;
if ($server = RAMBLER) return **http://search.rambler.ru/srch?
words=**. $endstr. H&start=l1.$numbeграде;
if{$server — ABORT) return "http://sm.aport.ru/scripts/template.dll? r=". $ endstr, ri&That=stdip=,f, $ number page ・ ”&
H 工 D=l_2_3_4_5_6_7_8_9_10_ll_12」3”;
return
Возвращенная строка передается функции openpage(), которая загружает страницу по переданному ей URL, и возвращает ее в виде текстовой переменной (листинг 15.4). После чего данная переменная передается в качестве второго аргумента функции findsite () (см. листинг 15.5), осуществляющей поиск сайта на данной странице.
:Листинг 15.4* Функция openpage (>
】.,;“ :i'i'aVi'a'i«It^i7tt ■ >Vb k ■ к'в'Л^"!^'Л A>Va> •<>'<«< ;;;・4;;K ■■■■■ : •« >'■ > >> «'■«'•SiSi» ii г А*?*Й'1,Л?5Йпй'Л fm S/riliirt ; rli';『*;; *;t;£¥;HT3 it^ititi Silt» » i*7iSi ♦ « » • 'wi i *'r* i"t« « • ;' »'?И J«i?«JiSStfBf •
// Функция openpage() аткр&шает страницу, URL которой передан
//в переменной $link, и возвращает содержимое этой страницы function openpage($link)
{
// Функция fopen() открывает файл для чтения и возвращает
// указатель файла.
$fd = fopen{$link, У);
§text=,r";
if (!$fd) echo нЗапрашиваемая страница не найдена**;
else
{
// Чтение содержимого файла в переменную text while (!feof ($fd))
{
$text .^ fgets($fd, 4096);
// Закрыть открытый указатель файла
fclose ($fd); return $text;
}
В функцию openpage() передается URL (переменная link), соответствующий строке запроса поисковой системы. Производится обращение к поисковой системе с помощью функции fopen (), которая открывает страницу по переданному URL. Затем содержимое открытой страницы поисковой
системы заносится в переменную text, значение которой и возвращается данной функцией.
今绵**笛辞制紀h岁眼堂曲}产卜¥1厘5斗結1T編串包*性辭扭抬單拓|?咎|"wД券?、斑比靜此|尊上£¥|¥?唳>*毋*二*"一:["屮''"'!"3¥”""1*""|1叩*三经施1*¥貰、”
^Листинг 15Л. функция findsite<)
function findsite($site, $text)
{
preg_match__all(п/$зхЪе/1ипг $text, $arr);
return $arr;
Функция findsitefi с помощью регулярного выражения производит поиск названия искомого сайта в тексте страницы (переменная text). Найденные участки текста возвращаются в массиве агг[].
Если страница, на которой имеется ссылка на сайт из HTML-формы, найдена, производится повторный ее анализ с целью определения позиции вхождения ссылки на сайт. Это осуществляется в третьем операторе switch функции siteposition о (см. листинг 15.2). При помощи функции patternO» принимающей константы, соответствуюшие поисковой системе, формируются шаблоны для поиска позиции вхождения ссылки на странице (листинг 15.6).
:Листинг 15.6* Функция pattern О二Л ;
function pattern($ typepat tern)
(
switch($typepattern)
case YANDEX:
return array (11 <LI value=\d{1,3}>\D*M, и [л ! ] *<\/A>");
break;
case APORT:
return array (n<FONT class=sm>rfr У FONT〉”); break;
case RAMBLER:
return array (И<Ы class^fsite*,^, м [л ! ] *<\/A>"); break;
}
Функция возвращает параметры, которые будут применены при поиске позиций сайта в тексте страницы с результатами поиска в поисковой системе.
21 Зак, 118]
Данные параметры уникальны для каждой поисковой системы. Их актуальность следует постоянно отслеживать и при необходимости вносить изменения. Например, для поисковой системы Aport эти параметры равны "<font ciass=sm>H и m</font>*\ Это значит, что номер позиции сайта в тексте страницы поисковой системы Aport расположен между〈font class=sm> и </font>* Например, для сайта, занимающего позицию 20, в тексте страницы будет расположен код <font class=sm>20</FONT>.
Возвращенные функцией pattern о шаблоны передаются функции findposition{), которая осуществляет поиск позиции в содержимом страницы, передаваемой через параметр $text (листинг 15.7).
際口"弱炉蒙"中*弊開■"?:■,岷綢伸艷笹軀^:W 耽W ……"搭狷找i皆部弊■"寸密颦窿喫駅 5寸%修职 /券|:患暫翡闡苗弋叫鸣:里魯题制宏鱒煤露囈聲崂
^ Листинг 15.7. Функция findposition ()
// Функция findposition() определяет позицию искомого сайта на странице // с результатам поиска в поисковой системе.
// Передаваемые параметры:
// server — имя поисковой системы
// site — имя искомого сайта
// text — содержимое страницы с результатами поиска в поисковой системе
// startpattern и endpattern — параметры для регулярного выражения, они // определяются функцией pattern()(листинг 15.5).
function findposition($server, $site, $text, $startpatternf $endpattem)
switch($server)
{
// Поиск позиции сайта в тексте страницы.
// Для каждой поисковой системы применяется свой алгоритм поиска case YANDEX:
// Регулярное вьлражение, о суще с твляющее поиск позиции сайта
preg_ma t ch_a11(r,/$startpattern* $site.$endpattern/iU",$text,$arr); ргед^^Ь_а11(н/\б(1,3}>/1инг$агг(0] [0],$position);
$numberposition = substr($position[0][0], 0,
strlen($position[0][0]-l));
return $nuroberposition;
case APORT:
$pos = strpos($textr $site);
$text = substr($text,0,$pos); while(true)
{
$pos = strpos ($text, "cTABLE'1);
if($pos =^= false) break;
$text = substr($text,$pos+7);
// Начало вырезаемого номера
$pos = strpos($textr n<FONT class=sm>n);
// Число символов в бьрезаемой строке
$lenght = strpos($text,"</FONT>");
$lenght ^ ^lenght - $pos - 15;
// Получаем позицию
return substr($text,$pos+l5 ? $lenght);
case RAMBLER:
$pos = strpos($text,и<о1 startu”);
$text = sobstr($text,$pos+ll);
$pos_begin ^ substr ($text, 0, strpos ($text, "X1*1'));
// Находим текст, относящийся к сайту, и вьфезаем его for($i=0; $i<20; $i++) (
$pos = strpos($text,"<li class=fsite>H);
$posend = strpos($text,"<li class=fsite>'\ ($pos+15));
$yes ^ strpos(substr($text,$pos,($posend-$pos)),$site);
if($yes >0) break;
else $text ^ substr($textf $posend-l);
$pos_begin++;
J
return $pos_begin;
default:
return array(M<DIV class=arrows>,1, ”<\/div>"};
Определение позиции сайта основывается на анализе HTML-кода страницы поисковой системы. Чтобы решать эту задачу, необходимо первоначально просмотреть текст страницы, определить, возле каких тегов находится номер позиции, задать правила, по которым можно будет вырезать искомый номер из текста страницы, сформировать шаблоны для регулярных выражений и условия поиска. Алгоритмы определения позиций сайта необходимо менять каждый раз, когда поисковые системы меняют дизайн своих страниц с результатами поиска.
Рассмотрим алгоритм определения позиции сайта на примере поисковой системы Aport.
В нижеприведенном листинге показана часть страницы с результатами поиска поисковой системы Aport (листинг 15.8).
i Листинг 15・8. Текст страницы поисковой системы Aport
<1— Верхняя часть страницы с результатами поиска -->
<TABLE cellSpacing=O cell Padding-0 wid七 h=777 class^rm border=Oxtr class=L><TD colspan=2><td></tr>
<TR><TD vAlign=top align=iniddle width=3% rowspan^2>
<DIV styUMARGIN-TOP: 2pt" align=center>
<FONT class^sm>ll</FO^JT></div><TD vAlign-top width=97%>
<DIV style=margin-top:2pt>
<a class=tbl href=http://softtime.ru target=_blank>Разработка сайтов. Web <Ь>студиж/Ь> SoftTime» Нижний Новгород</а><ВКХ0Х7 class=sdg> 工T- <b><font color="#ffOOOOl,>CTyflWM</font></b> <SoftTime> <br> Наша IT- <b><font co 1 о r=11 # f f О О 0 011 > студая< / font > < /b> <SoftTime> (Нижний Новгород), специализируется ... спектра услуг s области разработки <b><font color='l#ff0000ll>Web</font></b>-cajtTOB и ПО* <br> О нашей <bxfont
color= "#ff0000ir>Web</font></Ь> <bxfont со1ог=**#Г£00000>студии</^^></Ь> </DIV>
<!——Нижняя часть страницы с результатами поиска -->
Здесь приведен искомый блок HTML-кода, в котором находится ссылка на сайт softtime.ru. По результатам поиска сайт занимает 11-ую позицию, ее номер расположен сразу после тега <гаыт class=sm>.
Первоначально в переменной $text находится вся страница поисковой системы. Сначала ищем в тексте подстроку, соответствующую URL искомого сайта и запоминаем номер позиции, где найдена подстрока, $pom = strpos($textr $site)
Далее для удобства вырезаем участок текста страницы от начала но найденной подстроки с URL сайта. Так как у Apoit помер позиции в коде HTML расположен до URL сайта.
$text = substr($text,0, $pos);
Далее в цикле while ищем позипию последней найденной подстроки m<table". Эта позиция будет соответствовать началу блока HTML-кода с описанием искомого сайта (л и стан г 15.8)*
Теперь осталось найти номер позиции. Если посмотреть HTML-код страницы, то номер позиции будет расположен сразу после подстроки "〈font ciass=sm>”. Мы ищем позицию, где начинается эта подстрока с помощью функции strpos о и далее вырезаем номер функцией substr о. Таким образом, позиция сайта найдена.
Алгоритмы поиска для всех поисковых систем будут уникальны, так как уникален HTML-код страниц разных поисковых систем.
Глава 16FTP-менеджерГлава 16FTP-менеджер
Глава 16
FTP-менеджер
Глава 16
FTP-менеджер
Протокол передачи файлов FTP (File Transfer Protocol) — один из старейших прикладных протоколов, появившийся задолго до Web, в 1971 году. До начала 90-х годов на долю FTP приходилась половина трафика в сета Интернет. Данный протокол и по сегодняшний день используется для распространения программного обеспечения и доступа к удаленным хостам* В данной главе будет разработано Web-приложение, обеспечивающее доступ по HTTP к уда- летому серверу FTP, как с другого удаленного сервера, так и с локальной машины, на которой работает сервер Apache с поддержкой PHP.
( Замечание )
Протокол FTP описан в RFC959 (http://www.faqs.org/rfcs/rfc959.html).
FTP-менеджер должен предоставлять следующие возможности:
口 свободное перемещение по структуре FTP-сервера;
口 предоставление информации о владельцах, группах, обладающих правами доступа к файлам и директориям;
口 создание, переименование, удаление и изменение прав доступа к директориям;
О загрузка на сервер и с сервера файлов, а также их переименование и изменение прав доступа*
[ Замечание )
На прилагаемом к кнм「е компакт-диске можно найти готовые для использования коды приводимого в данной главе FTP-менеджера. Загрузить последнюю версию Web-приложения можно по адресу http://www.softtime.ru/lnfo/ downloads.php. Помощь по установке, настройке, а также модификации под свои задачи вы можете получить на форуме http://www.softtime.ru/forum/.
Web-приложение будет состоять из 12 файлов:
chdirform.php — HTML-форма для переименования директорий и изменения прав доступа к ним;
口 chdir.php — обработчик форм chdirfbrm.php и editfilefbrm.php;
口 config.php — конфигурационный файл Web-приложения, служит для установки соединения с FTP-сервером;
download.php — скрипт загрузки файла с FTP-сервера;
口 editfilefbrm.php ——HTML-форма для переименования файлов и изменения прав доступа к ним;
index.php —— главный файл, который несет ответственность за отображение содержимого директорий, расположенных на FTP-сервере;
mkdirforrn>php — HTML-форма создания директории на FTP-сервере;
mkdir.php — обработчик формы mkdirfbrm.php;
口 rmdir.php — скрипт, предназначенный для удаления директории с FTP- сервера;
rmfile.php ——скрипт, предназначенный для удаления файла с FTP- сервера;
uploadfbrm.php ——HTML-Оорма для загрузки файлов на FTP-сервер;
口 upload.php — обработчик формы uploadfbrm.php.
Кроме приведенных в списке файлов, для работы FTP-менеджера потребуется директория files, в которой будут разметаться временные файлы, полученные от FTP-сервера и предназначенные для дальнейшей передачи по HTTP-протоколу.
Конфигуранионный файл config.php (листинг 16.1) предназначен для установления соединения с FTP-сервером.
¥WM* 得.僧{曲¥料¥ 喉卅弱姑№ 豊龜2出渺*ьяд电MW3出吧卅摸銓飽開龄*型屮-1開■盥券:舞屋*£У空学 虹題*睨111 *融学&(酬酬^•諸屛fay*N^*竺竺t挣线1 辐HWfi^y
;Листинг 16.1. Файл config.php
<?php
// Адрес FTP-сервера
$ftp_server = "ftp.5erver.ru";
// Пользователь
$ftp_user = "user";
// Пароль
$ f tp_pas svjord = "password";
// Устанавливаем время исполнения скрипта 120 с
sеt_time_limit (120);
// Пытаемся установить соединение с FTP-сервером
$link = ftp_connect($ftp_serverl;
if(•$link) puterror("К сожалению, не удается установить соединение с FTP-сервером $ftp_serverM);
// Осуществляем регистрацию на сервере
$ login = ftp__login ($link, $ftp_user, $ftp_password);
//$login = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);
if(!$login) puterror(HK сожалению, не удается зарегистрироваться на сервере. Проверьте регистрационные данные * п);
// Небольшая вспомогательная функция, которая выводит сообщение
// об ошибке в окно браузера function puterror($message) {
echo v<center><p с1азз^Не1р>$теззаде</р>и;
exit();
)
?>
Для установки соединения в переменные $ftp_server, $ftp_user и $ftp_password необходимо поместить адрес FTP-сервера, имя пользователя и его пароль соответственно.
( Замечание )
Если доступ осуществляется к анонимному FTP-серверу, то в качестве имени пользователя можно ввести anonymous t а в качестве пароля— адрес электронной почты.
Соединение с сервером устанавливается при помощи функции ftp_connect, имеющей следующий синтаксис:
resource ftp_connect (string host [, int port [, iht timeout]]}
Функция принимает в качестве первого параметра host адрес FTP-сервера. Через второй необязательный параметр port можно передать значение порта, по которому необходимо установить соединение. По умолчанию соединение устанавливается по стандартному 21 порту. При помощи третьего параметра timeout можно установить время в секундах, в течение которого функция будет ожидать ответа от сервера. По умолчанию если данный параметр не задан, функция ожидает ответ в течение 90 секунд.
В случае если установить соединение не удалось, функция возвращает false. При успешном соединении с FTP-сервером функция возвращает дескриптор соединения, который далее используется для работы с FTP- сервером.
После установки соединения с FTP-сервером осуществляется регистрация пользователя при помощи функции ftp_loginO, имеющей следующий синтаксис:
bool ftp—login (resource link, string username, string password)
Функция принимает три параметра: дескриптор потока link, возвращаемый функцией f tp_connect (), имя пользователя username н его пароль password. В случае успешной регистрации на FTP-сервере функция возвращает true, в противном случае — false*
Кроме кода соединения с FTP-сервером, файл config.php (см* листинг 16.1) содержит небольшую вспомогательную функцию puterror (), принимающую в качестве единственного параметра строку, которую она выводит в окно браузера, после чего останавливает выполнение скрипта. Данная функция будет использоваться для вывода сообщений о различных ошибках в FTP-менеджере.
Файл index.php является главным файлом Web-приложения» он несет ответственность за отображение содержимого выбранного каталога на FTP- сервере. Внешний вид главной страницы показан на рис. 16.1. Содержимое текущего каталога выводится в виде таблицы, каждая строка которой соответствует отдельному файлу или директории.
Рис. 16.1. Главная страница FTP-менеджера
В ячейках таблицы размещена информация о правах доступа, количестве блоков, занимаемых файлом, владельце файла или директории, времени создания и название файла или директории. Для файла также выводится его размер в байтах.
Кроме этой информации, напротив каждого из файлов или директорий имеются две управляющие ссылки: Удалить и Исправить, позволяющие удалить директорию или файл и изменить их названия и права доступа.
Названия файлов и директорий являются гиперссылками. Переход по ссылке в виде названия директории открывает страницу с содержимым выбранной директории (как это продемонстрировано на рис. 16J), а переход по ссылке в виде названия файла приводит к загрузке данного файла с FTP- сервера на клиентскую машину.
〔 Замечание )
Информация в таблице (см. рис. 16.1) предоставляется в формате UNIX- утилиты 1s с ключом -1. Данная утилита возвращает содержимое текущего каталога в расширенном формате.
В конце таблицы приведены две управляющие ссылки: Создать каталог и Загрузить файл, позволяющие создать новый каталог в текущей директории и загрузить в нее файл с клиентской машины. Код файла index.php представлен в листинге 16.2.
LЛистинг 16.2. Файл index.php
<?р!ф-
// Устана влива^л соединение с ЕТР-сернером require_once("config.php"};
// Определяем тип операционной системы сервера,
// предоставляющего услуги FTP
$type = ftp_systype ($link);
// Если операционная система отличается от UNIX, // прекращаем дальнейшую работу
i員Stype != "UNIX”) puterror("К сожалению, на удаленном сервере операционная система, не совместимая с UNIX");
// Извлекаем из строки запроса значение директории
if (isset ($_GET{ ,dir, ]}) $dir = $_GET「dir');
else $dir = "/”;
// Заменяем символы %『используемые для передачи пути через URL // на символы /
$dir = з^_гер1асе^Г\ "/' $dir);
// Получаем все файлы корневого каталога
// Дескриптор соединения $link получен в config.php
$file_list = ftp_rawlist($link/ $dir);
〃 Закрываем соединение c FTP*сервером ftp_close ($link);
// Шводим текущую директорию
echo **<р с1азз^Ье1р>Текущая папка - ftp г //м. $ftp_seirver. $dir. "</р>н;
// Если текущий каталог не является корневым,
〃 выводим ссылку на один уровень вверх if($dir != "/”)
{
// Удаляем иэ переменной $dir последнюю директорию
$pos = strrpos($dir, ,/,);
if($pos>0) $dirup н substr($dir, 0, $pos);
else $dirup ^ "/";
echo "<a href^index,php?
dir^".str_replace('/1 '%', Sdirup) • ">Еверх</ахЬг>";
}
〃 Выводим содержимое каталога
// Формируем таблицу
echo "<table>
<tr>
<td>npasa доступа</Ъс!>
<td>блoки</td>
<td>группа</td>
^^пользователь </td>
<td>pasMep, 6a货itav/td〉
<td>speb^ co3BaHMH</td>
<td>finbsp;</td>
<td colspan=2>flefiCTBK^</td>
</tr>M;
foreach($file_list as $file)
{
〃 Разбиваем строку по пробельным символам list($асс,
$bloks,
Sgroup,
$user,
$sizef
$month,
$day,
$year,
$file) = preg_split (l7[\s]+/,\ $file);
// Если файл начинается с точки - игнорируем его if(substr($filer 0, 1) == '.') continue;
// Формируем ссылку на удаление файла
$delete = "-";
$edit ="-";
// Для формирования корректных ссылок в таблице
// файлов переменной $dir добавляем символ /
if ($dir != *'/”)$url = st ^replace С/1, '*251 $dir J7H .$file);
else $url ^ str__replace (1 /1 < ,彩25,, $dir. $file);
// Определяем, является ли объект директорией
if(substr($асс, 0, 1) = *d*)
{
$file ^ ,f<bxa href=index, php?dir=$url>$file</a></b>M ;
$delete = "<a href=nndir.php?diг=$иг1>Удалить</а>Г|;
$edit = "<a href^chdirform.php?dir^$url&acc=$acc>HcnpaBMTb</a>H;
$size = ”Gt;DIR>"; // "<DIR>"
}
// Определяем, является ли объект файлом
if (substr ($асс# 0, 1)=」) {
$file = м<а href download. php?dir=$url>$file</a>**;
^delete = "<a href=rmflie.рЬр?б1г=$иг1>Удапить</а>";
$edit = "<a href^editfileforтu.php?dir=$llrl&acc^$acc>Иcпpaвить</a>,,; }
// Формируем строку таблицы echo "<tr>
<td>$acc</td>
<td>$bloks</td>
<td>$ g roup</1d>
<td>$user</td>
<td>$size</td>
<td>$day $month $year</td>
<td>$file</td>
<td>$delete</td>
<td>$edit</td> v/tG";
}
echo *,</table>t,;
// Ссылки на создание каталога и загрузку файла echo ”<а href=mkdirforт[\*php?dir^,,.
str_replace(•/*, ф%25*, $dir).">Создать каталог</а>н; echo "<а href=uploadform*php?dir=1'.
str_replace (* /1» *%25*, $dir) . м>3агрузить файл</а>11; ?>
Файл index.php (см. листинг 16.2) принимает единственный аргумент dir, в котором передается путь текущей директории относительно корневой директории FTP-сервера. Так как формат URL не позволяет использовать символ слэша (/), вместо него используется символ %, который заменяется на слэш при указании пути в функциях PHP.
В начале файла при помощи оператора require_once() в тело скрипта воючается файл config.php (см. листинг 16.1), обеспечивающий соединение с FTP-сервером. После чего при помощи функции ftp^systype(J проверяется тип операционной системы на ЕТР-сервере. Функция принимает единственный параметр — дескриптор соединения с сервером» возвращаемый функцией ftp_connect().
В случае если на удаленном сервере развернута UNIX-подобная операционная система, из строки запроса извлекается значение параметра dir, через который передается путь относительно корневого каталога FTP-сервера. Как уже упоминалось выше, директории в пути разделены символами процента (%)т которые при помощи функции str_replace о заменяются слэшами (/). После этого при помоши функции ftp_rawiist () FTP-серверу от- праалАется команда ftp list, аналогичная по действию UNIX-утилите is -1. Синтаксис функции ftp_rawlist ()представлен ниже:
array ftp_raw.List (resource link, string directory)
Параметр link является дескриптором соединения, возвращаемым функцией ftp_connect о, вызов которой осуществляется в файле config.php (см. листинг 16.1), Второй параметр directory является относительным путем к директории, содержимое которой необходимо возвратить.
Функция возвращает массив строк, каждая из которых содержит информацию о подкаталоге млн файле. В файле index.php в качестве такого массива выступает $file_list.
Если директория не является корневой, то на страницу выводится ссылка возврата на уровень выше, после чего происходит формирование таблицы с информацией о содержимом текущей директории. Строки таблицы форм и-
руются последовательным обходом массива $file_iist при помощи цикла foreach. Столбцы в строках массива $fiie_iist, возвращаемого функцией ftp connect (), разделены произвольным числом пробелов:
drwxr-x3 softtime softtijne 4096 Jul 12 12:16 public_ftp
Так как чиаю пробелов произвольное, использовать функции explode ()и strtoko для разбивки строки на отдельные подстроки не получится. Вместо них следует использовать функцию preg_split I:)t позволяющую разбить строку по регулярному выражению. Синтаксис функции preg_spiit () представлен ниже:
array preg_split (string pattern,
string subject [, int limit [, int flags)J)
Первый аргумент pattern задает регулярное выражение подстроки разделителя в формате Perl. Во втором аргументе subject передается исходная строка, котора>1 подвергается разбиению. Необязательный параметр limit позволяет наложить ограничение на число возвращаемых функцией подстрок. Если данный параметр принимает значение —I или не передан, ограничение отсутствует. Последний необязательный параметр flags может быть комбинацией трех значений:
口 PREG_sPL工t_no_empty — в случае если этот флаг указан, функция preg_spiit()вернет только непустые подстроки;
PREG—SPL工T—DELiM CAPTURE — в случае если этот флаг указан, выражение, заключенное в круглые скобки в разделяющем шаблоне, также извлекается из заданной строки и возвращается функцией;
preg_split_offset_capture —— в случае естш этот флаг установлен, для каждой найденной подстроки будет указана ее позиция в исходной строке. Необходимо помнить, что этот флаг меняет формат возвращаемых данных: каждое вхождение возвращается в виде массива, в нулевом элементе которого содержится найденная подстрока, а в первом ——смещение.
В листинге 16*2 в качестве подстроки -разделителя используется регулярное выражение ”/[\sj+/“,которое интерпретируется как один или несколько обобщенных пробелов.
Символ обобщенного пробела [\s] аналогичен [ \f\n\r\t\v]. Таким образом, он включает не только символ пробела, но и символы конца файла, переноса строки, возврата каретки, символы обычной и вертикальной табуляции.
Функция preg_spiit () возвращает массив из 9 элементов, которые при помощи функции list .О помещаются в следующие переменные: 口 $асс — режим доступа;
$bloks — число блоков;
口 $ group — группа-владелец файла или директории;
口 $user — пользователь-владелец файла или директории;
口 $ size — размер файла в байтах;
$month — месяц создания файла или директории;
口 $day — день создания файла или директории;
口 $уеаг — год создания файла или директории;
$fiie ——название файла или директории.
Для дальнейшего описания работы FTP-менеджера необходимо остановиться на файловой системе и связанной с ней системе безопасности UNIX. Подстрока, определяющая режим доступа $асс (первая колонка в таблице на рис 16,1), содержит 10 символов. Первый символ характеризует тип файла: 口 ——обычный файл;
d — директория;
口 1 — символическая ссылка;
口 $ — сокет;
口 р — именованный канал.
На практике при работе с FTP встречаются только первые два вида файлов: директории и обычные файлы.
Оставшиеся 9 символов определяют режим доступа к файлу или директории. При этом подстрока разбивается на три группы, по три символа каждая:
Права владельца файла;
口 Права группы владельца;
口 Права остальных пользователей.
В рамках каждой из этих трех групп существует три вида разрешений:
г — право чтения данного файла;
w — право записи/изменения данного файла;
х — право выполнения данного файла, если он является сценарием или программой (для директорий ——право ее просмотра).
Таким образом, строка **-rw-r—г—может быть интерпретирована как принадлежащая обычному файлу, для владельца которого установлено право чтения и записи, а для пользователей, входящих в группу владельца файлов, и всех остальных пользователей установлено только право чтения. Строка Hdrwxr-xr-xn принадлежит директории, для владельца которой установлены все права: чтения, записи и просмотра, а для всех остальных пользователей —— только чтения и просмотра.
После разбиения строки в файле index.php (см. листинг 16.2) анализируется первый символ в названии файла (директории). Если он совпадает с символом точки (.), то файл (директория) относится к так называемым скрытым файлам — дальнейшая обработка такого файла игнорируется за счет перехода к следуюшему циклу при помощи ключевого слова continue. Это позволяет избежать вывода специальных директорий и обозначающих текущую и вышележащую директории.
После этого происходит формирование URL для файла или директории, помещаемых в переменную $url, все слэши в URL заменяются символом процента (%), так как данная переменная используется для передачи в качестве параметра скриптам загрузки н переименования файлов.
В зависимости от того, является файл обычным файлом или директорией, формируется содержание временных переменных $deiete и $edit для управляющих ссылок Удалить и Исправить соответственно. В переменную $file в случае обычного файла помещается управляющая ссылка на загрузку файла, а в случае директории ——ссылка на переход к содержимому данной директории. Для директорий переменная $size заменяется строкой- признаком директории "<d工r>"・
Завершает цикл вывод строки таблицы, формируемой из временных пере* менных. Файл index.php завершается ссылкой на добавление новой днректо- рии и загрузку файла на FTP-сервер.
Добавление новой директории осуществляется при помощи НТМ L-формы mkdirfbim.php, внешний вид которой показан на рис. 16.2. Содержимое файла представлено в листинге 16.3.
<?php
// Устанавливаем соединение с FTP-сервером
// require_once(иconfig,php”);
// Если не переданы параметры - настраиваем
// форму на добавление директории
if(!isset($action)) $action = "mkdir.php";
if (1 isset ($button)) $button = Добавить11;
if(!isset($title)) $title = "Добавление директории";
// Права доступа по умолчанию для
// пользователя
if(!isset($ur)) $ur = "checked";
if (J isset ($uw)) $uw 二"checked11;
if (!isset ($tix) ) $ux = "checked";
// Права доступа до// Права доступа доумолчанию дляумолчанию для// группыif(lisset($gr^) if(!isset($gw)) if (! isset〔$/)')// группыif(lisset($gr^) if(!isset($gw)) if (! isset〔$/)')$gr = "checked”;$gw = "";$gx ^ "checked11;$gr = "checked”;$gw = "";$gx ^ "checked11;// Права доступа по// Права доступа поумолчанию дляумолчанию для
// Права доступа до
// Права доступа до
умолчанию для
умолчанию для
// группы
if(lisset($gr^) if(!isset($gw)) if (! isset〔$/)')
// группы
if(lisset($gr^) if(!isset($gw)) if (! isset〔$/)')
$gr = "checked”;
$gw = "";
$gx ^ "checked11;
$gr = "checked”;
$gw = "";
$gx ^ "checked11;
// Права доступа по
// Права доступа по
умолчанию для
умолчанию для
// остальных пользователей (не входящих в группу) if (lisset($or)) $or = **checked";
if(lisset($ow)) $ow = ”“;
if(Iisset($ox)) $ox = "checked”;
// Извлекаем из строки запроса текущую директорию if (! isset ($dir)) $dir - $_GET[ Mir* ];
<form action-<?php echo faction; ?> wethod=post>
Название директории : <input type^text name^name value=*<?= $name ?〉‘> Права доступа :
《input type=checkbox name^ur <?php echo $ur;
?»
<input type^checkbox name=uw <?php echo $uw;
?»
<input t ype-chec icbox name=ux <?php echo $ux; snbsp;^nbsp;
?»
<input type=checkbox name=gr <?php echo $gr;
?»
<input typG=checkbox name=gw <?php echo $gw;
?»
<input type=checkbox name=gx <?php echo $gx;
?»
<input type=checkbox name*or <?php echo $or;
?»
<input type=checkbox name=ow <?php echo $ow;
?»
<input type=checkbox name=ox <?php echo $ox;
?»
<input type=submit value=<?php echo $button;?»
<input type=hidden name^dir value^=<?php echo $dir; ?»
<input type^hidden name=old value=<?php echo $name;?»
</form>
Форма содержит текстовое поле name для названия директории и 9 флажков, объединенных в 3 группы, для установки прав доступа. Имя каждого из флажков состоит из двух символов, первый символ может принимать знaчe^ ния u, g н。для пользователя, груп пы и остальных пользователей соответственно. Второй символ может принимать значения г, w и х, для чтения» записи и просмотра директории.
Рис. 16.2. Форма для добавления новой директорииРис. 16.2. Форма для добавления новой директории
Рис. 16.2. Форма для добавления новой директории
Рис. 16.2. Форма для добавления новой директории
HTML-форма в листинге mkdirform.php, как и в предыдущих Web- приложениях, настроена на повторное использование при редактировании названия и прав доступа к директориям. Для этого, помимо элементов управления, форма содержит два скрытых поля dir и old для текущей директории и старого имени директории.
По умолчанию форма настраивается на добавление новой директории с правами доступа "drwxr-xr-x".
S1S1Кнопка типа submit позволяет отправить данные из HTML-формь] обработчику —файлу mkdir.php, содержимое которого приведено в листинге 16.4.
S1
S1
^Й^Й^Д^^^^'"У^***滑"'* 簪墨旷号袋**:关:,"▲睿^^f"耳!喈;^^Z 气也*可 XY^^^^yi^y ,"",",",,", ^^^^次 VgF :.
:Листинг 16*4. Файл mKdlr.php,:.^Жй:;;—йМ礬■
Подождите...
<?php
// Устанавливаем соединение с FTP-сервером require_once ("config.php");
〃 Получаем значения переменных, переданных // методом POST из HTML-формы mkdirfопп.php
$dir = $_POST[*dir*];
$name = $_POST[*name'];
// Преобразуем права доступа пользователя
// в числовую форму
$user = 0;
if ($_
_POST['ur*]=
Tonf)
$user += 4;
if ($_
_POST[ ^w'] =u
$user +« 2;
if ($_
—POSTJux」^=
'on')
$user += 1;
// Преобразуем права доступа группы
// в числовую форму
$group = 0;
if ($_POST[ *gr* ] в,。~) $group +« 4;
if($_POST[*gw*] == ^n*) $group += 2;
if($_POST[1gx'] в *on*) $group += 1;
// Права доступа no умолчанию для
// остальных пользователей (не входящих в группу)
$other « 0;
if($_POST[1or1] н ronr) $other += 4;
if ($__POST[ *ow* ] = ,on,) $other +e 2;
if ($__POST[ *ox* ] = *on*) $other += 1;
// Проверяем, введено ли имя для директории
if(empty($name)) puterror("Недопустимое имя для директориип);
$ direct = str__replace (1 %, '/', $dir);
// Создаем каталог с именем $паше
if(ftp_mkdir($link, $direct. r7".$name)) {
// Создаем восьмеричную переменную $mode
// с правами доступа к директории
eval (и\$тоде=0$изег$дгоир$о^ег;п);
// Изменяем права доступа для только что
// созданной директории
if(ftp_chmod($linkr $mode, $direct.H/H.$name)) (
// Осуществляем а в томатиче с кий переход
//на страницу администрирования FTP-каталога
echo "<HTMLXHEAD>
<МЕТА HTTP-EQUIV= * Refresh* CONTENT=fO; URL-index.php?dir=$dir * > </HEADX/HTML>°;
} else puterror(пНе удается изменить права доступа к директории1*);
} else puterror (11 Не удается создать директорию4); ?>
Помимо рассмотренной выше текстовой формы для задания прав доступа в операционной системе UNIX права доступа можно задавать восьмеричным числом. При этом праву чтения соответствует цифра 4, праву записи — 2, а праву исполнения ——I. Общие права для групп задаются суммой этих чисел. Например, 6 (4 + 2) обеспечивает возможность чтения и записи, а цифра 7 (4 + 2 + 1) — предоставляет полный доступ к фа!1пу или директории. Для директории восьмеричное число 0755 означает, что владелец директории имеет полный доступ (rwx), а все остальные имеют право читать файлы директории и просматривать ее содержимое (г-х).
( Замечание )
В PHP восьмеричные числа предваряются нулем.
В листинге 16.4 после установления соединения с FTP-сервером анализируются состояния флажков, полученные из HTML-формы nikdirfbrm.php (см. листинг 16.3). Значения прав доступа в восьмеричном формате помещаются в трех временных переменных: $user, $ group н $other: для владельца-пользователя ,группы-владельца и всех остальных пользователей соответственно. Результирующее восьмеричное число формируется в переменной $mode при помощи функции eval {), принимающей единственный текстовый параметр, содержимое которого интерпретируется как РНР-код, Создание директории осуществляется при помощи функции ftp_mkdir (), имеющей следующий синтаксис:
string ftp_mkdir (resource link, string directory)
Функция принимает два параметра, первый из которых —— link —— является дескриптором соединения, возвращаемым функцией ftp_connect()< В качестве второго аргумента directory выступает имя создаваемой директории. В случае успешного создания новой директории функция возвращает имя только что созданной директории, а в случае неудачи — false.
После того как директория была создана при помощи ftp_chmod(), изменяются права доступа к ней. Функция имеет следующий синтаксис:
string ftp^chmod (resource link, int mode, string directory)
В качестве первого аргумента link эта функция, как и большинство других функций для работы с FTP, принимает дескриптор соединения, возвращаемый функцией ftp_connect () * Второй параметр mode принимает восьмеричное число, задающее права доступа к директории, последний параметр directory задает название директории, права доступа к которой изменяют-
ся. Функция возвращает только что установленные права в случае успеха и false в противном случае.
Замечание )
При помощи функции ftp^chmod〔) можно устанавливать права как директорий, так и файлов.
Далее будет рассмотрено изменение имени директории и прав доступа к ней, осуществляемое при помощи управляющей ссылки Исправить. Для исправления директории используется рассмотренная выше HTML-форма mkdicfbrm.php (см. листинг 16.3). Для использования ее при редактировании директории необходимо осуществить ее подготовку, которая проводится в файле chdirfbrm.php (листинг )6.5).
:…:W:."i・f У”",:,学,睪:.・,WA:J:・:,":《,・.:;£,—;,::.^J^f 专♦♦'”:£""..?",・:. """I'.'•;單‘.::"',・ :У;;"2;.:*В?*"・ W"O:''・卒":..乂:〃:,蜜":
;Листинг 16.5. Файл chdirform.php
t^k>^*v^<、議/.,.;;;,工,・;・;;口、«、В.;;,.'i^Vf7m;n;.;'f7?KS ….、:、…;?^i^V.;;r::; :;;・;..;;*;“x..■・..;△«.,:4.;、:鼻.;.‘:?;;.;.;,;扌M.;X?^rW;t;.:、<r.;c、7X・“./.*“;》
<?php
// Извлекаем из строки запроса имя изменяемой директории // и права доступа
$dir = $_GET['dir1];
$патпе 二 substr ($dir, strrpos ($dirf "考”)+ 1);
$dir = substr($dir, 0, strrpos($dir, "*"));
$acc = $_GET[1acc1];
// Осуществляем// Осуществляемразбор прав доступаразбор прав доступапользователяпользователяif(substr($accf 1, else $ur ="";if(substr($accr 2, else $uw ="";if(substr($acc, 3, else $ux м "”;if(substr($accf 1, else $ur ="";if(substr($accr 2, else $uw ="";if(substr($acc, 3, else $ux м "”;1) — *r*) $ur = ncheckedH;1) — *r*) $ur = ncheckedH;1) = W) $uw = "checked";1) == *x*) $ux - "checked";1) = W) $uw = "checked";1) == *x*) $ux - "checked";// Осуществляем разбор прав доступа// Осуществляем разбор прав доступагруппыгруппыif(substr($асс/ 4, else $gr ^ ”";if(substr($accr 5, else $gw = ”";if(substr($acc, 6, else $gx ="'';if(substr($асс/ 4, else $gr ^ ”";if(substr($accr 5, else $gw = ”";if(substr($acc, 6, else $gx ="'';1) = 'rl $gr = r,checked";1) — 'w1) $gw = "checked";1) ~ 'x') $gx ^ "checked'*;1) = 'rl $gr = r,checked";1) — 'w1) $gw = "checked";1) ~ 'x') $gx ^ "checked'*;// Осуществляем разбор прав доступа// Осуществляем разбор прав доступаостальных пользователейостальных пользователейif(substr($асс, 7, 1)if(substr($асс, 7, 1)'г') $ог'г') $ог"checkedп;"checkedп;
// Осуществляем
// Осуществляем
разбор прав доступа
разбор прав доступа
пользователя
пользователя
if(substr($accf 1, else $ur ="";
if(substr($accr 2, else $uw ="";
if(substr($acc, 3, else $ux м "”;
if(substr($accf 1, else $ur ="";
if(substr($accr 2, else $uw ="";
if(substr($acc, 3, else $ux м "”;
1) — *r*) $ur = ncheckedH;
1) — *r*) $ur = ncheckedH;
1) = W) $uw = "checked";
1) == *x*) $ux - "checked";
1) = W) $uw = "checked";
1) == *x*) $ux - "checked";
// Осуществляем разбор прав доступа
// Осуществляем разбор прав доступа
группы
группы
if(substr($асс/ 4, else $gr ^ ”";
if(substr($accr 5, else $gw = ”";
if(substr($acc, 6, else $gx ="'';
if(substr($асс/ 4, else $gr ^ ”";
if(substr($accr 5, else $gw = ”";
if(substr($acc, 6, else $gx ="'';
1) = 'rl $gr = r,checked";
1) — 'w1) $gw = "checked";
1) ~ 'x') $gx ^ "checked'*;
1) = 'rl $gr = r,checked";
1) — 'w1) $gw = "checked";
1) ~ 'x') $gx ^ "checked'*;
// Осуществляем разбор прав доступа
// Осуществляем разбор прав доступа
остальных пользователей
остальных пользователей
if(substr($асс, 7, 1)
if(substr($асс, 7, 1)
'г') $ог
'г') $ог
"checkedп;
"checkedп;
else $or =
if(substr($асс, 8, 1) == *w*) $ow = "ch包eked";
else $ow = ”,
if(substr($accr 9, 1) = *x*) $qx = "checked"; else $ox = ”";
// Если не переданы параметры - настраиваем
// форму на добавление директории
if(!isset($title)) $title = nПереименование директории";
if(!isset($button)) $button = пИсправитьп;
if(1isset($action)) $action = "chdir.php°;
// Включаем HTML-форму
include Hmkdirfom<phpn;
?>
Файл chdirfbrm.php принимает два параметра: имя исправляемой директории $dir и права доступа к ней $асс. Из полного имени директории $dir извлекается имя исправляемой директории, которое помешается во времен- i-гую переменную $name, используемую в качестве value-значение в текстовом поле для названия директории. Права доступа также подвергаются разбору, в результате которого соответствующим переменным присваивается значение "checked”, позволяющее установить флажок в HTML-форме* После этого устанавливаются значения упраапяюших переменных $title, $button и faction для названия HTML-формы, кнопки и имени обработчика формы После этого происходит включение файла mkdirfbrm.php содержащего HTML-форму, обработчиком которой теперь выступает файл chdir.php, содержимое которого представлено в листинге 16.6.
;Листинг 16.6. Файл chdir.php
Подождите ….
<?php
// Устанавливаем соединение с FTP-сервером require__once (config.phpn);
// Получаем значения переменных, переданных
// методом POST из HTML-формы mkdirform.php
$dir = S^POSTl'dir'];
$name = $_POST [Fama'];
Sold ^ $_POST「old'];
// Преобразуем права доступа пользователя
//в числовую форму
$user = 0;
if($_POST['ur1] == Pn'} $user +二 4;
if($_P0ST['uw4 == 'on'} $user += 2;
if($_POST['ux'] == 'on1) $user += 1;
// Преобразуем права доступа для группы
// в числовую форму
$group = 0;
土員$_POST「gr‘】 ^ 'on') $group += 4;
if($_POST['gw'1 == 'on') $group += 2;
if ($_POST[ rgxT] ^ ron4 $group 十=1;
〃 Права доступа по умолчанию для
// остальных пользователей (не входящих в группу)
$other = 0;
if($_POST['or1] == *on') $other += 4;
if($_POST[Tow,] == Pn‘) $other += 2;
if($_POST('oxr]二二'on') $other += 1;
// Проверяем, введено ли имя для директории
if (errpty ($name)) puterror (^Недопустимое имя для директории**);
$direct - str_replace('%'r,/ I $dir);
$dir = str_replace('%', '%25', $dir);
/ / Переименовываем каталог $old
if (f tp_r enaitie ($ link, $direct」/". Sold, $direct「/ "・ $name)}
{
// Создаем в ос ьмеричную переменную $mode
〃 с правами доступа к директории
eval(n\$mode=0$user$group$other;斤);
// Изменяем права доступа для только что
// созданной директории
if (f tp_chmod($link, $mode/ $direct .'•/". $name))
{
// Осуществляем автоматический переход
// на страницу администрирования FTP-каталога
echo "vHTML><HEAD>
<МЕТА HTTP-EQUIV='Refresh1 CONTENT=' 0; URL=index.php?dir^$dir' > </HEAD></H,rML>'^;
} else puterror(яHe удается изменить права доступа к директориип);
} else puterror ("Не удается переименовать директорию^*);
Как видно мз листинга 16.6, код файла chdir.php во многом схож с кодом mkdir.php (см. листинг 16.4). Единственное различие заключается в функции
ftp_rename(), осуществляющей переименование директории. Синтаксис функции представлен ниже:
bool ftp_rename (resource link, string from, string to)
В качестве первого аргумента функция принимает дескриптор соединения с FTP-сервером, возвращаемый функцией ftp_connect (). Через второй аргумент from передается имя переименовываемой директории. В качестве третьего параметра to выступает новое имя директории.
Удаление директории с FTP-сервера осуществляется при помощи скрипта rmdir.php, код которого приведен в листинге 16.7.
^*^^S^^瀚憑懲^^И^^^^帽酢^驚衿*"*济嵋^^й^^^^^^^‘巒/^溝室覺^^^^Ш^^擢炉^^E'^盥¥窮^^蓮:啰当隣驾辱闇^^^~"т ;Листинг 16.7. Файл rmdlr.php
Подождите..*
<?php
// Устанавливаем соединение c FTP-сервером
require_once (r,config >phpM);
// Извлекаем из строки запроса текущую директорию
$dir = $__GET[ Mir1];
$direct = str_replace(1 % , 1/\ $dir);
/ / Для того чтобы удалить директорию, необходимо
// убедиться, что она не содержит файлов. Для этого запрашиваем
// ее содержимое
$list_file = ftp_nlist($link, $direct);
if(count($list_file) > 1) puterror(нНевозможно удалить директорию, так как она содержит файлы11);
// Если даректория пуста - удаляем ее
if (ftp_rmdi工($link, $direct))
{
// Осуществляем автоматический переход.
// на страницу администрировалия FTP-каталога
echo h<HTML><HEAD>
<МЕТА HTTP-EQUIV士'Refresh* CONTENT='0;
URL^index.php?dir^11.substr ($dir/ 0, strrpos ($dir, "%n) ) . ** f> </HEAD>v/HTML>";
} else puterror ("He удается удалить даректорию");
?>
Скрипт в листинге 16.7 принимает единственный параметр dir, содержащий имя удаляемой директории. Удаление директории происходит лишь в том случае, если она пуста. Для того чтобы убедиться в этом, скрипт воз-
вращает содержимое удаляемого каталога при помощи функции ftp_nlist (), которая в отличие от ftp_rawlist () возвращает лишь имена директорий н файлов. Функция ftp_nlist о имеет следующий синтаксис: array ftp__nlist (resource link, string directory)
Функция принимает в качестве первого аргумента link дескриптор соединения с FTP-сервером, возвращаемым функцией ftp_connect(), а в качестве второго —— directory —— имя директории. В случае успеха функция возвращает массив, содержащий имена поддиректорий я файлов, присутствующих в директории directory. Если число членов массива, возвращаемого функцией ftp_niist о, больше единицы, скрипт останавливается с ошибкой. Если директория пустая, то она удаляется при помощи функции ftp rmdir (), имеющей следующий синтаксис: bool ftp_rmdir (resource link, string directory)
Функция удаляет директорию directory и возвращает true в случае успеха и false в противном случае.
Добавление файлов в текущую директорию осуществляется путем загрузки их на FTP-сервер со стороны клиента. Для этого предназначена HTML- форма uploadfbrm.php (листинг 16*8), сходная с формой создания новой директории (рис. 16.3).
3 FH*n»anager ОЛЛ - ftfirmsoft Internet Explorer
Файл Правка йид Набранное сервис Справка
J Назад * . j *田 度•;“/ поиск . Избранное «Медиа 咗,:,丿目
三I J Переход | Ссылки三I J Переход | СсылкиАдрес; I匿 http://bcatTO5Vsertpts/FTPman剪er/uptoadform.phpMiri由
三I J Переход | Ссылки
三I J Переход | Ссылки
FTPmanager раработай и поддерживается IT-студией "SoftTime" ww_与Qfnirne 川
FTPnianager 090FTPnianager 090Аам“нмс1 анизнанн*Веон亨ть=я наQ си&г 也“斗
FTPnianager 090
FTPnianager 090
с 歸軍—МFTPmanag 也
Загрузка файпа в текущую директорию
Файл:Файл:Обзор.*. I
Файл:
Файл:
Прана доступа: F P F P Г Г 卩厂厂I З-груз.ть I
府 Начапо загрузки с узла: ht^://toolhost/府 Начапо загрузки с узла: ht^://toolhost/i 卜 j Местная интрасетьi 卜 j Местная интрасеть旦旦
府 Начапо загрузки с узла: ht^://toolhost/
府 Начапо загрузки с узла: ht^://toolhost/
i 卜 j Местная интрасеть
i 卜 j Местная интрасеть
旦
旦
Рис. 16.3. Форма для загрузки файла на сервер
T^^^gT^^^gHTML-форма содержит практически те же управляющие элементы, что и форма mkdtrfbrni.php, за исключением текстового поля, которое заменено на поле типа file, позволяющее выбрать файл на машине клиента.
T^^^g
T^^^g
;Листинг 16.8. Фэйл uploadforrn>php
(,..>■■'>. Jl}.>^>>.(:;. ;:k ;v\i¥^i.t :;次^;大 盟m:.金,;"6 !*:::上:囂 **:.:扌,簿佻货才点鼠,曲忠碎h
<?php
// Если не переданы параметры - настраиваем
// форму на добавление директории
if(!isset(faction)) $action 之"upload.php”;
if(!isset($button)) $button = "ЗагрузитьM;
if(!isset($title)) $title = "Загрузка файла в текущую даректорию";
// Права доступа по умолчанию для
// пользователя
if < 1 isset ($ur)) $ur = 11 checked1';
if (1 isset ($uw)) §uw = Hchecked'1;
if(lisset($ux)) $ux = ””;
/ / Права доступа по умолчанию для
// группы
iff I isset ($gr)) $gr — ° checked**;
if(Iisset($gw)) $gw = ”";
if {!isset ($gx) } $gx = ”,
// Права доступа по умолчанию для
// остальных пользователей (не входящих в группу)
if(1isset($or)) $or - "checked";
if(!isset($aw)) $ow = ”";
if (! isset ($ox)) $ox = ,T";
// Извлекаем из строки запроса текущую директорию
if(!isset($dir)) $dir = $_GET[,dir,];
?>
<fonn action=<?php echo $ action; ?> method^ost>
<table>,
<?php
if(faction == "upload*phpM)
{
?>
Файл: <input type=file name=name value=1<?php echo $name; ?>*>
<?php
else
?>
Файл: <input type=text name=name value=,<?php echo $name; ?>*> <?php } ?>
Права доступа:
<input type=checkbox name=ur <?php echo
$ur; ?»
<input type^checkbox name=uw <?php echo
$uw; ?»
<input type=checkbox name=ux <?php echo
$ux; ?»
<input type^checkbox narae^gr <?php echo
$gr; ?»
<input type=checkbox nanngw <?php echo
$gw; ?»
<input type«checkbox name^gx <?php echo
$gx; ?»
<input type=checkbox name=or <?php echo
$or; ?»
<input type«checkbox name=ow <?php echo
$ow; ?»
<input type=checkbox name=ox <?php echo
$ox; ?»
<input class®button type«subndt value=<?php echo $button;?»
<input type^hidden name^dir value-<?php echo $dir;?»
<input type=hidden nam«=old value=<?php echo $name;?»
</form>
Как видно из листинга 16.8, HTML-форма создана с учетом повторного использования ее для редактирования. Если в качестве обработчика выступает файл upload.php (листинг 16.9), то в форме используется поле типа file. При любом другом обработчике выводится текстовое поле. В форме по умолчанию предлагается сохранить файл на FTP-сервере с правами доступа "-rw-r—г—”,что аналогично 0644 в цифровом формате*
"Г*城趣现货^三鑒甥挡掷型"作湯'弊茅呦iW繼投8*经魁)里世1樂咬砂:4^^^*翳?/■M/ilf把里聲難■ИИИОИКУ :Листинг 16.9. Файл upload.php
Подождите...
<?php
// Устанавливаем соединение с ЕТР-сервером require_once《"config.php");
// Получаем значения переменных, переданных
// методом POST из HTML-формы uploadform.php
$dir $ POSTfdir'];
$name 匸 $_POST{ Fame1 ];
// Преобразуем права доступа пользователя
// в числовую форму
$user « О;
if($_POST[fur*] == 'on*) $user += 4;
if($_POST[,uwtJ — ^n1) $user += 2;
if($_POST[tux,] h= 'on') $usar += 1;
// Преобразуем права доступа для группы
// в числовую форьлу
$group = 0;
土f ($_POST「gr‘] а У} $group += 4;
if($_POST[,gwl] =,ojV) $group +二 2;
if ($_POST[ *gx* ] == 1 опЧ $ group += 1;
// Права доступа по умолчанию для
// остальных пользователей (не входящих в группу)
$other = 0;
if($_POST[lort] g Pn‘) $other +■ 4;
if($_POST[^ow*] = fon1} $other += 2;
土負$_P。5T['ox'] ~ *onf) $other += 1;
// Проверяем, введено ли имя для файла
if (empty ($name)) puterror (11 Недопустимое имя файла");
$direct = str__replace £1 % , * / * f $dir);
// Начинаем загрузку
Spath = H$direct/°.substr($name, strrpos($name, ”\\“) + 1);
$ret = ftp_nb_put ($link, Spath, $name, FTP_a工NARY);
while ($ret = FTP__MOREDATA)
// Выводим точки, чтобы пользователь
// знал, что процесс идет
echo
// Продолжаем загрузку
$工6七 = ftp_nb_continue($link);
if ($ret != FTP_FINISHED)
echo "<br>Bo время загрузки файла произошла ошибка...п; exit ();
else
{
/ / Создаем восьмеричную переменную 帛mode
// с правами доступа к директории
eval (11 \$mode=0$user$group$other;n);
// Изменяем права доступа для только что
// созданной даректории
if (ftp_chmod($ 1 ink, $mode, $path))
// Осуществляем автоматический переход
// в текущий каталог
echo "<HTMLXHEAD>
<МЕТА HTTP-EQUIV=1Refreshf CONTENT=lO; URL=index.php?dir^$dir1> </HEADX/HTML>n;
} else puterror{^Невозможно установить права доступа к файлу11);
}
?>
В начале скрипта, как и в листинге 16.6, анализируются полученные данные о правах доступа, которые преобразуются в восьмеричный формат. Инициализация загрузки файла на FTP-сервер осуществляется функцией ftp_nbjut (), которая имеет следующий синтаксис: int ftp_nb_put (resource link, string remote_file,
string local___file, int mode T, int startpos])
В качестве первого параметра link функция принимает дескриптор соединения с FTP-сервером. Второй аргумент remote_file определяет имя и путь к файлу на удаленном FTP-сервере, третий параметр local_file определяет путь к локальному файлу. Четвертый параметр mode определяет режим передачи информации: ftp_ascii для текста и ftp_binary для бинарных файлов. Необязательный пятый параметр startpos позволяет задать позицию в байтах, начиная с которой следует загружать файл.
Функция возвращает одну из трех констант:
口 ftp_fa工led — в случае если не удалось передать файл на FTP-сервер;
□ ftp_finished ——в случае если передача файла на сервер успешно завершена;
□ FTE MOREDATA —— данная константа возвращается, если в настоящий момент передача данных продолжается.
Если функция возвращает константу ftp_moredata, то скрипт ожидает завершения загрузки файла в цикле while. Статус процесса контролируется при помощи функции ftp jib__contxnue(), принимающей в качестве eдин^ ственного параметра дескриптор соединения link, который возвращается
функцией ftp_connect(). Функция ftp_nb_continue() возвращает те же самые константы, что и функция ftp_nbMput ().
После завершения процесса загрузки файла на FTP-сервер функция ftp_nb_continue() возвращает константу ftp_finished и скрипт выходит из цикла whig После того как файл успешно загружен на сервер, права доступа к нему изменяются согласно данным, введенным пользователем в HTML-форме uploadform.php (см. листинг 16.8). После чего происходит автоматический переход на главную страницу администрирования.
Для исправления названия файла и прав доступа к нему используется рассмотренная выше HTML-форма uploadfbrni.php. Настройка ее для редактирования файла осуществляется в файле editfilefbrm.php, содержимое которого представлено в листинге 16Л0.
;Листинг 16.10. Файл editfileform.php
<?php
// Извлекаем из строки запроса имя изменяемо го файла
// и права доступа
Sdir = $_GET[^dir1];
$пахпе 二 substr ($dir r str гроз ($dir,"金 ”)+ 1);
$dir = substr($dir, 0, strrpos($dir,叫"));
$acc = $_GET['acc*];
// Осуществляем разбор прав доступа пользователя
if (substr ($accr 1, 1) = 1 г *) $口工=n checked1*;
else $ur ="”;
if(substr($acc, 2, 1) = 'w') $uw = "checked";
else $uw =
if {substr ($acc, 3, 1) = ”')$ux = 11 checked";
else $ux = ”";
// Осуще ст вл нем разбор прав доступа группы
if(svbstr($асс, 4, 1) = 'L】$gr = "checked";
else $gr ”“;
if (substr (§acct 5, 1) = *w*) $gw = "checked11;
else $gw ="”;
if(substr($acc, 6, 1) — 7') $gx = ncheckedM;
else $gx = ”";
// Осуществляем разбор прав доступа остальных пользователей if(substr($асс, 7, 1J — *r*) $or = нchecked";
else $s =
if (substr ($асс, 8, 1)=='『)$ow = nchecked1';
else $ow к
if (substr ($acc, 9, 1) = *x*) $ox = иchecked11; else $ox ="";
〃 Настраиваем форму на исправление названия
〃 файла и прав доступа к нему
if (!isset ($title)) $title = "Перименование файла**;
if(!isset £$button)) $button = "Исправить";
if (! isset ($action)) $action = r,chdir.phpn;
// Включаем HTML-форму include "uploadform.php";
Файл editfilefbrni.php принимает два параметра: dir ——путь к файлу и асе — права доступа к файлу. Оба параметра помешаются в одноименные временные переменные. Из переменной $dir извлекается имя файла, подвергающегося изменению, а из $асс — состояния флажков в HTML-форме. Управляющие переменные $title, $button и $action настраивают HTML-форму, передавая ей название окна, кнопки и обработчик формы соответственно. В качестве последнего выступает рассмотренный ранее файл chdir.php (см. листинг 16.6).
Удаление файла с FTP-сервера осуществляет скрипт, расположенный в файле rmfile.php, содержимое которого приведено в листинге 16.11. Файл принимает единственный параметр dir, который содержит путь к удаляемому файлу.
:『* и^, !•也!纪:,上,”,,*,沙*¥2,若而1廿_"4*簪±・! пуз, ».у w* •»♦«J-UV<* ЧК^^Й^^^^^^К^Й^^1* ^ * ww^toул • J^" VJ V ЦТ* 号"У,; " к •,” у " "Н,発Yk^K^M,":』**.,”,у
;Листинг 16Л1. Файл rmflle.php
式?* 堵 $;;. ・Y.;rJ;、W:* ;*”;?.i*.先.^^:弍**..*.* ♦**•$}/!'£ J”. •••書;* .:.1?*;*:. ;'.;;;..; .z-j.«t*;;*;;;***xt* *. .*.;#*;;»;*;***■*£*"*;***■. laM.»*/*;”/*.*才才;*;'*,;■
Подождите* * <
<?php
// Устанавливаем соединение с FTP-сервером require_once (11 config.php");
// Извлекаем из строки запроса путь к удаляемому файлу
$dir = $_GET[»dirf];
$direct = str_replace(*%*, '/', $dir);
// Удаляем файл
if(ftp_delete($link, $direct))
(
// Осуществлнем автоматический переход
//на страницу а дмини стрирова ния FTP-каталога
echo H<HTMLXHEAD>
СМЕТА HTTP-EQUIV= * Refresh1 CONTENT"'0;
URL=index.php?dir=M. substr ($dir, 0, strrpos ($dir, **%и) ) /м> </HEADX/HTML>H;
} else puterror (nHe удается удалить файл1');
Удаление файла осуществляется при помощи функции ftp__deiete(}, которая принимает в качестве первого аргумента дескриптор соединения с FTP- сервером, а в качестве второго ——путь к удаляемому файлу. В случае успешного удаления файла функция возвращает true, иначе ——false*
Последний сервис, который будет рассмотрен, это загрузка файлов с FTP- сервера на локальную машину пользователя. Для этого файл необходимо загрузить во временную директорию Web-приложения files и уже оттуда отправить его по HTTP-протоколу пользователю. За загрузку файлов с FTP- сервера несет ответственность файл download.php, содержимое которого представлено в листинге 16.12.
Листинг 16.12. Файл download.php
Подождите・..
<?php
// Устанавливаем соединение с FTP-сервером require_once (нconfig ・ php");
/ / Извлекаем из строки запроса загружаемый файл
$dir = $_GET[,dir,);
$direct = str_replace《*学I '/', $dir);
// Загружаем файл в директорию files
$path = "files/".substr($direct, strrpos($direct, l7,4 + 1);
// Инициируем загрузку
$ret = ftp_nb_get($link, $path, $direct, FTP_BINARY);
while ($ret = FTPJ4OREDATA)
{
// Выводим точки, чтобы пользователь
// видел, что процесс идет
echo ”「;
// Продолжаем загрузку
9ret = ftp_nb_continue($link);
// Если происходит ошибка при загрузке файла, // уведомляем об этом пользователя
if ($ret 1= FTP_FINISHED)
{
echo "<br>Bo время загрузки файла произошла ошибка•…”; exit ();
}
else
{
/ / Отправляем посетителю файл
echo H<HTML><HEAD>
<МЕТА HTTP-EQUIV= * Refresh1 CONTENT=,1; URL=$pathf> </HEADX/HTML>**;
}
?>
Файл, путь к которому передается через параметр строки запроса dir, загружается во временную директорию file Web-приложения при помощи функции ftp_nb_get (), которая имеет следующий синтаксис: int ftp_nb_get (resource link, string localfile,
string remote^file, int mode [, int reswnepos])
В качестве первого параметра link функция принимает дескриптор соединения с FTP-сервером. Второй аргумент local^file определяет путь к локальному файлу, третий параметр remote_fiie определяет имя и путь к файлу на удаленном FTP-сервере. Четвертый параметр mode определяет режим передачи информации: ftp_ascii для текста и ftp_binary для бинарных файлов. Необязательный пятый параметр startpos позволяет задать позицию в байтах, начиная с которой следует загружать файл.
Функция возвращает одну из трех констант:
口 FTP_FA工led ——в случае если не удалось передать файл на FTP-сервер;
□ ftp_fin工shed ——в случае если передача файла на сервер успешно завершена;
口 FTPMOREDATA — данная константа возвращается, если в настоя ши й момент передача данных продолжается.
Точно так же, как и в листинге 16.9, контроль за состоянием загрузки файла осуществляется в цикле while при помощи рассмотренной выше функции ftp_nb__continue(). После того как файл загружен при помощи НТТР- заголовка 'Refresh', он отправляется пользователю.
Глава 17
Почтовая служба
Сейчас многие сайты предоставляют посетителям возможность пользоваться электронной почтой через Web-интерфейс. В этой главе будут рассмотрены основные принципы создания такого Web-приложения.
Web-приложение "Понтовая служба" обязательно должно предоставлять пользователям следующие сервисы:
создание учетных записей н подключение к ним;
чтение сообщений электронной почты;
口 отправка сообщений электронной почты;
удаление сообщений электронной почты;
口 переадресация сообщений электронной почты.
Методы отправки сообщения электронной почты были подробно рассмотрены в главе /3 Для чтения сообщений необходимо применять два протокола: POP3 и IMAP.
К Пр-мечанме Ц
Различие между этими протоколами также подробно рассмотрено в главе 13.
Для реализации Web-приложения "Почтовая служба" необходимо использование IMAP-функций из библиотеки расширения IMAP. Эта библиотека содержит большое количество функций. Но основное ее достоинство за* ключается в том, что для реализации эффективного почтового приложения вполне достаточно восьми основных функций, которые мы подробно рассмотрим ниже.
工 Примечш4к!е 】
IMAP-функции, несмотря на свое название, предназначены для работы как с протоколом IMAP, так и с протоколом POP3, что весьма удобно, поскольку архитектура приложения легко расширяется для работы с обоими протоколами. Кроме того, эти же функции применимы и для работы с протоколом NNTP (Networks News Transfer Protocol ——протокол передачи сетевых новостей), и
22 Зэк. 118】
любое почтовое приложение, использующее IMAP-функции, очень легко расширяется для работы с этим протоколом.
Таким образом, в нашем приложении все функции, связанные с чтением сообщений, будут реализовываться на основе протоколов POP3 н IMAP (с использованием 1 MAP-функций), а все функции, связанные с отправкой сообщений, —на основе протокола SMTP (с использованием функции mall).
Рассмотрим более подробно список основных функций, которые должны быть предоставлены пользователю. При первом обращении к системе пользователь должен ввести свои логин и пароль» для хранения которых используется СУБД MySQL После этого он по порядку может совершать следующие действия:
настройку учетной записи;
выбор учетной записи;
просмотр своего почтового ящика;
口 получение почты;
П выход из системы.
Чтобы на этапе просмотра почтового ящика пользователь мог просматривать сообщения, в системе необходимо реализовать следующие функции:
показ заголовков сообщений;
ответ на сообщение;
пересылка сообщения;
удаление сообщения.
Создание базы данных
SQL-запрос на создание базы данных будущего Web-приложения приведен в листинге 17.1. Так как хранение самих сообщений в базе данных не предусматривается, ее структура достаточно проста.
fF; VW ",“y:・ 塗."ff,黑уw,y …у #£•'“
Листинг 17.1. SQL-запрос на создание базы данных mail
CREATE DATABASE mail;
USE mail;
CREATE TABLE users
name CHAR(16) NOT NULL PRIMARY KEY,
pass CHAR(16) NOT NULL,
addr CHAR(100) NOT NULL,
£готп_паше CHAR (100) NOT NULL );
CREATE TABLE account
id_account INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
name CHAR(16) NOT NULL PRIMARY KEY,
remoteuser CHAR(50) NOT NULL,
r^notepass CHAR(50) NOT NULL,
server CHAR (100) NOT NULL, type CHAR ⑷ NOT NULL port INT NOT NULL, );
Как видно нз листинга 17」,база данных mail содержит две таблицы: users и account. Таблица users предназначена для хранения сведений о пользователях системы и имеет следующие поля:
паше — имя пользователя, под которым он работает в системе (логин);
pass — пароль;
口 addr — адрес электронной почты, который будет отображаться в поле From;
口 from_name — имя, которое будет отображаться в отправляемых пользователем сообщениях.
Таблица account предназначена для хранения сведений о пользователях системы и имеет следующие поля: 口 id_account ——первичный ключ;
name —— нмя пользователя, который работает под данной учетной записью;
О remoteuser — имя, под которым пользователь подключается к удаленному серверу;
remotepass — пароль на подключение к удаленному серверу;
server — адрес сервера;
type ——тип протокола, используемого для чтения сообщений с заданного сервера (POP3 или【МАР);
port ——номер порта, к которому будет выполняться подключение в рамках данной учетной записи.
Напоминаем
IMAP-сервер использует для своей работы порт с номером 143, а РОРЗ-сер- вер — порт с номером 110.
Архитектура приложения
В этом Web-приложении за все ответствен один большой скрипт index.php, функционирующий как цикл обработки событий программы (листинг 17.2). То есть независимо от того, какое действие выбирает посетитель, управление передается основному скрипту, но с параметром, по которому можно ндентифицировать это действие. В зависимости от значения этого параметра вызываются различные функции, В нашем случае функции вынесены в отдельный файл ——скрипт fbnc.php.
:Листинг 17.2. Основной файл приложения
<?php
// основной файл приложения **Почтовая службан //***♦***》**************** •女《*«★***★*
// Этап I. Предваритель ная обработка //★*****•+*+* + + *****■********* + + ♦**** + include func. php *);
session_start(); // начало сессии // создаем короткие имена переменных $naine 二 $_POST [ rname,];
$pass 二 $_POST['pass*];
if (isset ($_POST [1 action ЧП faction = $_POST['action *];
else
$action = $_GET['action'];
if (isset($_POST{Account1]))
$account =帛_POST[* account,]; else
$account = $_GET[,account1];
$id_msg = $_GET [ * id msg *);
$to 二 $_FOST「8,];
$cc - $_POST[lccl];
$subject = $_PQST[1 subject1];
$msg = $_POST {1 msg,J;
// идентифицируем пользователей
// здесь следует блок кода,
// ответственный 为己 регистрацию пользователей в системе
II если пользователь нажал кнопку выхода из системы, // производим закрытие сеанса if faction = 'log_out『) {
sessiondestroy(J;
unset (faction);
unset ($_SESSION[• name1 ]);
〃 производим обработку на удаление, сохранение
// и выбор учетных записей путем вызова
// соответствующих функций switch($action)
case ,<lel_account *: / / удаление {
del_account($_SESSION[oauth_user"]); break;
}
case 1 saver: // сохранение (
save_account ($_SESSION [ ^auth^user" ], $_POST); break;
case 1 select__accoumt1: // выбор {
// если выбрана допустимая учетная запись f
// необходимо сохранить ее в нужной
// сеансовой переменной
$_SESSION[Hselect_account°] = $account;
/*
ниже следует блок кода, б котором создаются необходимые кнопки, выводимые на панели инструментов.
К примеру:
// блок создания кнопок
$buttons = array();
^buttons[0] = "Посмотреть ящик**;
$buttons [ 1 ] = "Новое сообщение*1;
^buttons[2] = нСоздать учетную запись н;
//****★★★★***★★★**★******★★******•★**★•*★*** *************
// Этап II. Основной обработчик польэдвательских действий
switch ($action)
{
// Если выбрана опция создания новой учетной записи
// либо учетная запись только что добавлена или удаленаf
// отобразить страницу создания учетных записей
case 'save1:
case *setup_account*:
case *del_account *: {
s how_s etup_a ccotmt($_BESS工ON[1auth_user1]); break;
}・
case 'send_msg *: // послать сообщене
(
if{sendjnsg($to, $cc, $subject『 $msg)) echo "Сообщение отправлено11;
else
echo "Невозможно отправить сообщение"; break;
}
case Fel^jnsg,: // удалить сообщение {
del_msg ($一SESSION「auth^user'],
$_SESS工ON[1select_account *]f $id_msg);
}
case * select^account *: // выбрать учетную запись
case * view_mailbox*: // просмотреть ящик
〃 Если почтовый ящик только что выбран г
I/ нужно отобразить его содержимое
show__list ($_SESSIONГ auth^user*], $_SESSIOK[1select^account *]);
break;
J
case 1show_headers *: // показать заголовки
case ^ide^headers*: // скрнть заголовки
case ^iewjnsg*: // просмотр сообщения
// Если сообщение выбрано из списка или
// оно просматривается и выбрана опция скрытия/показа
// заголовков, то нужно загрузить сообщение
$fullReaders = ($action= * show-headers1);
show__msg ($_SESSION [' auth_user11,
$_SESSION [' select__account1 ], $id_msg, $full_headers);
break;
case * reply-all1: // ответить всем
{
// Установить значение переменной $сс равным строке сс
// текущего сообщения
if(!$imap)
$imap = open_mailbox ($_SESSION J auth__user1 ], $_SESS工ON「select_account']);
if ($ixnap)
{
$header = imap_header ($iinapr $id_msg);
if($header->reply_to_address) $to = $header->reply_to_address;
else
$to = $heade r-> from_addres s;
$cc ^ $header->cc_address;
$subject = TRe: * *$header->subject;
$body = add_quoting(stripslashes(imapjoody($imap, $idjnsg))); imap_close ($ imap);
shcw_new_ms g_f о rm ($_SESSION [ 'auth^user1 ], $to, $cc, $subject, $body);
break;
case 'reply1: // ответить
{
// Установить значение переменной $to равным полю replу-to
// или полю from текущего сообщения
if (!$imap)
$imap = openjnailbox (J?_SESSION [1 auth^user1 ],
$_SESS工ON[^select^account']};
if($imap)
^header = imap__header ($intap, $id_msg);
if($header->reply_to_address)
$£◎ = $header->reply_to address;
else
$to == $header->f rcaraddress;
$ subject 二 *Re: *. $header*->sub ject;
$body = add_quoting (stripslashes (imapqcdy ($imap, S id msg})); imap_close ($imapj;
show_neirfinsg_f orm ($_SESSION ('auth^user1 ] # $to, $cc, Ssubject, $body};
}
break;
case fforwardT: // переадресация
<
// Установить значение переменной $body равным телу
// текущего сообщения, взятому в кавычки
if《!$imap)
$imap = open_mailbox($__SESSION[*auth_user,1, $_SESSTON(*select^account4);
if($imap)
{
^header = imap_header($imap, $id_msg);
$body = add_quoting (stripslashes (imapjoody ($ intap, $id_rnsg)));
$ subject = "Fwci: 1 »$ header *> subject;
imap_close (Simap);
show_new__msg_form ($_SESSION [ ‘3uth_user,], $to, $cc, $subject, §body};
}
break;
case 'new-messagef: // новое сообщение
{
show_new_msg_fo3nn ($_SESSION [' auth_user1 ], $to, $cc, $subject, $body);
break;
}
?>
На этом разработку главного сценария проекта можно считать законченной, теперь подробнее рассмотрим основные моменты этого сценария.
Создание учетных записей
При первом обращении посетителя к Web- при ложе н и ю необходимо настроить учетные записи электронной почты. После того как пользователь нажмет кнопку Создать учетную запись, переменная faction получает значе^ нне setup^account (ЛИСТИНГ 17.3).
Листинг 17.3. Настройка учетных записей
case 1 save1:
case ' setup_account.':
case 'del_account':
s h оw_s et up_ account($_SESS工ON[,占uth_user,]); break;
}
Это Приводит К вызову функции show_setup_account, которая выводит ©орму заполнения данных об учетной записи. Форма может выглядеть, к примеру, как показано на рис. 17Л.
Указанную функцию можно использовать двояко. С одной стороны, она предназначена только для вывода формы для заполнения. Но с другой стороны, пользователю ведь может понадобиться изменить данные своей учетной записи, и поэтому имеет смысл предусмотреть вызов этой функции с параметрами, значениями которых будут данные об учетной записи пользователя (листинг 17.4).
次e*名二'**W** *AW :*,:.*"1奖岁"屮*?m安W ' y W U …*,…:.,.….L■JJ;**1 看上^雪露."T? •■"■■" "V' "!
Листинг 174 Функция show—sretup_account
function show_setup__account ($auth_user)
(
show_account__form ($auth__user);
$list = get_account($auth_user); // выводим $account = sizeof($list);
// Отображение всех учетных записей foreach($list as $key => Saccount) {
s how_accoun t _f о па ($auth_user,
$account「id_account*], $accoxint [ ■ serverf ] #
$account[1remoteuser1], ^account[fremotepass'], ^account[1type,],
$account[*port1 ]); }
To есть в том случае, если функция вывода формы show_account_form вызывается без параметров, выводится просто пустая форма, как на рис. 17.L
Если функция вывода формы вызывается с параметрами, то все поля формы уже будут заполненными. Функция get__account предназначена для извлечения информации об уже существующих учетных записях из базы данных (листинг 17.5)*
;Листинг 17.5. Функция get_account для извлечения информации
:об учетной записи
SW* ♦ Г“ • • G^V^T^H^A^T^ff» ■ > ■「『*” К"Л * Г» * * . ?т^^Л>«| :力^ 耳域 “* M?V*v*v** “ й«-Aft# f * a t flick |^?|?4 v- «чСТ1?■ .%:'“*■;. * .1,*;;:*<♦>*“* Л#4*^?3 иЧ rtrfl^trt*?^ ■ ■ ■ it^V^4 ,
function get_account($auth_user)
{
$ query = H SELECT * FROM account WHERE name = 1 $auth_user111;
$result = mysql_query($query);
if($result)
$var = mysql_fetch_array ($result, MYSQL_NUM);
return list($var);
} else return false;
Функция get__account соединяется с базой данных, извлекает учетные записи и возвращает их в виде массива.
После того как пользователь заполнил форму учетной записи, он должен нажать кнопку Сохранить данные об учетной записи, что приведет к собы* тию save и выполнению следующего блока кода:
case 'save:
t
save^account ($_SESSION [ rrauth_user »], $_POST); break;
Функция save_account сохраняет данные об учетной записи (листинг 17.6).
;Листинг 17.6. Функция сохранения данных об учетной записи
t^t«.X?^W^m№W№№^..**<fiiTniTlAlVwi"?i№ri*?№l.;豆落*^®;』'寫V7.紀 i'« « firt^^ivSil J\f»t* * «Л Л <» * r«. Л.VV*» Л1?. ii^ Л .Vi?3tS#iiSl№ ;
function save_account($auth_user, $var)
{
$query = "INSERT INTO account VALUES ('$auth_user', f$var[server]1f $var[port], •$var[type]1, f$var[remoteuser]'t
*$var[remotepass]1, NULL)”;
В этой функции можно также предусмотреть возможность изменения данных уже существующей учетной записи, для чего нужно всего лишь заменить оператор insert в вышеприведенном запросе оператором update. К примеру:
if ($var [нaccountrt] >0)
$ query = "update into account values (…) else
$ query = 11 insert into account values (...)
В том случае, если пользователь нажал кнопку Удаление учетной записи, происходит событие dei_acconnt и выполняется следующий участок кода: case 'del_account1: // удаление {
del_account ($_SESSION [ ^aut^user**]); break;
}
Удаление учетных записей происходит с помощью функции dei_account, код которой приведен в листинге 17.7.
哨М"&"*]窮叱*当”“-”""式‘*"‘啸也y"*"*1'浦支r” 部2城蚊学^S^**"HYXWitL" 3vH,咗'XF* 2境'旨,澄”工空力装**守赞*m!1^*:0喪号外妒**'**#^/*^辞 ;Листинг 17.7. Функция удаления учетных записейi
function del^account($auth_userf $id_account) {
$ query = '* DELETE FROM account WHERE id_account= * $id__account1 and name = 1 $auth__user * *';
$ re suit = mys ql^que ry ($ query);
return ^result;
Просмотр почтового ящика и чтение сообщений
После того как учетные записи настроены, можно приступать к чтению сообщений, предварительно подключившись к учетным записям почтового сервера.
Возможность просмотра содержимого почтового ящика реализована в функ- инн show_iist1 отображающей список сообщений, находящихся в почтовом яшике пользователя (листинг 17.8),
:Листинг 17.8. Функция show__list просмотра почтового ящика
function show_list($auth_user, $id_account)
if(!$id_account)
Echo пПочтовый ящик не выбран"; } else {
$imap = open^nailbox ($auth_userf $id_account);
$headers = imap^Jieaders ($hnap);
Smsg = sizeof(Sheaders);
for($i = 0; $i<$msg; $i++)
// вывод HTML-таблицы под сообщения
Как видно из листинга 17.8. почтовый ящик пользователя открывается с помошью функции open^mailbox, а заголовки сообщений читает IMAP- функция imap headers. Код функции open_mailbox о приведен в листинге 179
:Листинг 17.9. Открытие почтового ящика
♦ OleVe VW;:;《;S<^i^fi^,-, ;:V".*■ .*%**; ♦、.*. ■-♦3・~а*Г;'¥ТС\.;%1'『;*'.*.;;♦.「* *X';f ;t * i *1',1*・*一*;・ t*'iF^a1a%~B ■*■*■■■■>«■>■■■'■ ***V«<^b* ■-■"■"■Va ■> t til t p fe^i«^i-i'1l-l'i v *
function open_ma i Ibox ($ aut h_us e r, $ account id) {
// Выбираем почтовый ящик, если он единственный
if (number_of_accounts ($auth_ iiser) =1)
$ account = get_account_nanie ($auth_user);
$_SESSION['selectaccount1] = $account[0];
$id_account = $account[0j;
// Подключаема只 к POP3 или IMAP-серверу
$var ^ get_account ($auth_user, $accountid); if(!sizeof($var)} return 0;
$wailbox = *{* * ^settings[server];
if ($var [type] =' POP3')
$mailbox <= */рорЗ,;
$rtiailbox .= *:1 . $settings [port].1}INBOX';
@ $imap = imap__open ($maiIbox, $var [ • remoteuser1 ],
$var[1remotepass*]); return $imap;
Функция number_of_accounts О возвращает количество учетных записей, которые зарегистрировал пользователь. Код этой функции приведен в листинге 17.10.
function number_of_ассounts ($auth_user)
{
$ query = 11 SELECT COUNT (*) FROM account WHERE name = 1 $auth_user • H;
$result = mysql_query{$query);
if($result)
return mysql_result ($result# 0);
}
return 0;
Функция get_account_name выводит имена учетных записей, чтобы пользователь мог выбрать нужную. Эта функция подобна ранее рассмотренной ФУНКЦИИ get_account () *
Выбранная пользователем учетная запись является текущей и сохраняется в сеансовой переменной $_session [1 select__account1 ]»
В листингах 17.8 и 17.9 мы впервые столкнулись с IMAP-функциями, которые подробно рассмотрены ниже.
Открытие почтового ящика происходит с помощью функции imap_Qpen(), имеющей следующий синтаксис:
int hnap_open (string mailbox, string username, string password, 【,int options])
Рассмотрим параметры этой функции:
□ mailbox — строка, содержащая имя сервера н имя почтового ящика. Указанная строка имеет следующий обязательный формат:
(хост/протокол:порт}ящик
Номер порта и название протокола являются необязательными параметрами. В том случае, если они не указаны, по умолчанию подразумевается, что имеется в виду протокол 1МАР с соответствую щи м ему номером порта. К примеру, в случае протокола [МАР параметр mailbox будет иметь следующий вид:
{serveгname:143}INBOX
А для протокола POP3:
{s е rve rname/рорЗ:110}INBOX
口 username —— имя пользователя, которое зарегистрировано в текущей учетной записи;
口 password — пароль для доступа к учетной записи.
После того как работа с IMAP закончена, IMAP-соединение следует закрыть при помощи функции lmap_close(). В качестве единственного параметра функции выступает дескриптор соединения. В листинге 17.8 закрытия соединения не происходит, поскольку дескриптор соединения передается в функцию show_list о, В которой С ПОМОЩЬЮ функции imap__headers () извлекаются заголовки почтовых сообщений.
Функция imap_headers()предназначена для вывода заголовков всех почто* вых сообщений, присутствующих в выбранном почтовом ящике. Заголовки возвращаются в виде массива, в котором каждая строка соответствует отдельному сообщению. В качестве единственного параметра эта функция принимает дескриптор IMАР^соединения: imap_headers ($imap)
Для получения более полной информации в заголовках предназначена функция imap_header(), принимающая в качестве параметра дескриптор IMAP-соединения и идентификатор сообщения (帛:Ld_msg).
После того как получены заголовки сообщений, необходимо реализовать метод, при помощи которого можно прочитать каждое конкретное сообщение. Это осуществляет функция show_msg, ее код приведен в листинге 17.11.
+Я中"审"吋5*1守W 吃*:’“賞甥*畢Уj^M?'■魅и" «空r,尊时 *^對嚳*尸壊""*■,11屮 *寸" У 北t幫盟 卄勿 州必 繩时鸵M*WA賢必 婴■出睜V陶,臂*耀城耀储ь騎叫蹴<"
;Листинг 17.11. Чтение выбранного сообщения
function showjnsg($auth_user, $id_account, $id_m£g, $full_headers)
{
$msg = array ();
// открываем почтовый ящик
$imap = open^nailbox ($auth_userf $id_accotint);
// получаем все заголовки сообщения, по обращению
// к которым ниже будет формироваться сообщение
^header = imap_header ($iniap, $idjnsg);
// получаем тело сообщения
$msg「body' ] = imap_body ($ imap, $id_msg); if(!$msg['body1]) $msg['bodyr] = н[Отсутствует тело сообщения]и;
/ / получаем тему
$msg['subjес± 1] 二 $header->subj ect;
// получаем поле from
$msg [' from_address,) - $header^>f romacidress;
// получаем поле to
$msg[『to_address*] - $header->toaddress; // получаем поле cc
$msg [' cc__address 丨]二 $header-*>ccaddress;
// получаем дату приема сообщения
$msg[*date']二 $header->date;
// закрываем IMAP-соединение
iinap^close ($iinap);
return $rnsg;
Просмотрев сообщение, пользователь может его удалить. В этом случае инициируется событие del__msg: case 'del_msg1: // удалить сообщение {
deljnsg($_SESSION[fauth^user*lг
$_SESSION[* select account1], $idjnsg); }
Код функции del_msg(), удаляющей сообщение, рассмотрен в листинге 17.12.
г 17,12. Удаление сообщений
..«..;i・, ♦.♦・■♦.•;♦”♦.;;>♦♦♦♦"♦♦♦. ♦・、《・"《б\;....:.,・♦♦】;.,..《^т?..,/;Л^К^???・.;.;;..『:•..・;♦.♦、.♦;♦♦....•♦♦.,京 j;・3ii^f《》7・.;.;・edi..
function del_nisg($auth_user, $id_account# $id_msg)
$imap = open_mailbox($auth_user/ $id_account);
if ($imap)
{
imap_delete ($imap, $message_id);
imap_expunge ($imap);
imap^closE ($imap);
return true;
}
else return false;
Для удаления сообщений используются функции imap_delete и imap_expunge. Их отличие друг от друга состоит в том, что первая функция только помечает сообщения как удаленные, а вторая —— действительно удаляет их. Общим параметром у каждой функции является дескриптор IMAP- соединения. Функция imap__delete имеет в качестве второго параметра идентификатор сообщения.
Отправка сообщений
Отправка сообщений реализуется с помощью функции send msg(), которую в данной главе мы рассматривать не будем в силу того, что вопросам отправки сообщений целиком посвящена глава /3.
Глава 18Глава 18回
Глава 18
Глава 18
Система рассылки писем
Система рассылки писем является одной из старейших служб Интернета. Она появилась задолго до Web и являлась предвестником группы новостей UseNet. Сейчас такой способ распространения информации устарел, так как лишен интерактивности, предоставляемой такими Web-приложениями, как гостевая книга, форум и т. п. Тем не менее в качестве дополнительного сервиса рассылка до сих пор актуальна. В первую очередь рассылки применяются для распространения прайс-листов и новостей компаний.
В данной главе будет рассмотрена система рассылки, позволяющая публиковать новости как на сайте, так и распространять их в виде писем подписчикам. Посетители ресурса с использованием данного Web-приложения смогут подписываться на рассылку самостоятельно. Если рассылка является не публичной, а внутренней, то с помощью страницы администрирования можно будет формировать и редактировать список подписчиков.
Отправка почтового сообщения происходит один раз, при добавлении новостной позиции в системе администрирования. Подписчики получают почтовое сообщение, а текст самого сообщения публикуется на сайте в виде новости.
Разработка системы администрирования
Для данного Web-приложения потребуются две таблицы news, для хранения отправляемого подписчикам текста (требуется для дальнейшей публикации на сайте) и users, для хранения информации о подписчиках.
Таблица news состоит из 8 следующих полей:
id_news ——первичный ключ таблицы, снабженный атрибутом auto_ increment;
口 паше — заголовок новостей;
body ——текстовое содержимое новостей;
679
putdate — время отправки рассылки;
uri — URL по теме новости (необязательное поле);
uri_text — текст ссылки для URL (необязательное поле);
口 urljpict — путь к файлу или изображению, прикрепляемому к файлу (необязательное поле);
口 hide — текстовое поле типа enum5 принимающее два значения: 'hide1, если новость недоступна с сайта, и show1 — если новость доступна.
1&L Создание таблиць! newa1&L Создание таблиць! newaSQL-оператор create, позволяющий создать данную таблицу, приведен в листинге 18.1.
1&L Создание таблиць! newa
1&L Создание таблиць! newa
弋•iyA^v^^^fj^^^g* F 号*.fj^iy~if 电 1йУ**“ ^W^t'itjbWMffl1 士
CREATE TABLE news (
id_news INT(11} NOT NULL AUTO_INCREMENT, name TINYTEXT NOT NULL, body TINYTEXT NOT NULL,
putdate DATETIME NOT NULL DEFAULT 『0000-00-00 00:00:00', url TINYTEXT NOT NULL,
url_text TINYTEXT NOT NULL, urljpict TINYTEXT NOT NULL, hide ENUM「shew'J hide') NOT NULL DEFAULT 'show' PRIMARY KEY (id_news)
)TYPE=MyISAM;
Таблица подписчиков users состоит из пяти полей:
О id_user —— первичный ключ таблицы, снабженный атрибутом auto_increment;
name — ИМЯ ПОДПИСЧИКЭ;
口 organization —— организация, в которой работает или которую представляет подписчик (необязательное поле);
口 email — e-mail подписчика, на который он будет получать письма рассылки;
口 status — статус подписки, текстовое поле типа епшп, принимающее два значения: active1 и 'inactive', если подписка активирована и дезактивирована соответственно.
Если поле status принимает значение 'inactive', подписчик не получает писем, для акггивации подписки данное поле должно получить значение 'active:
SQL-оператор create, позволяющий создать таблицу users, приведен в листинге 18.2.
Листинг 182 Создание таблицы usersЛистинг 182 Создание таблицы users
Листинг 182 Создание таблицы users
Листинг 182 Создание таблицы users
CREATE TABLE users (
id_user INT (11) NOT NULL AUTO_INCREMENT,
name TINYTEXT NOT NULL,
organization TINYTEXT NOT NULL,
email TINYTEXT NOT NULL,
status ENUM(1 inactive1,* active1) NOT NULL DEFAULT inactive1, PRIMARY KEY (id^user)
)TYPE=MyISAM;
Страница администрирования, расположенная в отдельной директории admin, состоит из двух частей: системы рассылки и системы управления подписчиками. Система рассылки и публикации новостей, состоит из 8 файлов:
口 addnewsform.php — HTML-форма для формирования тела письма и новостной позиции;
口 add news, php — обработчик формы addncwsfbrm.plip, отправляющий письма подписчикам н помещающий отправленный текст в базу данных для отображения в блоке новостей на сайте;
口 delnews.php — скрипт удаления новостной позиции из базы данных;
editnewsform.php —— HTML-форма для редактирования уже существующей новостной позиции;
editnews.php — обработчик формы editnewsfbrm.php;
hide.php —— скрипт, несущий ответственность за сокрытие новостной позиции;
口 index.php — главная страница системы администрирования;
show.php — скрипт, несущий ответственность за отображение новостной позиции на сайте.
Для установки соединения с базой данных» как и в предыдущих Web- приложениях» будет использоваться файл config.php (см. листинг 7.5), расположенный одним уровнем выше директории с системой администрирования. Кроме представленных в листинге 7.5 переменных в config.php необходимо поместить две дополнительные директивы: $pnumbernews ——количество новостей, выводимых на одной странице, и $aii_nmnber_news —— количество новостей на странице администрирования и в группе Все новости.
( Замечание )
Далее в листингах для удобства восприятия будет опущен несущественный HTML-код, связанный, в первую очередь, со стилевым оформлением Web- приложения. Полная версия данного Web-приложения находится на прилагаемом к книге компакт-диске, а также доступна по адресу http://www.softtime.ru/ info/downloads.php.
Файл index.php несет ответственность за формирование главной страницы системы администрирования, содержимое этого файла представлено в листинге 183
?м<у ■犒* ечглги.уг^—"'* *" %”工^jfe^££M" 鹭安书"2*£";£r,:^№^^?^*^^*^1^^4^' "'*•""'' М' w七%,吊* a ty出и,'''&¥ 牡? 2*,w&= w■" • * •••«•**• "、*цу !?,мм^^^■
Листинг 18.3. Файл index.php
<?php
// Проверяем переменную $start, равную порядковому номеру первой // новости на странице
if(empty($_GET[1 start1])) $start = 0;
else $start = $_GET[1 start1];
// Устанавливаем соединение с базой данных
require_once (".. /config. php'1);
// Воспроизводим новости таким образом, как они выглядят на
$query$query$new$new// главной странице, но отображаем также невидимые новости
$query
$query
$new
$new
="SELECT * FROM news
ORDER BY putdate DESC
LIMIT $start, $ a1l_number_news n; mysql__query ($query);
if ($new)
// Выводим ссылки управления новостями, добавление, удаление
// и редактирование
echo ”<а href=addnewsform,php?start=$start^o6aBHTb новость</а><Ьг>н; echo "<а href=nsers.рЬр>Подписчики</а>";
?>
<table>
<tr>
<Ъб>Дата</td>
<td>HoBocTb</td>
<td>M36p-e</td>
<td co1зрап=3>Действия</td>
</tr>
<?php
while ($news = mysql_fetch_array ($new)) {
// Если новость отмечена как невидимая (hide=rhide*), выводим
// ссылку 11 отобразить", если как видимая (hide=, show*) + "скрыть11
if ($news [ ^ide1 ] = ,show,)
{
$showhide = "va href-hide.php?id_口ews="・ $news[1id_news')*"& start=$start>CKpbiTb</a>,1;
}
else
<
$showhide = "<a href=show.php?id_news=r,.$newg「id_news」•”& start=$start>0ro6pa3MTb</a>ri;
}
// Проверяем наличие изображения
if ($news[,url_pict,] !=八 && $news['url_pict1 ] !==」}
$url_j^ict=M<bxa href=.. /".$news furl^pict* ] . °>есть</ах/Ь>н;
else $иг1_р1с^"нет";
if (($news LurJ. r ]!='-') and ($news [1 url1 ] 1 =11 M
$news_url = и<Ьг><Ь>Ссылка:</b> <a h匸邑f='".$news(*url'].”‘>”
•$news[1url_text']."v/a>”;
else $news_url = *•'*; // Выводам новость echo T,<tr>
<td>"・ $news[*putdate'J.”</td>
<td><a href=editnews form. php?id__news^'*, $news [ * id_new§' ]."& start^$start>H.$news['name1]<"</a>
<br>l\nl2br($newsf ^ody1]) * и "• $news__url. " </td>
<td>**. $url_pict ・"</td>
<td>”.$ showhideJ </td>
<td><a hre f=delnews.php? start~$ sta rt &
id_n0ws=".$news「id_newsf]* н>Удалить</а></Ёс1>
<td><a href=editnewsfomi.php?start=$start&
id_new3="・ $news [f id_news1 ] . |Г>Исправить</а></td> </tr>H;
} echo *,</taЫe>,,;
else puterror ("Ошибка при обращении к блоку новостей1*);
// Выясняем общее количество новостей в базе данныхг для того чтобы // правильно отображать ссылки на последующие и предыдущие новости. $tot = mysql__query(нSELECT count(*) FROM news");
if ($tot) $total = mysql_result £$tot,0);
else puterror(н0шибка при обращении к блоку новостей");
II Выводам ссыпки на "Следующие" и "Предащущие^ новости
// Если $start больше нуля, значит, у нас есть предыдущие новости echo ”<р>”;
if($start > 0) echo "<а href=1index.php?start="*
($start - $all_nuinber_news) »и 1 >
Предыдущие <b>$all_number_news</b> новостей</а>м;
// Если $start+$pnumber все же меньше общего количества новостей,
// значит, у нас есть следующие новости
if($start + $all_nwnberenews < $total)
echo "<a href=* index.phpTstart^11» ($start + $all_number_news). ”,〉Следующие <b>$all_nuniber_news</b> новостей</а>м;
?>
Внешний вид главной страницы администрирования системы почтовой рассылки представлен на рис, 18.1.
Результатом работы скрипта из листинга 18.3 является таблица, каждая строка которой соответствует отдельному новостному блоку. Помимо даты добавления новостной позиции, текста самой новости и ссылки на изображение (файл) каждому новостному блоку соответствуют три управляющие ссылки: Скрыть/Отобразить, Удалить и Исправить. Новостные блоки выводятся постранично, количество новостных блоков на странице определяется значением переменной $ а 1 l_numbe r_ne ws из конфигурационного файла config.php. Поэтому SQL-запрос выбирает $aii_number_news новостей из таблицы news, начиная с позиции $start. Переменная $start при первом обращении равна 0, а при обращении к следующим новостным позициям увеличивается на величину $aii_number_new3.
Для добавления новостного блока необходимо перейти по ссылке Добавить новость, которая приводит к HTML-форме addnewsfbrm.php, содержимое которой представлено в листинге 18.4.
( Замечание )
Размещение новостного блока приводит к автоматической рассылке содержимого новости по активированным почтовым адресам в таблице users.
Рис. 18.1. Главная страница администрирования системы рассылки
;Листинг 18.4. Файл addnewsform.php
<?php
if (! isset ($button)) $ but ton = ••Добавить '*;
if(!isset($action)) $action = "addnews.php";
if(!isset($showhide)) $showhide = "checked";
?>
<a href=Hindex.рЬрм>Вернуться в администрирование новостей</а>
<form enctype=,multipart/form-data, action«<?php echo $action; ?> method-post>
Название <input type^text name=name value=*<?php echo $name; ?> * ><br>
Содержание <textarea name^body rows=10 cols=60>
<?php echo $body; ?></texta rea><br>
Ссыпка <input type=text name^url value=1<?php echo $url; ?>T ><br>
Текст для ссылки <input type=text name=vrl_text
value=*<?php echo $url_text; ?>f><br>
Вложение <input type^file name=putfile value=*<?= Sputfile; ?> * ><br>
Отображать <input type=checkbox name=hide <?php echo $showhide; ?»<br>
<input type=sulxnit value=<?php echo $button; ?»
<input type^hidden name=id_news value=<?php echo $_GET [ * id_news 1 ]; ?»
<input type-hidden narne^start value=<?php echo $_GET[* start1]; ?»
<input type=hidden name^status value=<?php echo $_GET[•status1]; ?» </table>
</form>
Форма, представленная в листинге 18Д, содержит следующие поля:
трн текстовых поля: name, uri и uri_text для названия новостного блока, ссылки и текста ссылки соответственно;
одну текстовую область body для текста новости;
поле типа file для вложения в письмо, отсылаемое подписчикам;
флаг hide для задания статуса отображения новостного блока на сайте;
кнопку для отправки данных формы обработчику addnewiphp, содержимое которого представлено в листинге 18.5.
Внешний вид формы для добавления новостной позиции представлен на рис. 18,2.
吗 iH^MiffiW舒置叫" " " :先;嚳"'解''+**"*"*曾 ***"賦* 彗他• II ””%ijy^yymf|Y
i Листинг 18.5. Файл addnews.php
<?php
// Устанавливаем соединение с базой данных include "../config.php";
// Проверим - достаточно ли информации для занесения в базу данных
if (empty ($_POST [Fame1))) links (м Отсутствует заголовок11);
if(empty($_POST[*body*])) links(иСодержание не введенон);
if (empty ($_POST[ furl_text • ]) && !empty ($_POST [ • url •]))
(---
$_POST[,url_text,] = $_POST[,url,];
}
// Определяем, скрыта новость или нет
if($_POST[1hide1] == "on") $showhide = "show";
else $showhide = "hide";
// Добавляем протокол в url, если посетитель забыл это сделать сам S—POSTJurU] = strtr($_POST(,url,Jr "HTTP", "http");
if (!empty($_FOST[,url,]))
{
if (strtolower((substr($_POST[^rl1], 0, 7))*="http://M) && (strtolower (substr ($__POST[ ^rl* ], 0, 7)) !="ftp: 〃”))
$url-Mhttp :〃"・ $_POST [^rl*];
}
// Заменяем одинарные кавычки обратными, чтобы избежать конфликта
〃 при добавлении информации в таблицу
$_POST[*name1) = str_replace(”'", ”'", $_POST[1name']);
$_POST[,body,] = str_replace(,,,,rf ”'", $__POST [1 bodyf ]);
$picture н "”;
// Если поле выбора вложения не пустое - закачиваем его на сервер if (Seirpty ($_FILES[1 put file* ] [1 tmp_name1 ]) &&
$_FILES[1putfile1][1name1] !=”-")
{
// Закачиваем файл
$path=Hfiles/M.$_FILES(•putfile1][^ame*];
if (copy ($_FILES[ *putfileMt1 tmp_name•],". ・/"・$path)) $picture = $path;
}
// Формируем и выполняем SQL-запрос на добавление новости
$query = "INSERT INTO news VALUES (0, M\$_POST[tnamet] .,,\ l,\$_POST[,body,]<,°/ now()/
i・$_POSTfurl1] .,w, 一『",$_POST [* url^text' ] JI ^picture I
• $showhide1)n;
if <mysql_query($query)} {
// Отправляем почтовое сообщение подписчику
$query = "SELECT * FROM users
WHERE status = * active *M;
$usr = mysql_query ($query);
if (! $usr) links (rtОшибка при обращении к базе подписчиков**);
if (myзql_num_rows ($usr) >0) (
$thm ^ "Новости компании SoftTimerl;
$msg = "<b>''・ $_POST【『name' 1. H</bxbr>H . $_POST [* body' 1;
// Изменяем кодировку
Sthm = convert_cyr_string (stripslashes ($thm) # 'w‘, 'k‘); $msg = nl2br (convert_cyr_string (stripslashes ($msg) f ' w1, 1 k*)); if (empty ($picture)) {
while ($users = mysql_fetch__array ($usr)) mail ($users [1 email1 ], $thm, $msg);
else <
while($users ^ mysql_fetch_array($usr)) sendjnail ($users [1 email1 ], $tbmf $msg, 11.. / M* Spicture);
} } // Если запрос выполнен удачно, осуществляем автоматический переход // на главную страницу адаинистрирования echo "<HTML><HEAD>
<МЕТА HTTP-EQUIV=4 Refresh1 CONTENT='0;
URL=index.php?start=M.$_PO5T['start*].n,>
</HEAD>";
} else links ("Ошибка при добавлении новостной позиции11);
// Вспомогательная функция для вывода ссылок возврата function links($msg) {
echo м<р>" . $msg. ”</p>";
echo "<p><a href=# onClick=,history.back()f> Вернуться к правке </a></p>n;
exit();
}
// Функция для отправки почтового сообщения с вложением function send^nail{$to^ $thm^ $html, $path)
$fp = fopen($pathr ,rr");
if (!$fp) {
print ° Файл $path не может быть прочитан'1; exit();
}
$file = fread($fp, filesize($path));
fclose($fp);
$path = sx^>str ($pathf 12};.
$ boundary = ”一° .md5 (uniqid (time ())); // генерируем разделитель
$headers .= MMIME-Version.: 1.0\nM;
$headers ,="Content-Type: multipart /mixed; boundary=\ n $boundarу\11 \n**;
Smultipart .= M—$boundary\nH;
$kod ^ ”oi8-r'; // или $kod ^ 1windows-12511;
$multipart .- HContent-Type: text/html; charset=$kod\n*f;
$multipart .= "Content-Transfer-Encoding: Quot-Printed\n\nrf;
^multipart .= n$html\n\nn;
$message_part = ”";
$message_part .= 11 Content-Type: application/octet-streamn;
$message_part .= "; file_name = \"$path\M\nn;
$message_part ・=11 Content-Transfer-Encoding: base64\n";
$message_part 宀 "Content-Disposition: attachment; filename = WpathJWW;
$message_part .= chunk__split(base64_encode($file)).n\n";
$multipart 亠 ”一$boundary\nM.$message_part.я—$boundary—\n";
if(Imail($to, $tbm, $multipart, ^headers)) {
echo "К сожалению, письмо не отправлено1'; exit ();
После проверки обязательных поле访 и загрузки изображения (файла) на сервер осуществляется формирование SQL-запроса на добавление новостной позиции в таблицу news. Если SQL-запрос выполнен успешно, происходит выборка записей из таблицы users, чье поле status принимает значение Active. Таким образом формируется список электронных адресов, для которых подписка активирована*
Если изображение (файл) не было добавлено в форме добавления новостной позиции, отправка сообщений происходит в цикле while при помощи функции mail(), синтаксис которой подробно описан в главе 13. Если же к письму необходимо ггрикрепить файл, используется функция send_mail () расположенная в конце файла addnews.php (см. листинг 18.5)* Функция принимает четыре параметра:
$to ——адрес, на который отправляется e-mail;
$thm — тема почтового сообщения;
$htn\l ——текст почтового сообщения в формате HTML;
$path — путь к файлу, прикрепляемому к почтовому сообщению.
Отправка почтового сообщения происходит один раз, прн добавлении новостной позиции в системе адкшнистрирования. Подписчики получают почтовое сообщение, а текст самого сообщения публикуется на сайте в виде новости.
Новостные позиции, оставшиеся в таблице news, можно отредактировать* Для этого предназначена HTML-форма editnewsform^php, содержимое которой представлено в листинге 18*6.
戸 ЛИ,&,,•親)И,.у|уЛ7у|**.уд^-,^ку^*у^^|^%*95у1г・645.у^ц^4е,,М4а¥,*у,,.А^."ЛМЛ4**,.!*,$М>;.у.%.^1!*;^]Р,**=+*¥.ДА^МЗ,,|»£3!,”.,+е,;.“.《",0、¥и,3*1;,\・.“^:>^|*};^^/0..!., i Листинг 18<6. Файл editnewsform.php;
<?php
// Устанавливаем соединение с базой данных
include ”../config,php";
// Так как для исправления мы будем использовать
// форму добавления новостей, необходимо соответствующим
// образом настроить управляющие переменные.
$titlepage = "Редактирование новости";
$button = ^Исправить";
$action = 0editnews,php";
// Извлекаем из таблицы news запись, соответствующую
// исправляемой новостной позиции
$query = "SELECT * FROM news
WHERE id_news^,* Ф $_GET [1 id^news'];
$new = mysql_query ($query);
if($new)
{
$row = mysql_fetch_array ($new);
// Берем информацию для оставшихся переменных из базы данных $name = $row [1 name1 ];
$body = $row「body";
$url = $row['url*];
$url_text = $row「url_text'];
$url_pict = $ row[1url_pict1];
$_GET[1id_news*] = $ row[1id^news,];
// Определяем, скрыто поле или нет
if($row[*hide1] = 'show') $showhide = "checked";
else $showhide =
// Включаем HTML-форму, в полях которой будет размещена
// редактируемая информация из таблицы news include ” addnewsform.php";
} else puterror{^Ошибка запроса к таблице новостей.4.11);
Для формирования HTML-формы, предназначенной для редактирования новостной позиции, используется ранее рассмотренная HTML-форма addnewsfbrm.php (см. листинг 18.4). Для этого из таблицы news извлекается запись, соответствующая редактируемой новостной позиции, поля которой подставляются в value-значения формы. В качестве обработчика формы editnewsfbrm.php выступает файл editnews.php, содержимое которого представлено в листинге 18.7.
;Листинг 18«7* Файл edftnews.php
S^tTkontlvr^?»!^*tt^^fWfifriVt^'t я9>¥«Лт!т4КЧ|: ■ i'i^Stnf^K^^^n^^K^^mK > .WH/fJJwmwiiJ»
include ".»/config*php";
// Проверим - достаточно ли информации для занесения в базу данных
if (empty ($_POST [ * name [ )) Links ("Отсутствует заголовок**);
if (ertptу ($_POST [1 body1 ]}) links (rrСодержание не введено");
if (errpty (帛一POST [* url_text' ]) & & ! empty ($_POST「url1 ])) $_POST[,url_textf] = $_POST[,url,J;
// Определяем, скрыта новость или нет
if($_POST(,hidel] = "on”) $showhide = "show”;
else $showhide = "hide";
// Добавляем протокол в url, если пользователь забыл это сделать сам $_POST[turlt] = strs($_POSTpurr ], “HTTP", "httpH);
if (! empty ($_POST [ ^l1 ]))(
if (strtolower((substr($__POST[*urlr], 0, 7))!^°http://") && (strtolower(substr($_POST[rurl']/ 0, 7))!=Mftp://")) $url=nhttp: 〃"・ $_POST「W ];
}
// Заменяем прямые кавычки обратными
$_POST [ *namer ] = str_replace ("' " # ”* ”,$_POST [' name * ]); $_POST「body,] = str__replace(,M",心",$_POST[1 body1 ]); $strtmp ="";
// Если в поле выбора картинки стоит знак "-” - удаляем изображение $url^pict = trim($_FILES [1 url^pict1 ] f1 name1 ]);
if ($url_pict = "-M I I !empty($url_pict))
$query = "SELECT * FROM news
WHERE idjnews=M. $_POST I Td_naws1 ];
$new = mysql_query($query);
if($new)
{
$row = mysql_fetch__array ($new);
if (is_file (”../”• $row U url」ict' ])) unlink (H. ./'\ $row [• url_pict『]);
$strtnqp = ”, urljpict=f * H;
} else puterror ("CtoidKa запроса к таблице новостей, ..**)# }
// Если поле выбора картинки не пустое - закачиваем ее на сервер if (!empty($url_pict) && $url_pict !="-”) {
// Закачиваем файл
$path = 11 Oles/n.date ("YndHis", time ()).
strrchr ($_FILES [1 url_pict1 ] [1 name 1 ” •");
if (copy ($_FILES [f urljpict1 Ц * tnip_name1 ], H., / '• .$path)) $strtmp = ", ur l_pict= * $path111;
}
// Формируем и выполняем SQL-eanpoc на обновление новостной позиции
$query = "UPDATE news SET name^r rr. $_POST [ fname1 ] * H * ₽
body— ",$_POST['body*] <H\ url=T". $_POST [' ur「]・ ” I url_text=-'・ $_POST [1 url_text • ] .H 1,
hide=1$showhide'
$s55p
WHERE id_news="* $_POST「id_news'];
if(mysql^query($query))
{
// В случае успеха осуще ст вл нем автоматический переход на главную
// страницу администрирования echo H<HTML><HEAD>
<МЕТА HTTP-EQUIV=1Refresh1 CONTENTS 0;
URL=index.php?start=".$_POST[•startf]. **?start=".$_POST Г start,]・”,> </HEADX/HTML>H;
} else links (пОшибка при исправлении новостей,r);
// Вспомогат ель на я функция для вывода ссылок возврата function links($msg)
{
// см. листинг 18.3
) ?>
<?php
// Устанавливаем соединение с базой данных require_once (f,.. /config. php");
// Если новостная позиция имела изображение - удаляем его
$ query = 11 SELECT * FROM news
WHERE id_news=#\ $_GET「id_news」;
$new = mysql_query ($query);
if($new)
(
$row = mysql_fetch_array ($new);
if (is__file (”••/”・ $row[ *url_pictf ])} unlink (и. . /M.$row[ 'url_picf ]);
} else puterror (^Ошибка запроса к таблице новостей.. <11);
// Формируем и выполняем SQL-запрос на удаление записи в таблице news
$ query = rf DELETE FROM news
WHERE id__news=M. $_GET [ Г d_iwws'];
if (mysql_query ($query))
{
// Осуществляем автоматический переход на страницу администрирования echo U<HTMLXHEAD>
СМЕТА HTTP-EQUIV=1Refresh1 CONTENT=,0;
URL=index.php?start=n.$_GET[* start']."■> </HEAD>n;
} else puterror ("Ошибка при обращении к рассыпке*1); ?>
За сокрытие и отображение новостного блока на сайте несут ответственность скрипты hide.php и show.php соответственно. Содержимое файла hide.php представлено в листинге 18.9. Этот скрипт» как и delnews.php, принимает два параметра — id_news и start.
^^卷 ЖЖ^“ ч7^у^^у^^»^^'1Мй^МОйММЙ1ММЙМЙШШЙ1ЙЙ1ЙВ^^^
i Листинг 18.9. Файл hide.php
<?php
// Устанавливаем соединение с базой данных
require_once('\ */config•php");
// Формируем SQL*запрос
$query = "UPDATE news SET hide = * hide1
23 3ar 1181
WHERE id_news="・ $_GET「xd^news『];
if <mysql_query($query)) ( echo n<HTMLXHEAD>
<META HTTP-EQUIV^1Refresh * CONTENT"‘0;
URL= index. php?start-*1. $_GET [ ♦ start1 ] J" >
</HEAD>";
} else puterror{"Ошибка при обращения к блоку новостей");
Сокрытие новостного блока осуществляется за счет установки полю hide значения 'hide*. Файл show.php аналогичен hide.php (см. листинг 18.9) за исключением присваиваемого полю hide значения, в качестве которого выступает 'show*.
Переход по ссылке Подписчики на главной странице системы управления рассылкой приводит к системе управления подписчиками, которая состоит из следующих 8 файлов:
users.php — главная страница управления подписчиками;
О adduserfbrni.php — HTML-форма штя добавления нового подписчика;
adduser.php — обработчик формы adduserfbrm.php;
Рис, 18.3. Страница управления подписчиками
口 editiisei'sform.php — HTML-форма для редактирования данных подписчика;
口 edituser.php ——обработчик формы editusersform.plip;
口 deluser.php — скрипт удаления подписчика из системы администрирования;
口 inactive.php — скрипт, дезактивирующий подписку;
□ active.php — скрипт, активирующий подписку.
За формирование главной страницы управления подпнснмкамн несет ответственность файл users.php, содержимое которого представлено в листинге 18.10. Внешний вид страницы users.php представлен на рнс. 18.3.
,.津暇毋jj^^y^^^^^^^^^^^^^^^^^^^i^l^^^^f^^^a^^^^^ W^^^ 汽у》■,畳,啰:芋(,[堂魄*笄冃^^^y :q^^^^^^^ 毋::閣^^l、3,y,:,T 笃 *2*5*?*' ""Л
i Листинг 18.10. Файл users.php
<?php
if(empty($_GET[*start1])) Sstart = 0;
else $start = $__GET [1 start1 ];
// Устанавливаем соединение с базой данных require_once(н../config.php");
$query = "SELECT * FROM users
ORDER BY name
LIMIT $start, $all_numberenews";
$usr = mysql_query ($query);
if(!$usr) puterror(мОшибка при обращении к блоку новостей");
// Выводим ссылки управления подписчиками: добавление, удаление
// и редактирование
echo ”<а href=adduserform.php?start=$start>
Добавить подписчика</а><Ьг>";
echo "<а href=index.рЬр>Новости</а>";
if (mysql_nmt_rows ($usr) >0)
{
?>
<table>
<tr>
<td>PlMH</td>
<td>0praHM3auMH</td>
<td>e-mail</td>
<td>cTaTyc</td>
<td colspan=3^eficTBMH</td>
</tr>
<?php
while($users = mysql fetcharray($usr))
{
// Если подписка не активирована (actives1 inactive1), выводам // ссылку "активировать", если активирована (hide='active *)- // "дезактивировать”
土員$users [' status '] = ' active')
{
$3howhide = ”<a href«inactive.php?id_user=n.$users[Tid_userf].H& start=$start>4e3aKTMBMpoBaTb</a>";
$str_stat = "Активироваим;
}
else
(
$showhide = "<a hrefHactive.php^id—Usern”*$users['id_aser‘]*"& start=$start >Активировать</a>M;
$str_stat = "Дезактивирован11;
)
// Выводим данные подписчика
echo "<tr>
<td><a href=editusersform* php?id__user=M . $users [1 id^user * ]. "& start=$start>M. $users [ *name' ]. H</ax/td>
<td".$users[*organization*]»" </td>
<td>”.$users[1 email *]."</td>
<td>$str_stat</px/td>
<td>$showhide</td>
<td><a href=deluser.php?start=$start&
id_user=*', $ users [1 id_user1 ] • ° ^Удалить v/ax/td>
<td><a href=editusersform.php?start=$start& id_user=".$users[* id_user1].м >Исправить</a></1d> </tr>";
echo l,</table>n;
/ / Выясняем общее количество новостей в базе данныхf для того чтобы // правильно отображать ссыпки на последукаде и предьодунд^е новости. $tot = mysql_query("SELECT count(♦) FROM users
WHERE type_news = ^status*");
if ($tot) $total = mysql_result($tot,0);
else puterror (^Ошибка при обращении к блоку новостей**);
// Выводам ссылки на "Следующие" и "Предыдущие” новости
// Если $start больше нуля, значит, у нас есть предыдущие новости if ($start > 0) echo "<а href=1 users.phpTstar^".
($ start - $ al l_nuiribe renews)
."1 >Предадущие <b>$ a 1 l_nunibe renews</b> новостей</а> и;
// Разделитель выводам только в том случае, если есть "Предыдущие и
// мСледующие11 новости
if ($start > 0 && $start + $ а 11_пшпЬе renews < $total) {
// Сюда можно вставить картинку-разделитель 11 Предыдущихп и
// "Следующихн новостей
}
// Если $start+$pmmber все же меньше общего количества новостей,
// значит, у нас есть следующие новости
if($start + $all_number_news < $total)
echo "<a href=*users.php?start=H.($start + $all_nuniberenews).
111>Следующая <b>$all_number_news</b> новость</а>";
Файл users.php формирует в результате своей работы таблицу, каждая строка которой соответствует отдельному подписчику. В ячейках таблицы размещается информация о подписчике: нмя, организация, e-mail, статус и три управляющие ссылки: Дезактивировать/Активировать, Удалить и Исправить, позволяющие изменить статус подписки, удалить подписчика и редактировать его данные соответственно. Структура файла аналогична ранее рассмотренному файлу index.php (см. листинг 18.3).
Добавление нового подписчика осуществляется при помощи HTML-формы adduserfbrm.php, содержимое которой представлено в листинге 18.11. Внешний вид страницы добавления нового подписчика представлен на рис. 18.4.
<form actionra<?php echo $action; ?> method«post>
Имя <input type=text name=name values,<?php echo $name; ?>*><br>
Организация <input type=text name=organi zation value=f<?php echo ^organization; ?>* ><br>
E-mail <input type-text najne=email value=*<?php echo $ email; ?>1><br> Активировать <input type^checkbox name=active <?php echo $active; ?»<br>
<input “<input ,<input ■ <input </form><input “<input ,<input ■ <input </form>typers ubmit type=hiddcn type=hidden type=hlddentypers ubmit type=hiddcn type=hidden type=hlddenvalue«<?php Buho $ button; ?»<br>
<input “
<input ,
<input ■ <input </form>
<input “
<input ,
<input ■ <input </form>
typers ubmit type=hiddcn type=hidden type=hldden
typers ubmit type=hiddcn type=hidden type=hldden
Рис. 18.4. Добавление нового подписчикаРис. 18.4. Добавление нового подписчикаname=id_user value=<?php echo $id_user; ?» name*start valuei=i<?php echo $_GET[1 start1 ]; ?» name^status value=<?php echo $_G£T〔'status * 1; ?»
Рис. 18.4. Добавление нового подписчика
Рис. 18.4. Добавление нового подписчика
Форма содержит три текстовых поля: name, organization и email ДЛЯ имени подписчика, названия организации, которую он представляет, и почтового адреса, на который будут отправляться письма рассылки. Кроме этого, для
указания текущего статуса подписки форма имеет флажок active. Нажатие на кнопку позволяет отправить данные обработчику, расположенному в файле addiiser.php (листинг 18.12).
^^^^^^^^I^^^jff^^^^^^^产*卡j^^'‘'*^^^^^j^^^^^^^1"岩,"^^時":ti^42注 14y *** I R^j^j^^,41^1^^^^ t^^*『'产 ^^'* •*蹲'^^^^^^y 七干"”q,:
]Листинг 18.12. Файл adduser.php;
r,冉储信寸常芸公務*v%;;«二;*X;.;“;3 ”:r;Ev** 壽tCTifi:“:.;両普电工 tr^Jj E.」;17;.“!;#;гЯШ,,” “」|喻忐"n f•“器・;・>№ hz “ 川:託?i^^"・3・,"X” …:
<?php
include . * /config. php”;
// Проверим - достаточно ли информации для занесения в базу данных
if(empty($_POST[1name1])) links("Отсутствует имя");
if(en^ty($_POST[1email1])) links("He введен e-mail°);
if (!preg^natch(n/[0-9a-z_]+@[0-9a-z_A\.]+\.(a-z](2,3}/i", $email)) links (S—POST [1 email 口 #
nВведите e-mail в виде <i>something@server.com</i>H);
// Определяем, активирована подписка или нет
if ($_POST [1 active * ] == "on”)$active = "active**;
else $active = ° inactive**;
// Заменяем одинарные кавычки обратными, чтобы избежать конфликта
// при добавлении информации в таблицу
$_POST[lnamet] = str_replace("'п, "、", $_POST [1 name']);
$_POST [ Organization * ] = str_replace (H 1 n, "'", $_POST[ Organization* ]);
// Формируем и выполняем SQL-запрос на добавление новости
$query = "INSERT INTO users VALUES (。,
M,.$_POST[,namer] J',
『” • $_POST [f organization' ] JI "'.S^POSTL'email1] ”.
'$active')";
if (mysql_query ($query))
{
// Если запрос выполнен удачно, осуществляем автоматический переход // на главную страницу адаинистрирования echo n<HTMLXHEAD>
СМЕТА HTTP-EQUIV=1 Refresh1 CONTEN2 0;
URL=users.php?start=M * $_PQST [* start1] .tM> </HEAD>'1;
} else links < $_POST[1 status11# "Ошибка при добавлении подписчикаи);
// Вспомогатель на я функция дая вывода ссылок возврата
function links($msg)
echo ,|<р>11. $msg. n</p>H ;
echo **<p><a href=# onClick=,history*back() *> Вернуться к добавлению подписчика</ах/р>м; exit ();
Как видно из листинга 18.12, после соединения с базой ланннх осуществляется проверка корректности введенных данных, после чего формируется и выполняется SQL-запрос на добавление новой записи в таблицу users,
:Замечание
Как и в предыдущих Web-приложениях, HTML-форма настроена на повторное использование при редактировании параметров подписки. Файлы edituser.php и editusersform.php, отвественные эа редактирование, здесь рассматриваться не будут. Полный код Web-приложения можно найти на прилагаемом к книге компакт-диске, а также по адресу http://www.softtime>ru/info/downloads.php.
Удаление подписчика из системы осуществляется при помощи скрипта deluser.php, содержимое которого представлено в листинге 18.13. Скрипт принимает два параметра: id_user —— первичный ключ, соответствующий удаляемому подписчику, и start (необходим для корректного отображения главной страницы).
f,A、*W " : "?11 h *「": " ;*〔*:*,213 ? w~q*Wjr,,y :艺伊 **^у W.K-?Ly工"■w's^tw^■宣*二£F塁学产±^^fl][冲端AУЖК**':'三混J* /':"x***11:*"'"'"*:*******11**1'"""……'……** :Листинг 18.13, Файл deluser.php:彳 专
<?php
// Уст^авливаем соединение с базой данных require_once./config.php");
// Формируем и выполняем SQL-запрос на удаление записи в таблице users
$query = мDELETE FROM users
WHERE id_user=" , $_GET「id_user'];
if (mysql_query ($query))
{
// Осуществл нем автоматический переход на страницу администрирования echo "<HTMLXHEAD>
<МЕТА HTTP-EQUIV= Refresh1 8NTENT='O;
URL==users.php?start=n>$_G£T「start1 ] ."*>
</HEAD>n;
} else puterror ("Ошибка при обращении к рассылке1');
?>
За активацию и дезактивацию рассылки несут ответственность скрипты active.php и inactive.php соответственно. Содержимое файла active.php представлено в листинге 18.14.
[装:挙,;'[聲 К&**":*Л?” ",У比 i*W~t^^J^^^M^|^-!^yjg^,^y^^y^m苗装早,^yy^^ie.j [^^欢賀У ¥ ••寺吋*j^^* 坦.ууз”产,—|^” •:莘,^^用: ;Листинг 18.14, Файл active.php
<?php
require_once {"…/config.php");
$query ^ "UPDATE users SET status^*active1
WHERE id__user=n . $_GET [' id_user1 ];
if (mysql_query ($query)) {
echo "<HTML><HEAD>
СМЕТА HTTP-EQUIV=1Refresh1 CONTENT"『0;
URL=users. php?start='\$_GET「start *].”『>
</HEAD>";
} else puterror(мОшибка при обращении к блоку новостей"); ?>
На этом разработка системы администрирования почтовой рассылки завершена.
18.2. Разработка
системы представления
Система представления несет ответственность за отображение новостных блоков на сайте. Кроме того, система предоставляет посетителям сайта возможность подписаться на новости данного ресурса. В состав системы представления входят 5 файлов:
□ index.php — главная страница системы представления;
口 news.php ——вспомогательная страница системы представления;
describefbrm.php — HTML-форма для подписки на рассылку;
describe.php — обработчик формы describefbrm.php;
activate.php — скрипт активации/дезактивации подписки.
Внешний вид главной страницы представления, формируемой скриптом index.php, представлен на рис. 18.5. Содержимое файла index.php представлено в листинге 18.15.
Рис. 18.5. Страница представления новостей на сайтеРис. 18.5. Страница представления новостей на сайте
Рис. 18.5. Страница представления новостей на сайте
Рис. 18.5. Страница представления новостей на сайте
W,弟 Ц^!^^[,ЗМ&*書•^^Л>^А^^^^^|^^Л^Л:у^Ц*, 'AmWWtij^^S^yA^V^yyy^**^":.学,f过У У*&廿,*”F,〕'守":KJ 丁竺 q》tH j”, " !A ? Листинг 18.15* Файл Index.php «>■>■■<■; bbni>i'ft<^$^^f%Mif}^ ■ iViTi'Sf% A№(t^^^^^^M^^^%^^^^^nff^^^W^^^^^^^MH^m I«r> I i« Д •Лг.> >« > <:'■ ■ • e >=»:.*.**;.$|
vhtml>
<head>
<title>HoBOCTM</title>
</head>
<?php
// Этот файл выводит первые Spnumber новостей
// Устанавливаем соединение с базой данных require_once ("config. php");
// Выясняем общее количество новостей в базе данных, для того чтобы // правильно отображать ссылки на последующие новости.
$query = "SELECT count(*) FROM news WHERE hide=1 show1n;
$tot = mysql_query ($query);
if ($tot)
$total = mysql_result($tot,0);
// Если в базе новостей меньше чем $pnumberf // выводим их без ссылки "Все новости".
if($pnumber < $total) echo 'Уа href=news.php>Bce новости</а>°;
} else puterror("Ошибка при обращении к блоку новостейи);
〃 Запрашиваем все видимые новости, т.е. те, у которых в базе
// данных hide='show1, если это поле будет равно 'hide1, новость
// не будет отображаться на странице
$new = mysql__query ("SELECT * FROM news
WHERE hide = 'show'
ORDER BY putdate DESC
LIMIT $pnumberM);
if ($new)
{
while ($news = mysql_fetch_array ($new))
{
// Вывсщим заголовок новости
echo n<p><b>r,. $news「name' ] J</b></p>";
〃 бормируем анонс
// Переменная $numchar содержит примерное
// количество символов в анонсе
$pos = strpos(substr($news[*bodyf],$nunichar), ” ");
// Если новость длинная, то выводам троеточие...
if{strlen{$news[rbody1])>$numchar) $srttmpend = ”.・」'; else $strtirpend ="”;
// Выводим анонс
echo substг($news[1 body1]f 0, $numchar+$pos).$srttmpend;
echo " va href=news. php?id__news=rr. $news [ * id_news1 ] . ,,>подробнее</а>Г|; }
} else puterror("Ошибка при обращении к блоку новостей11};
?>
<br><br><a href=describeform.php>IIolmiMcaTbCH</a>
Скрипт выводит последние $рпшпЬег новостей, доступных для просмотра с сайта (у таких новостей поле hide таблицы news принимает значение %how')・ Изменить число $pnumber можно в конфи гураци онном файле config.php* При этом, если число добавленных новостей ($totai) превышает число отображаемых ($pnumber), на странице выводится ссылка —— показать все новости. За отображение страницы в этом режиме несет ответственность файл news.php, содержимое которого представлено в листинге 18.16.
<table>
<tr>
<td>
<?
include ” index.php";
</td>
<td>
<hl>HOBOCTH</hl>
<?
if (isset($_GET[fid_newsf]))( ?>
<pxa href='* news. phpц>показать все новости</а>
<?
J
?>
<?php
// Устанавливаем соединение с базой данньох require_once{"…/config* php");
// Проверяем переменную $ start, равную порядковому номеру первой
// новости на странице
if(empty($_GET[* start *])) $start = 0;
else $start = $_GET[1start1];
〃 Запрашиваем новость id_news (она должна быть видимой hide=,show,)
// Если скрипту передан первичный ключ выводимой новости, отображаем
// только одну новость
if(isset($_GET[1id_news1])) $query = °SELECT * FROM news
WHERE hide=*showr AND
id_news=” ・ $_GET「id_news'1;
// если параметр id_news не установлен - выводам все новости else $query = "SELECT * FROM news
WHERE hide=1 show•
ORDER BY putdate DESC
LIMIT $start, $а11_питЬег_печзи;
$new = mysql_query ($query);
if ($new)
while($news = mysql_fetch_array($new)) {
// Выводим заголовок новости
echo $news(*name,].$news[rputdate,];
/ / Выводим изображение
if(trim($news[#url^>ict*]) != "” && trim($news [1 url__pict * ]) != ”-”) echo ”<img align=right class=img src=".Snews[1url^pict,]・">, // Выводим тело новости, переводя символы
// переноса строки в тег <br>
echo nl2br ($news [ Ibody, ]) . ,т</р>н;
// Выводим URL
if (trim($news [ 'urlr 1) ” "” && trim(^news [ *url1 ]) != r,-“) echo ”<a href="* $news「url'].">"・ $news[*url_text,].M</a>";
}
} else puterror(^Ошибка при обращении к блоку новоетейм);
// Выясняем общее количество новостей в базе данных, для того чтобы // правильно отображать ссылки на последукмцие и предыдущие новости- $tot 二 mysql_query ("SELECT count (*) FROM news WHERE hide=,show* "); if ($tot) $total = mysql_result($tot, 0);
else puterror ("Ошибка при обращении к блоку новостей1*);
// Выводам следующие новости
if($start > 0) echo ”<а href=1news.php?start=".
<$start - $all_nurober_news).M1> Предыдущая <b>$all_nuInber_news</^b> новость</а>н;
// Если $start+$pnumber все же меньше общего количества новостей,
// значит, у нас есть следующие новости
if ($start + $ а 1 l_nunibe r_news < $total)
echo '*<a ЬгеГ=*пемз,php?start=".($start + $all_number_news) .11 * >Следующая <b>$a 1 l_nuraberjews</b> новосгь</а>**;
?>
</td>
</tr>
</table>
Файл news.php может принимать два параметра. Через первый параметр id^news передается первичный ключ отображаемой новости. Если этот параметр не передан, отображаются все новости системы. Второй параметр start необходим для передачи позиции, начиная с которой следует выводить новости в том случае, если отображаются все новости.
Переход по ссылке Подписаться на главной странице (см. рис. 18.5) приводит к форме describefbrm.php, заполнив которую, посетитель ресурса может стать подписчиком новостей (рис. 18.6). Содержимое файла describeform.php представлено в листинге 18.17.
Рис* 18.6. Форма для подписки на новости
''"К^З^^^^^УЕ韋 ^У*Д^^^^**“** 姑‘*'工.挚、*^^"~У* 承尹*”^^^5У^МЙ^^ЙЙ^|^^ЙМ^^^ЙЙЙ^^^^^”^^Й:"7^1^ 實:;^^ 注黑^^^F^^yg*'"'1^ 尋^^^^ЙЙЙ^ЮЙК5‘
:Листинг 18.17. Файл describeform.php
*『..:.*,.♦♦...*.*..*.;".*.*•■.『**・ft.....*•!*■、 >«.^ t■ ■ I■ 7ж^.t/Л^■->■•• .4 >'>.'>i^V^«i>«?>««««<«¥«>'<•• <t№>v«^.«t/»'a\'ii'L*><»^*>«>T»«;««>«i«■«^i<■ ^*79■ ■ Г.>n^■ •?•?>?»-•■ ■*>•* j»f?W*f
<table>
<form action=Mdescr ibe. php" methods "postn >
Имя <input type=text name=name><br>
Организация <input type=text name=organization><br>
E-mail <input type=text name=email><br>
<input type=submit уа1ие=||Подписатьсян><Ьг>
</fonn>
Форма describeform.php состоит из трех текстовых полей: name, email и organization н кнопки, позволяющей отправить данные обработчику describe.php, содержимое которого представлено в листинге 18*18.
Поле organization является необязательным для заполнения.
义计ДМ{У^мс^Дьуд^ДЩ^*Д**м^ЦШшмудм^и;.*у^ш^ l^ffilH1^ttfctfttttt^5!OilMmii^JJJI-"Rf¥ ^Ш^^ *л^^ййЖйМ^ШЖ^й^
:Листинг 18d8; Файл describe.php
<html>
<head>
<title>noATiMcaTbca на новости компании SoftTime</title>
</head>
<bcdy>
<?php
// Устанавливаем соединение с базой данных require_once("config.php");
// Проверяем правильность заполнения полей
if(empty($__POST[*паше1])) links(^Отсутствует имя");
if (en^ty ($_POST「email* ])) links (иНе введен e-mail");
if (!preg_match(м/[0-9a-z_]+@[0-9a-z_A\.]+\.[a-z]{2,3)/i", $email)) links (иВведите e-mail в виде <i>something@server »ccan</i>**);
// Проверяем, не имеется ли уже такого e-mail в базе данных
$ query = "SELECT COUNT (*) FROM users WHERE email LIKE ” .$_POST「name * ];
$usr = mysql_query($query);
if ($usr)
{
if (mysql_nuin_rows ($usr) >0) links (^Подписка на данный e-mail уже произведена");
}
// Заменяем одинарные кавычки обратными, чтобы избежать конфликта
// при добавлении информации в таблицу
$_POST I 'name4 ] = st r_replace I ”'","'", $_FOST [ * name1 ]);
$_POST[Organization1] = str_replace(”1", "'", $_POST[rorganization']);
// Если все правильно, добавляем подписчика в таблицу users, //но оставляем его не а ктивиро в анным до получении подтверждения
$query = »INSERT ЮТО users VALUES (0,
,M.$_POST[,name,) ."\ ,"•$_POST[Organization']J 。
I ” * S_POST「emaiJL」JI
'public I
,inactive')";
if (mysql_query($query)) {
$id_user = mysql_insert_id();
$thm = "Подписка на новости компании Mevako";
$msg = ^Здравствуйте, ".$_POST(*name']."!
Получение новостей компании Mevaco возможно после завершения этапа подписки. Для этого следует активировать подписку на новости по следующей ссылке ”.$_SERVER{"SERVERNAME"]・"/activate.php?status=active& id_user=$id_user&key=" .md5 {$_POST [f name1 ]) .11
Отказаться от новостей можно по ссылке
".$_SERVER["SERVER—NAME”] . "/activate.php?status=inactive&id_user=$id_user &key=" .md5 ($_POST「name* ]);
// Изменяем кодировку
$thm = convert_cyr_string (stripslashes ($thm), *w*, * k1);
$msg = nl2br{convert_cyr_string(stripslashes($msg),1wf r1k1));
if(mail($_POST[1 email1], $thm, $msg)) {
echo **Сейчас на ваш e-mail будет выслано письмо, по указанной в нем ссылке можно будет активировать подписку.";
}
}
function links($msg)
{
echo "vp>”.Smsg."</p>”;
echo "<p><a href=# onClick='history.back()'>Вернуться к подписке</a></p>rt;
exit ()『
}
?>
</body>
</html>
Скрипт в листинге 18.18 следит за корректностью данных в форме и проверяет, имеется ли в базе данных подписчик с переданным из формы e-mail. Если такой e-mail уже существует, работа скрипта останавливается с преду* преждением. Если же подписчик указывает e-mail, который отсутствует в таблице users, в нее добавляется новая запись, поле status которой при-
нимает значение inactive'. Таким образом, в таблице появляется запись, но подписчик еще не может получать пнсьма рассылки. Для того чтобы поле status получило значение Active' и подписчик мог получать письма, необходимо пройти второй этап регистрации. Для этого на e-mail подписчика высылается почтовое уведомление, содержащее ссылку на скрипт activate.php (листинг 18.19) для активации подписки. Переход по этой ссылке запускает скрипт activate.php, изменяющий соответствующим образом запись в табли* це users. Скрипт принимает трн обязательных параметра:
口 status — параметр, принимающий одно из двух значений, active* и Inactive* для Подписки и отписки от новостей соответственно;
П id_user ——первичный ключ записи таблицы users, соответствующей подписчику;
П key ——хеш-код имени подписчика, вычисленный по алгоритму md5, необходимый, чтобы избежать изменения статуса подписчиков злоумышленниками.
i Листинг 18.19. Файл activate.php
<?php
// Устанавливаем соединение с базой данных
require_once (rtconfig.php**);
$status = $_GET(1 status *];
$id__user = $_GET [1 id__user'];
$key = $_GETrkey4;
if(empty($status)) links("Ошибка активации/дезактивации подписки”); if(empty($id_user)) links(пОшибка активации/дезактиващии подпискин); if(empty($key)) links(11 Ошибка активации/дезактивадии подииски");
/ / Проверяем полномочия подписчика
$ query = ** SELECT * FROM users
WHERE id_user = $id_user°;
$usr = mysql_query($query);
if(!$usr) links(нОшибка активации/дезактивации подпискип);
$user = mysql_fetch_array($usr);
if (xnd5 ($user [' name1 ]) = $key)
// Посетитель имеет полномочия на поддписку, // поэтому изменяем статус его подписки
$query = "UPDATE users SET status»'^status' WHERE id_user = $id_user";
if (mysql_query($query))
echo nСпасибо за использование системы новостей компании SoftTime";
)else links ("Ошибка активаиии/дезактивании подписки1*); function links($msg)
{
echo ”<р>".$msg."</p>";
exit 6;
ЧАСТЬ IV
Профессиональный форумна PHP 5
Глава 19
Проектирование
На протяжении ближайших трех глав будет разрабатываться форум, который мы назвали LiteForum. В отличие от гостевой книги, основное назначение которой —— поддержка обратной связи с посетителями ресурса, форум предполагает более тесное общение н возможность вести дискуссию. Каждый посетитель может внести тему на интересуюидий его вопрос, по которой он и другие посетители смогут оставлять сообщения.
При этом движок форума должен обеспечивать следующие функции:
口 регистрация и обновление регистрационных данных участников форума;
О надежная авторизация;
口 возможность зарегистрированным и незарегистрированным посетителям добавлять новые темы и сообщения, а также прозрачную навигацию по уже созданным темам;
口 сообщение посетителям о новых темах;
口 полнотекстовый поиск как по заголовкам тем, так и по сообщениям;
предоставление различных сервисов: отправка письма при добавлении новой темы, отправка письма посетителю с форума, вывод участников on-line, вывод полного списка участников и др.;
口 возможность быстрой смены дизайна форума (переключение скина (skins));
口 поддержка нескольких разделов в форуме и возможность быст}юго перехода между разделами;
возможность выбора режима представления информации участниками: структурный (лестничный) или линейный (с прямой и обратной сортировкой сообщений);
поддержка тегов форматирования текста.
Форум должен обладать гибкой системой администрирования, выполняющей следующие функции:
простое развертывание одного или нескольких форумов на сервере (инсталляция);
口 создание нескольких разделов в форуме;
модерирование;
предоставление статистической информации по форуму;
предоставление информации по участникам форума;
口 настройка форума: число выводимых на одной странице тем, максимальный размер прикрепляемого к сообщению файла и фотографии в портрете посетителя, срок дейстивия cookie, переключение скинов н др.
Проектирование форума будет разбито на два этапа: проектирование базы данных н структуры форума.
Проектирование базы данных
База данных форума Lite Forum будет состоять из пяти таблиц:
authors — данные зарегистрированного посетителя форума;
forums — разделы форума;
themes — темы форума;
posts — таблица для сообщений форума;
口 settings — настройки форума.
Посетители форума могут инициировать новые темы для обсуждения и добавлять сообщения как после регистрации на форуме, так и без нее. Зарегистрировавшемуся посетителю гарантируется, что его имя не будет использоваться остальными участниками форума. Кроме этого, ему предоставляется дополнительный сервис: его информируют обо всех новых сообщениях с момента его последнего посещения форума, он имеет возможность исправить свое сообщение до того, как на него ответит другой посетитель, имя зарегистрированного посетителя будет являться гиперссылкой, позволяющей другим участникам просматривать контактную информацию, которую он по желанию может поместить в свой “портрет”.
Регистрация нового участника форума будет сводиться к добавлению новой записи в таблицу authors, которая имеет 13 полей:
口 id_author ——первичный ключ таблицы, снабженный атрибутом auto_ INCREMENT;
name — имя зарегистрировангюго посетителя — ник;
口 passw — пароль;
口 email — e-mail посетителя;
sendmaii — поле типа enum, принимающее два значения yes* и по\ Если данное поле принимает значение 'yes', при появлении новой темы на форуме посетителю отправляется уведомление на его e-mail, указанный в предыдущем поле;
口 uri —— адрес домашней страницы посетителя или сайт организации, которую он представляет;
icq — номер ICQ посетителя;
口 about — поле, в которое посетитель может поместить несколько слов о себе;
口 photo — поле для хранения фотографии посетителя;
口 time —— время последнего посещения форума, это поле обновляется при авторизации м просмотре страниц форума;
1 astatine ——время предыдущего последнего посещения, необходимое для вывода новых сообщений с момента последнего посещения участником форума;
口 themes — количество сообщений, оставленных посетителем на форуме, данное поле увеличивается на едини цу каждый раз, когда посетитель оставляет новое сообщение;
口 statususer ——поле типа enum, принимающее три значения: пустая строка —для обычных посетителей, moderator — для модератора и 'admin' для администратора форума.
Первичный ключ id author будет также порядковым номером зарегистрированного посетителя на форуме.
( Замечание )
Необходимо заранее продумать стратегию распределения порядковых номеров среди участников форума. Во избежание нездорового интереса к порядковому номеру 1 иногда полезнее начать нумерацию с цифры 10. Кроме того, можно отследить распределение номеров 13 и 666. Когда вашему начальнику на форуме достанется номер 13, доказать свою непричастность к этому будет много сложнее, чем написать сам форум.
Не все поля таблицы authors являются обязательными для заполнения посетителем при регистрации. К обязательным полям относятся только имя посетителя (name) и его пароль (passw). Часть полей вообще не предназначены для заполнения посетителем, их значения устанавливаются движком форума автоматически. Последнее поле statususer, позволяющее задать статус посетителя, может быть изменено только со страницы администрирования форума администратором.
SQL-оператор create, позволяющий создать таблицу authors, приведен в листинге 19.1.
CREATE TABLE authors (
id—duthor INT(6) NOT NULL ADTO_INCREMENTr name TINYTEXT NOT NULL, passw TINYTEXT NOT NULL, email TINYTEXT NOT NULL, sendmail ENUM《'yes「田} NOT NULL DEFAULT『n。', url TINYTEXT NOT NULL, icq TINYTEXT NOT NULL, about MEDIUMTEXT NOT NULL, photo TINYTEXT NOT HULL,
time DATETIME NOT NULL DEFAULT 10000-00-00 00:00:00*, last_time DATETIME NOT NULL DEFAULT 10000-00-00 00:00:00\ themes INT(10) NOT NULL DEFAULT ‘0]
statususer ENUM(* \ Moderator',1 admin1) NOT NULL DEFAULT ' PRIMARY KEY (id_authcr)
)TYPE=MyISAM;
Форум может содержать несколько разделов, между которыми посетители могут переключаться. Добавление новых разделов необходимо в том случае, когда форум слишком активен и количество появляющихся за один день новых тем больше чем их количество, которое отображается на первой странице форума. В этом случае наиболее часто обсуждаемую тему главного форума целесообразно вынести в отдельный раздел. Каждому разделу соответствует запись в таблице forums, имеющей 6 полей:
id_forum ——первичный ключ таблицы, снабженный атрнбутом auto_ INCREMENT;
name — название раздела;
rule — правила форума;
口 logo — краткое описание проблем, обсуждаемых на форуме;
pos ——позиция раздела форума относительно других разделов;
口 hide — поле типа enum, принимающее два значения: ’sho" и 'hide' для доступного и скрытого форума соответственно.
Так как в каждом из разделов тематика обсуждаемых проблем различна, поля rule и logo, определяющие правила поведения участников и краткое
описание проблемы, уникальны для каждого раздела, а не едины для всего форума в целом.
; Замечание )
Параметры, действие которых распространяется на весь форум, помещаются в таблице settings.
SQL-оператор create, позволяющий создать таблицу forums, приведен в листинге 192
:Листинг 19.2. Создание таблицы forums
:*.,*、* ♦£*. ;*¥*;貝-*」*т耳*. *'■'#* « «I ГЛ 疗* N/L$*. ;;$-.*;*;■?!**・ ”*;^IW*i5^” ^^Wi^l^^S^«H^^« i ■'■'••и eK-irrA'rir^wW^WmrB^tV^ieVlivVi * * «Л ■ ■■'■'■■« »^»4* ■* >'?r <
CREATE TABLE forums (
id_forum 工NT (6) NOT NULL AOTO_INCREMENTr
namenameMEDIUMTEXTMEDIUMTEXTNOTNOTNULL,NULL,ruleruleMEDIUMTEXTMEDIUMTEXTNOTNOTNULL,NULL,logologoMEDIMTEXTMEDIMTEXTNOTNOTNULL,NULL,pos TNT(6) NOT NULLpos TNT(6) NOT NULLDEFAULT 'O',DEFAULT 'O',
name
name
MEDIUMTEXT
MEDIUMTEXT
NOT
NOT
NULL,
NULL,
rule
rule
MEDIUMTEXT
MEDIUMTEXT
NOT
NOT
NULL,
NULL,
logo
logo
MEDIMTEXT
MEDIMTEXT
NOT
NOT
NULL,
NULL,
pos TNT(6) NOT NULL
pos TNT(6) NOT NULL
DEFAULT 'O',
DEFAULT 'O',
hide ENUM(fshow'Jhide•) NOT NULL DEFAULT 々how',
PRIMARY KEY (id_forum) )TYPE^MylSAM;
Как уже говорилось выше, каждый из участников форума может иницииро' вать обсуждение интересующей его проблемы, добавив в форум новую тему. Каждой теме соответствует одна запмсь в таблице themes, которая состоит из 7 полей:
口 id theme —— первичный ключ таблицы, снабженный атрибутом auto_ INCREMENT;
口 name — название темы;
口 author ——имя посетителя, добавившего сообщение;
口 id^author — уникальный индекс посетителя (см. листинг 19,1);
hide — поле типа enum, принимающее три значения: 'show', 'hide* и *1оск* для доступной, скрытой и закрытой темы соответственно;
time — время последнего обращения к теме;
口 id_forum ——уникальный индекс раздела форума, которому принадлежит тема.
Помимо первичного ключа id_author, таблица содержит поле для имени посетителя author, добавившего новую тему. Дополнительное поле author необходимо по двум причинам* Во-первых, это позволяет избежать двухтаб-
личного запроса при формировании списка тем (так как каждая тема должна сопровождаться информацией об ее авторе, числе сообщений в теме и времени последнего к ней обращения). Во-вторых, такая структура позволяет инициировать новые темы не зарегистрированным посетителем, имена которых не хранятся в таблице authors . В этом случае имя автора будет пометено в поле author таблицы themes, а поле id_author примет значение 0, которое будет сигнализировать о том, что посетитель не зарегистрирован.
В рассмотренных до этого Web-приложениях поле hide принимало лишь два значения: 'show' н 'hide*. Ресурс, чье поле hide принимало значение show1, был доступен для просмотра с сайта. Если поле hide принимало значение 'hide' — получить доступ к ресурсу можно было только со страницы администрирования. В таблице themes данные значения интерпретируются точно так же, но поле hide может принимать третье состояние — lock*. Введение третьего состояния связано с тем, что ресурс (в данном случае тема форума) подвергается редактированию (добавлению новых сообщений) не только администратором сайта, как это было в предыдущих Web- приложениях, но и посетителями форума. Поэтому администратор форума должен иметь возможность закрыть тему. В закрытой теме невозможно добавлять новые сообщения, а следовательно, и продолжать обсуждение.
Поле time предназначено для хранения времени последнего обращения к теме и обновляется при добавлении нового сообщения в данной теме.
ПОМИМО первичного ключа id^thenie, таблица themes имеет КЛЮЧ search типа fulltext, обеспечивающий полнотекстовый поиск по названиям тем и именам посетителей, добавивших тему.
SQL-оператор create, позволяющий создать таблицу themes, приведен в листинге J9.3.
vw^^i:"w】"‘?…:?';'£?…二二• ■"….…磷可^^5 s •:;;: 争芸
買Листинг 193 Создание таблицы themes
二.;二…:..:..I …也 …反....; 丿〃.; :..:.;為.£ “二.С.:'* .二..;….!•.:;舍余厶氏、ii.^Ai ;JЛ
CREATE TABLE themes (
id_theme INT(ll) NOT NULL AUTO_工NCREMENT#
name TINYTEXT NOT NULL,
author TINYTEXT NOT NULL,
id_author 工NT(6) NOT NULL DEFAULT 'O',
hide ENUM(『show1,fhide‘3 lock1) NOT NULL DEFAULT 'show I
time DATETIME NOT NULL DEFAULT *0000-00-00 00:00:00',
id_forum 工NT ⑵ NOT NULL DEFAULT O,
PRIMARY KEY (id_theme),
HJLLTEXT KEY search (name,author)
)TYPE^MylSAM;
Посетитель, инициирующий новую тему, помимо названия темы, размещает на форуме сообщение, которое является первым в теме. Остальные участники форума, которых заинтересовала объявленная дискуссия, также могут оставлять свои сообщения, отвечая на первое или последующие сообщения в теме. Каждому сообщению соответствует запись в таблице posts, имеющей 10 полей:
口 id_post — первичный ключ таблицы, снабженный атрибутом дито_ INCREMENT;
口 name — текстовое сообщение;
口 url — ссылка по теме сообщения;
putfiie — путь к файлу, который посетитель может добавить к сообщению;
口 author — имя автора сообщения;
口 id_author ——уникальный индекс посетителя (см. листинг 19.1);
hide — поле типа enum, принимающее три значения: fshow\ 'hide* и 'lock1 для доступного, скрытого и закрытого сообщения соответственно;
口 time —— время добавления сообщения;
口 parent^post —— первичный ключ сообщения, ответом на которое является данное сообщение;
id_theme —— уникальный индекс темы, которой принадлежит сообщение (см. листинг 19.3).
Поле hide здесь принимает три значения, но в отличие от темы сообщения установка последнего значения 'lock' приводит к запрету ответа на данное сообщение.
Поле time устанавливается один раз —— при добавлении сообщения —— н затем не меняется даже при последующем редактировании сообщения автором.
Поле parent__post необходимо для отслеживания структуры ответов. Форум будет поддерживать два режима работы: линейный (с сортировкой сообщений по дате добавления) н структурный (или логический, сообщения в котором приводятся в лестничной форме, в соответствии с тем, ответом на чье сообщение является текущий пост). Последняя форма требует, чтобы в поле сообщения сохранялась информация о родительском сообщении.
( 「амечаную )
Для самого первого сообщения, которое не является ответом ни на чье сообщение, поле parent_post принимает значение 0.
Точно так же, как и в таблице themes, в таблице posts имеется ключ search типа fulltext, обеспечивающий полнотекстовый поиск по телу сообщений и именам посетителей, добавивших сообщения.
SQL-оператор create, позволяющий создать таблицу posts, приведен в листинге 19.4.
г号中.^^4^^:.? 在g^梏^^»:置厘贵慘"事笠:军蹣宙逑纖衿^乎寸‘:经笑皙請弋齢*畳盘兜学嵯部軍.簪 ■>片齐,部,塔瞽^^^8^11882 ;Листинг 19.4. Создание таблицы posts
***.“;*r**、i**“*.*“***i*.,;;;;**iiii**jj. ..п.*.к$;”п.,“$Х:с『“、*.*.;;;、3;、?.吁.^^*#』*匕*^^^%1厶冷*卄^^?^^沼;丁. ;‘:¥ni*fiv^iTS^rt&'J^CJl^Art'V»、":?.i^$*^?WI^^^^5^A^^W^^f
CREATE TABLE posts (
id_post INT(II) NOT NULL AUTO_INCREMENTf name TEXT NOT NULL, url TINYTEXT NOT NULL, putfile TINYTEXT NOT NULL, author TINYTEXT NOT NULL, id^author INT(6) NOT NULL DEFAULT f0l hide ENUM('show','hide I'lock') NOT NULL DEFAULT fshow\ time DATETIME NOT NULL DEFAULT 丨0000-00-00 00:00:00', parent_post INT{11) NOT NULL DEFAULT ‘0’, id_theme LNT(ll) NOT NULL DEFAULT ‘0’, PRIMARY KEY (id_jx>st), FULLTEXT KEY search (name,author)
)TYPE=MyISAM;
Для хранения различных глобальных настроек форума используется последняя, третья, таблица settings, которая имеет 11 полей:
口 name__forum ——название форума, значение этого поля используется для заголовка окна на главной странице форума и также в качестве логотипа на вспомогательных окнах (добавления сообщения, регистрации и т. п.);
□ number_themes ——число тем, выводимых на одной странице (на страницы с остальными темами выводятся ссылки);
口 size_fiie —— максимальный размер файла (в байтах), прикрепляемого к сообщению;
口 size_photo — максимальный размер фотографии посетителя, которую он может загрузить на сервер при регистрации и обновлении своего "портрета11;
О send_maii — поле тип a enum, принимающее два значения: yes' и по\ разрешающее и запрещающее отправку уведомляющего почтового сообщения на почтовый адрес помешенный в поле email;
口 email ——почтовый адрес, куда отправляются почтовые сообщения, если их отправка разрешена;
口 show struct switch ——поле типа enum, принимающее два значения: 'yes' и ho', разрешающее и запрещающее соответственно вывод переключения
между структурным и линейным способом представления сообщений. По умолчанию используется структурный способ представления;
□ show_foruin_switch — поле типа enum, принимающее два значения: \es и по', разрешающее н запрещающее соответственно переключение между различными разделами форума;
口 hello — приветственная фраза, которая выводится в верхней части форума. По умолчанию иДоброго времени суток”;
口 cooktime ——срок действия cookies, устанавливаемых на машинах посетителей (в сутках);
口 skin — скнн, используемый форумом.
SQL-оператор create, позволяющий создать таблицу settings, приведен в листинге 19.5.
*:戈空,":生,^^,¥*£!*:戈空,":生,^^,¥*£!. . .产^ - 一黑^^^^;^^Ж^^^ 第少^^^罹^紀:贵瞿亨Y^^^f蒙e"'"、…/讨/舒*・y:,.嘗>.,r " 吧《笠芝… 屮”¥,r道上◎第岂戶,,
*:戈空,":生,^^,¥*£!
*:戈空,":生,^^,¥*£!
;Листинг 19.5. Создание таблицы settings
• ' •」■',,♦♦'“;■” ・,"Ут*:”'・Т^1^?,,е・ “*..C..rri* c J. f^At^^.^^^l^^^X^i.:*:、、*^^?,^^?* .А^^^Д^:„^"А】.%/;、^^^^.・ nA.V»*?i“;.:C,.:““ c;* .*y^":.:,〉d^»^^ ♦♦♦*♦••♦♦:
CREATE TABLE settings (
name_forum TINYTEXT NOT NULL, nuwber^themes INT(3) NOT NULL DEFAULT ‘0、 size_file INT (10) NOT NULL DEFAULT。, size_photo 工NT《10) NOT NULL DEFAULT *0', send_mail ENUM('yes','no') NOT NULL DEFAULT 'no*, email TINYTEXT NOT NULL,
show_struct_switch ENUM「yes', Fo') NOT NULL DEFAULT 'уез', show_f оrum_switch ENUM (1 yes ', 1 no *) NOT NULL DEFAULT ' yes1, hello TINYTEXT NOT NULL, cooktime INT (10) NOT NULL DEFAULT O, skin TINYTEXT NOT NULL
)TYPE=MyISAM;
LitcForum должен обеспечивать гибкий подход при работе с форумами и их разделами:
口 создание нескольких независимых форумов, каждый из которых имеет собственную базу данных н отдельную директорию;
□ в случае, когда существуют ограничение по используемым базам данных, создание на основе одного форума нескольких разделов с возможностью переключения между ними при помощи выпадающего списка;
口 даже когда существует несколько независимых форумов, каждый из них может иметь или не иметь дополнительных разделов.
В корневой директории forum будут расположены следующие поддиректории: □ admin ——в данной поддиректории расположены файлы системы админи* стрирован и я форума;
skel — движок форума, при создании нового форума, файлы из этой папки копируются в папку, название которой совпадает с названием нового форума;
skins —— в данной директории расположены Нскиныи форума — варианты дизайна;
О utils ——в данной директории расположены общие для форумов и системы администрирования файлы.
Кроме перечисленных поддиректорий? в директории forum может также присутствовать неограниченное число поддиректорий для созданных форумов, имена которых не совпадают с вышеперечисленными и задаются администратором. Содержимое этих директорий совпадает с содержимым поддиректории skel п содержит две поддиректории photo и files для фотографий зарегистрированных посетителей и файлов, прикрепляемых к сообщениям. Директория skel является своеобразным эталоном и используется для внесения исправлений, которые затем переносятся на все остальные форумы.
Файлы utils являются общими для всех форумов; кроме того, часть их используется системой адм и н и стрп рован ия форума.
Поддиректория skins предназначена для разделения дизайна и кода форума — это позволяет вести работы по дизайну и коду форума независимо.
〔 Замечание )
Все описываемые в данной книге Web-приложения, в том числе и этот форум, являются реально действующими и обслуживают множество сайтов в рунете. Поэтому для всех Web-приложений постоянно выходят новые версии (http://www.softtime.ru/info/downloads.php}. в которых увеличены функциональность, производительность и устранены мелкие ошибки. Потратив массу усмли访 на создание собственного варианта дизайна, вам не потребуется повторять эту работу при выходе новой версии форума.
Каждая поддиректория директории skins соответствует одному варианту дизайна (скину) и содержит пять стилевых таблиц action.css, fbrumxss, mainstyles.css, operate.css, read .css, "шапку" страницы diztop.php н подкаталог images с изображениями, используемыми форумом.
〔 Замечание )
Более подробное описание "скмнеГ и приемы создания собственного "скина" приведены в главе 20.
Глава 20Глава 20回
Глава 20
Глава 20
Создание базового "движка11
В данной главе будут рассмотрены директории skel и utils, содержащие базовый движок форума* Файлы в директории utils являются общими как для форумов, так и для системы администрирования и будут рассматрнваться на протяжении двух последующих глав по мере описания базового движка и системы администрирования.
Описание форума
Главная страница форума несет ответственность за вывод списка тем форума (рис. 20」).
Как видно из рис. 20.1, список тем оформлен в виде таблицы, где каждая строка соответствует отдельной теме, для которой указано число сообщений (в скобках указывается число новых сообщений), название темы, имя автора темы и дата последнего сообщения в теме. Название темы является гиперссылкой, переход по которой приводит к генерации страницы с сообщениями, относящимися к данной теме. За формирование этой страницы несет ответственность файл read.php.
В случае если автор темы зарегистрирован на форуме, его имя также является гиперссылкой, переход по которой приводит к странице с регистрационными данными участника (рис. 20*2).
Рис. 20*2. Perистрационные данные участника
В верхней части форума расположены управляющие ссылки (см. рис. 20.1). В показанном на рисунке варианте форума имеется два раздела PHP и О форуме, переключение между которыми можно осуществлять щелчком по названию (которое является гиперссылкой) или через выпадающий список с последующим нажатием на кнопку Перейти.
Рис. 20.3. Форма для добавления новой темы на форум
24 Зак 1181
Меню форума состоит из 10 ссылок: Новая тема. Регистрация, Вход, Анонимно, Поиск, Отметить все, Обновить визитку, Правила форума, Участники Online, Все участники.
При переходе по ссылке Новая тема открывается форма» представленная на рис. 20.3, в которой посетитель может ввести свое имя, пароль (для зарегистрированного посетителя), название темы и содержание сообщения. При желании посетитель также может указать URL по теме или прикрепить файл при помощи двух последних полей формы. Название темы и текст сообщения могут быть форматированы при помощи пользовательских тегов: [Ь] и [i], позволяющих выделить текст полужирным и наклонным начертанием. Тег 也]помечает начало выделенного фрагмента текста, а [/ы помечает конец фрагмента. Кроме этого, код в тексте сообщения (напрнмер, код PHP-скрипта) может быть обрамлен тегами [code] и [/codeb Текст, помещенный между данными тегам и > будет подсвечиваться соптасно синтаксису PHP. Кнопка Отправить позволяет поместить новую тему на форум.
При переходе по ссылке Регистрация открывается форма, представленная на рис. 20,4. Форма позволяет зарегистрировать нового посетителя сайта. Для этого в первом поле формы следует ввести имя посетителя (ник), в двух последующих полях вводится пароль. Все остальные поля формы являются необязательными и предназначены для предоставления посетителем контактной и личной информации. Поля формы позволяют ввести e-mail, номер ICQ, адрес домашней страницы посетителя или сайт организации, которую он представляет, дополнительную текстовую информацию о себе и фотографию или изображение. Форма также содержит флажок, после установки которого посетитель будет получать уведомления при добавлении но* вых тем. Уведомления отправляются по адресу, указанному в поле e-mail.
Зарегистрированный посетитель может авторизоваться на форуме, осуществив переход по ссылке Вход. В результате этого будет открыта форма (рис. 20.5), поместив в поля которой имя и пароль, зарегистрированный посетитель может войти на форум. После этого в приветственной фразе Доброго времени суток, Посетитель слово "Посетитель*' будет заменено именем посетителя. Кроме того, будут выведены все новые сообщения с момента последней авторизации посетителя, а при добавлении новых тем н ответов на сообщения поля для имени н пароля будут заполнены автоматически. Переход по ссылке Анонимно позволяет осуществить "выход" с форума.
Регистрационные данные можно исправить» осуществив переход по ссылке Обновить визитку, после ааторизации (см. рис. 20.5) будет открыта форма, сходная с представленной на рис. 20.4. Отличие заключается в отсутствии поля для имени посетителя и в том, что поля формы заполнены старыми регистрационными данными, отредактировав которые, можно закрепить изменения, нажав кнопку Внести изменения.
Рис. 20.4. Форма регистрации нового посетителя
Переход по названию темы на главной странице форума (см. рис. 20.1) приводит к странице представления темы форума, на которой показаны сообщения выбранной темы (рис. 20.6).
Сообщения внутри темы могут быть представлены в трех вариантах:
口 структурный (см. рис. 20.6) — ответы на сообщения располагаются с некоторым отступом;
口 линейный с обратной сортировкой ——сообщения расположены одно под другим (как в гостевой кннге), при этом сообщения, добавленные позже, располагаются в начале темы, а добавленные раньше — в конце;
□ линейный с прямой сортировкой — сообщения расположены одно под другим, при этом сообщения, добавленные раньше, располагаются в начале темы, а добавленные позже — в конце.
当 Bkg/1 на tboovx (знгорюаими] - Microsoft Internet ”. ■間 F^l
Рис. 20.5. Авторизация посетителя на форуме
Рис. 20.6. Сообщения темы форума
Посетитель может переключаться на удобный для него вариант просмотра при помощи ссылок в правой верхней части страницы —— вид форума. Каждое сообщение снабжается ссылкой Ответить, переход по которой приводит к форме добавления сообщения, полностью совпадающей с формой добавления новой темы (см. рис. 20.3), за исключением текстового поля для на* звания темы. Если посетитель авторизован, он может осуществлять редактирование своих сообщений по ссылке Править сообщение.
(3@ме“ 2 HHe二)
Разрешено редактирование только сообщений, на которые нет ответа.
Рис. 20,7. Поиск на форуме
Переход по ссылке Поиск позволяет осуществлять полнотекстовый поиск по темам и сообщениям форума и авторам темы. Внешний вид страницы По-
иск представлен на рис, 20.7. В первое поле помешаются ключевые слова через пробел, второе поле предназначено для ввода максимального количества тем в результирующем списке. Следующий за этим полем выпадающий список позволяет выбрать режим поиска: в названиях тем и в сообщениях форума. Второй выпадающий список позволяет задать раздел форума, по которому осуществляется поиск. Два переключателя — н н или — позволяют задать логику поиска при наличии более одного ключевого слова. В первом случае осуществляется поиск при обязательном вхождении в текст всех ключевых слов, во втором ——при вхождении хотя бы одного из ключевых слов.
Разработка форума
Директория skel содержит 25 файлов, которые будут рассмотрены ниже:
口 addpost.php — обработчик формы addthemeform.php;
口 addtheme.php — обработчик формы addthemefbrm.php;
addthemefbrm.php — HTML-форма для добавления новой темы;
口 answer.php — HTML-форма для добавления нового сообщения;
authors!ist.php — скрипт, генерирующий список зарегистрированных посетителей;
口 editpost*php — обработчик формы editpostfbrm.php;
口 editpostform.php — HTML-форма для редактирования сообщения;
口 enter.php — обработчик формы enterforni.php;
口 enterform.php —— HTML-форма авторизации посетителя;
exit.php — скрипт ^выхода** посетителя с форума;
口 index.php — главная страница форума, на которой выводится список тем и управляющие ссылки;
门 infb.php ——скрипт, генерирующий страницу с личными данными зарегистрированного посетителя, которые он при желании заносит в свой "портрет” при регистрации;
口 mail.php — обработчик формы niailfbrm.php;
mailform.php ——HTML-форма для отправки e-mail зарегистрированному посетителю;
online.php — скрипт, генерирующий список зарегистрированных посетителей, находящихся в данный момент на форуме;
口 read.php — страница просмотра темы форума;
readall.php —— скрипт, позволяющий отметить все сообщения как прочитанные;
register.php ——обработчик формы registerfbrni.php;
registerform.php — HTML-форма для регистрации нового посетителя;
О rules.php — скрипт, выводящий правила форума;
setstruct.php — скрипт, устанавливающий форму представления сообщений в теме: структурную (лестничную) или линейную (последовательную);
口 srchfbrm.php — HTML-форма и обработчик для поиска по форуму;
口 update.php — обработчик формы updatefbrm.php;
口 updatefbrm.php — HTML-форма для обновления регистрационных данных зарегистрированного посетителя;
口 updateshow.php — вспомогательная страница для вывода обновленных данных после исправления информации в портрете посетителя.
( Замечание )
Для работы многих из представленных в этом списке файлов требуются файлы из директории utils, которые мы будем рассматривать по мере надобности.
С Замечание )
Далее в листингах для удобства восприятия будет опущен несущественный HTML-код, связанный, в первую очередь, со стилевым оформлением Web- приложения. Полная версия данного Web-приложения находится на прилагаемом к книге компакт-диске, а также доступна по адресу http://www.softtime.ru/ 1nfo/d ownloads, php.
Рассмотрение будет начато с главной страницы форума — index.php* Внешний вид страницы, которую генерирует данный файл, показан на рис. 20」. Содержимое файла представлено в листинге 20.1.
Листинг 20.1. Файл Index.php
<?php
〃 Помещаем вывод в буфер
ob_start 0;
// Устанавливаем соеданение с базой данных require_once(мconfig.php");
// Подключаем библиотеку форума
require_cmce (” •・ /utLls/uti 工s .php");
// Извлекаем параметры из строки запроса
$id forum = $ GET [ * id forum* ];
$page = $_GET「page1];
// Проверяем значение $id_forum на допустимое значение
if (! is set ($id_forum) ) $i Jforum = L;
$ query = "SELECT MIN (id_f orum) AS min, MAX(idforum) AS max
FROM forums
WHERE hide != 'hide'";
$frm = mysql^query($query); if($£rm)
(
$formum = mysql_fetch_array ($frm);
if ($id_forum < $founum[ ,min, ]) $id_forum = $formuin( ^in1 ]; if ($id__forum > $formum[ *тах* ]) $id_foriim = $formum[ ^тсах1};
} else puterror ("Ошибка при выборке тем форума11);
// Вывод линейки новых сообщений
$ showf orwis 1 ine = true;
// Включаем "шапку11 страницы
require_once (”…/utils/topforum.php**);
И Отображаем темы no $pnumber штук
$pnuniber ^ Ssettings ['number_themes '];
// Если запрос выполнить не удалось и переменная
// осталась не инициализированной, присваиваем ей
〃 значение, равное 30
if{empty($pnumber)) $рпшпЬег = 30;
〃 Если в строке запроса не передана страница,
// выводам первую страницу
if(empty($page))$page = 1;
$begin ^ ($page - 1)*$pnuwber;
// Запрашиваем информацию о $pnumber темах
$query - "SELECT * FROM themes
WHERE id_forum 匸 $id_forum AND hide != 'hide?
ORDER BY time DESC
LIMIT $begin, $pnumbern;
$thm = mysqlquery($query);
if ($thm)
// Начало таблицы с темами
?>
<table>
<tr>
<td<p> </p></td>
<td><p>Ha3B3HMe темы</p></td>
<tdxp>ABTop< /p></td>
<td><р>Последнее сообщение< /px/td>
</tr>
<?php
while ($ themes = mysql_fetch_array ($thm)) {
///////////////////////////////////////////У///////////////
// Блок вывода числа сообщений в теме /////////////////////////////////////////////////////////// // Подсчитываем количество сообщений в текущей теме, // результат помещаем в переменную $posts_in_topic $query = нSELECT COUNT(*) FROM posts
WHERE id_theme = " • $ themes [1 id_theme "J AND hide != ,hide,n;
$pst = mysql_query($query);
if ($pst) $posts__in_topic = mysql_result ($pstr Q); else puterror("Ошибка при подсчете количества сообщений темы..."); // Подсчитываем количество новых сообщений в текущей
// теме, результат помешаем в переменную $new_posts_in_topics
$query = нSELECT COUNT(*) FROM posts
WHERE id_theme=1"»$themes[1id_theme".”1 AND1$lasttime1<time ANDhide != 'hide'";
$tim = mysql_query ($query); if($tim)
{
i f (mysql_nuin_rows ($t im) > 0)
$ ne w_po s t s_in_t opics = mysql^result ($tim, 0); else $ new_po s t s_i n_topi cs = 0;
)else puterror("Ошибка при подсчете числа новых сообщений");
// Формируем стилевое оформление теь&1 и строку с числом новых и // общим числом сообщений в теме
if($new__posts_in__topics != 0)
{
// Если в системе имеются новые сообщения, приводам их
// в скобках
$theme_uount = u$posts_in_topic ($new_j>osts_in_topics) ”;
$theme_style = ** с 1 a s s =namenewt ema11;
}
else
<
// Если новых сообщений нет, просто приводам общее число
// сообщений в теме
$ theme_count = $posts_in__topic;
$theme_style = ^class^ametema**;
}
echo *f<tr><td><p $ theme_style><nobr>$theme_count</nobr></p></td>H;
///////////////////////////////////////////////////////////
// Блок вывода названия теьлы
///////////////////////////////////////////////////////////
// Предварительно обрабатываем угловые скобки и знаки абзаца
// Обрабатываем теги [b]f[/b],[i] и t/i]
$name ^ theme_wor k_up ($ themes [1 name1 ]);
if(isset($page)) $strpage - M&page-M.$page;
// Если тема закрыта # выводим предупрелщение
$closetheme ="”;
$closetitle = ”";
if($ themes[1hide1] = 1lock1)
{
$closetheme = "(тема закрыта)";
$closetitle - "Тема закрыта для обсуждения";
}
echo "<td>
<р $theme_style><a $theme_style $closetitle href=read.php?id_forum=n .$id__forum. Mfiid_theme=" . $ themes [' id__theme' ] . $strp age,M>$name $closetheme</a></p>
</td>”;
///////////////////////////////////////////////////////////
// Блок вывода автора теьлы
///////////////////////////////////////////////////////////
$author = html specialchars($themes[1author1]);
if($themes[1id_author」1= 0) { echo ”vtd>
<a
href=inf о.php?id_forum=n. $id_fomm» n&id_author=n. $themes [1 id_author1 ]."> $author</a></td>"; // Зарегистрированные авторы } else (
// Незарегистрированные авторы
echo ,,<tdxp class=author>$author</td>n; }
/////////////////////////////////////////////////////////// // Блок вывода последнего обновления темы
///////////////////////////////////г/////////////////////// echo "<td>". substr {$themes[ 'timeL 0,10) *
” в M.substr ($themes[ *time* ], 10, 6) .•*</td></tr>n;
/ / Конец табшщы по выводу тем форума }
///////////////////////////////////////////////////////////
〃 Блок вывода ссыпок на другие темы форума
///////////////////////////////////////////////////////////
〃 Запрашиваем информацию о количестве всех тем
$query = "SELECT COUNT(*) ГИСЖ themes
WHERE id_forum = $id_forum AND hide != fhideirt;
$tot = mysql_query ($query);
if (!tot) puterror ("Ошибка при выборке общего числа тем форума"); $total = mysql__result ($tot, 0);
$ number = (int) ($total/$pnumber);
if((float)($total/$pnumber) - $number != 0) $number++;
echo ”<trxtd cclspan=4 >Ссюбщения: 11;
for ($i n 1; $i<fe$nurnber; $i++) {
if($numb6r == $i) { if($page == $i) echo "[”・(($i - 1)*$pnumber + :U J-$total]”; else
echo "<а href^index.php?id__forum=n. $id_forum・ "&page二"• $i."> [”.(($i - irSpnumber + 1) . "-$total]</a>n;
else { if($page ==帛土) echo 〕"・(($土 - l)*$pnumber + 1) . rt*ir.$i*$pnumber/1]; else
echo 11 <a href^index.php?id_forum=M. $id_fortnn. ”&page=" • $i. ”> [M. (($i - 1) *$pnumber + 1) .n-,1*$i*$pnuniber.n]</a>M;
} } // Конец вывода ссылок на другие темы форума ?> </table> <?php
} else puterror ("Ошибка при выборке тем форума");
〃 Выводим завершение страницы
include "…/utils/bottomforum.php1';
// Помещаем страницу из буфера s переменную $buffer
$buffer = ob_get_contents();
/ / Очищаем буфер ob__end_clean ();
// Отправляем страницу клиенту echo $buffer;
?>
Как видно из листинга 20」,при выводе главной страницы используются функции управления выводом, работа с которыми описана в главе 4. Данные функции позволяют поместить весь вывод в окно браузера в буфер и отправлять страницу с сервера единым блоком, что позволяет увеличить скорость работы Web-приложения. Буферизация вывода инициализируется функцией ob_start(), которая сообщает интерпретатору PHP, что вывод в окно браузера следует задерживать и размешать во внутреннем буфере, содержимое которого затем при помощи функции ob_get_contents()помещается в переменную $buffer. После этого буфер очищается функцией ob__end_clean(), а переменная $buffer, содержащая HTML-код всей страницы, отправляется клиенту.
Для работы файла index.php требуется скрипт инициализации соединения с базой данных ——config.php, который уже рассматривался в предыдущих
Web-приложениях и файл utils.php, содержащий вспомогательные функции, которые будут рассмотрены ниже.
Файл index.php принимает два параметра:
□ id_forum —— первичный ключ записи таблицы forums, соответствующей текущему разделу форума;
口 page ——номер текущей страницы ——так как на одной странице убирается ограниченное число тем, все темы форума разбиваются на страницы (напрнмер, по 25 тем на страницу), переход к каждой из страниц можно осуществить при помощи ссылок, расположенных в нижней части главной страницы форума, имеющих вид: |1-25][26-50][51-75] и т. д.
После извлечения параметров из строки запроса происходит проверка корректности их значений. Так, если значение параметра id_forum не передано, ему присваивается единица. Затем из таблицы forums извлекается максимальное и минимальное значения первичного ключа id_forum. Если текущее значение не попадает в интервал между максимальным н минимальным значением, то ему присваивается значение, соответствующее максимальному или минимальному значению id_forum в таблице, в зависимости от того, по какую сторону интервала расположено текущее значение параметра.
В^ючаемый после этого при помоши функции require__once() файл topforum,php ответствен за вывод "шапки*' форума, состоящей из меню, приветственной фразы, переключателей между разделами форума и т. п. Кроме этого, в данном файле извлекается содержимое единственной записи таблицы settings, ответственной за глобальные настройки форума. Поля данной записи помещаются в ассоциативный массив $ settings.
:Замечание )
Более подробно файл topforum.phpt а также сопутствующие ему файлы будут рассмотрены ниже.
Переменная $pnumber получает значение, равное числу тем, выводимых на одной странице. Если по каким-то причинам переменная окажется не инициализированной, она примет значение по умолчанию, равное 30. Переменная $begin получает значение, равное номеру темы, начиная с которого следует выводить $pnumber тем. Эти две переменные необходимы для постраничного вывода тем на форуме.
После того как переменные $рпшпЬег и $begin инициализированы, к таблице themes осуществляется SQL-запрос select, выбирающий $pnumber не скрытых тем, начиная с позиции $ begin:
SELECT * FROM themes
WHERE id_form = $id_forum AND hide !^ 'hida'
ORDER BY time DESC
LIMIT $begin, $pnumber
Темы сортируются по полю time, в обратном порядке, т. е. темы» которые обновлялись недавно, оказываются вверху, а более старые темы — внизу. Если запрос выполнен успешно, то происходит формирование таблицы с темами, описанной в предыдущем разделе.
Для каждой из тем производится подсчет общего числа сообщений» а также числа новых сообщений* Новые сообщения вычисляются за счет сравнения времени сообщения с временем последнего посещения форума посетителем $iasttime. Значение $iasttime формируется в topforum.php, код которого представлен ниже.
(Замечая丝)
Для зарегистрированных посетителей новыми считаются сообщения, появившиеся с момента их последнего посещения форума, для незарегистрированных посетителей ^ новыми считаются сообщения появившиеся за последние два часа*
Обработка тегов [ь], [/b], [i] и [/i] осуществляется при помощи функции theme_work_up()из utils.php. Код функции представлен в листинге 202
Листинг 20.2. Функция theme work up О
〃〃/〃〃/〃〃〃〃〃〃〃〃〃/〃〃〃〃〃〃〃〃〃〃〃〃/ // Функция для обработки названий тем заменяет все // теги ([b]f [/blf [i] и [/i]) на их HTML-эквиваленты, ////////////////////////////////////////////////////// function theme_work_up($ theme) (
// Обрабатываем теги [b],[/b],[i] и [/i] $theme = html special chars ($ theme);
$ theme = str_replace (H [b] n, r,<b>1\ $ theme);
$theme 匸 str_replace(”[/b]”,”v/b>",$theme);
$ theme = str__replace (n [i] n, n<i>M, $ theme);
$ theme = str_replace (M [/i] '\ 11 </i>a f $ theme);
$ theme 二 st r_r eplace ("”",",", $ theme);
return $theme;
Внутренние теги в квадратных скобках заменяются их HTML- эквивалентами при помощи функции str_replace О ; кроме этого, функция
преобразует все обратные кавычки в прямые. Это необходимо, так как перед помещением текста в базу данных прямые кавычки преобразуются в обратные для того, чтобы избежать конфликта, вызванного тем, что все текстовые поля в SQL-запросе должны быть обралшены одинарными кавычками.
Содержимое файла topforum.php, расположенного в директории utils и несущего ответственность за вывод ^шапки" страницы, представлено в листинге 20.3.
<?php
// Извлекаем имя посетителя из cookie
$ си г гent_author = $_COOKIE[* current^authorf];
/ / Загружаем настройки форума из базы данных
$set 匸 mysql_query ("SELECT * FROM settings**);
if($set) $settings = mysql_fetch_array($set);
// Устанавливаем название форума * Если странице не будет
// передаваться название - будет подставляться это значение.
$titleall n $settings[fname_forum,];
if (!isset($title)) $title = $titleall;
// Выясняем название директории, где хранится текущий скйн форума $skin = н.,/skins/м.$settIngs[^kin*]* п/и;
// Выясняем дату последнего вхождения и выводим приветствие
if (! empty ($current_author))
{
settime($current_author,false);
$ query = 11 SELECT * FROM authors WHERE name = * $current_author1";
$ath = itLysql_query ($query);
if($ath)
{
$auth ^ mysql_fetch__array ($ath);
$lasttime = $auth[1last_time1];
J else puterror("Ошибка при выборке тем форума...");
} else $ cur rent_author = r, Посетитель11;
// Если время последнего пребывания посетителя не установлено,
// будут отмечены изменения за последние 2 часа
if (empty ($lasttime) ) Slasttiine = date (nY-rn.-d H: i: sM, time () 一 3600*2);
if (($showforumsline tI $ readforumline) &&
$settings[1show_forum_switch * 1 = 1 yes1) $shownewpost = true; else $shownewpost = false;
if($showforumsline && $settings['show_forum_switch1] == *yes,) $show_switch_forum 二 true;
else $ sho w_s wit ch_f о rum = false;
?>
<html>
<head>
<meta http-equiv^11 Cache-ControlT, content=Mno-cache, must-revalidateM>
<title><? echo $title ?></title>
<link rel=uStyleSheet" type-" text/css1' href^M<?php echo $skin; ?>forum.cssn>
<link rel=MStyleSheetM type*" text/css1' hrefn'yphp echo $skin; ?>mainstyles. cssn>
<?php
if (basename ($_SERVER [' PHP_SELF1 ]) ="read. php") {
?>
<link rel=nStyleSheetM t ypg" text/css*1 href=M<?php echo $skin; ?>read.csstt>
<?php
}
?>
</head>
<?php include $skin."diztop.php0; ?>
В начале файла извлекается имя посетителя из cookie, куда оно помещается при каждом обращении зарегистрированного посетителя к страницам форума. Код, несущий за это ответственность, рассматривается ниже.
Затем в ассоциативный массив $ settings помещаются глобальные настройки форума, извлекаемые из таблицы settings. Информация из данного ассоциативного массива используется далее во всем коде форума.
Переменная $skin играет большую роль в стилевом оформлении форума: именно она несет ответственность за путь, где расположены изображения и стилевые таблицы форума — изменение значения этого пути приводит к изменению дизайна форума.
( Замечанье )
По пути, расположенному в переменной $skint должна располагаться директория со строго определенным содержимым, которое описано ниже, в разд. 20*3.
После этого для текущего посетителя выясняется время последнего посещения форума, которое помещается в переменную $lasttime< Если в результате SQL-запроса переменная не получила значение, следовательно, данный посетитель не является зарегистрированным, и переменная $iasttime прн- нимает значение текущего времени за вычетом двух часов.
Завершает topfbrum.php включение файла diztop.php, который уникален для каждого из вариантов дизайна и будет рассмотрен ниже.
Добавление новых сообщений на форум осуществляется при помощи HTML-формы addthemeform.php, содержимое которой представлено в листинге 204 Внешний вид формы показан на рнс. 20.3.
凝琴翼弊隅版?鶴黨гу整寧■■課膜::器电:堂賜僦饗聚踰&寧阳鳄,準翩嘴曜騎就締.承淡澱価艷峨^7讀經破■押黜靜攫健
i Листинг 20.4. Файл addthemeform.php
<?php
// Устанавливаем соединение с базой данных
require_once £r,config *phpH);
// Извлекаем параметры из строки запроса
$id_forum = $_GET [,id_forumT];
$id_theme = $_GET [ * id_theme1 ];
?>
<a href=# onClick^1 history * back () 1 >Вернутьсж/а>
<form enctype=1 multipart/form-data * name=* form1 action^addtheme.php method=post>
Имя: 《input type=text name=author
value='<?php echo $_COOKIE[* current_author *]; ?>*><br>
Пароль: <input type=password name^pswrd
value=1<?php echo $_COOKHE[1wrdp *]; ?>'><br>
Тема: <input type=text name=theme value=,<?php echo $theme; ?>‘> <?php
include ".•/utils/answerform.phpn;
?>
</form>
Форма может принимать два параметра: id_fогшп — первичный ключ таблицы forums» соответствующий разделу форума, и id__theme ——первичный ключ таблицы themes, соответствующий теме форума. Ник посетителя и его пароль извлекаются из cookies, куда они помещаются при авторизации. В результате этого посетителю не требуется каждый раз вводить свое имя и пароль. Так как одни и те же элементы управления формы используются для добавления темы, сообщения и для редактирования сообщения, они вынесены в отдельный файл answerform.php, содержимое которого представлено в листинге 20.5.
#?号■齡繼蟹岁¥¥^^^邑侬餐堂里跟:Е*,■"**"; .
Листинг 20.5, Файл answerform.php
学露 JS^^SSSUS总卞1s fA;*・毎!. j •「..,.「
Сообщение: <textarea cols=76 rows-10 name^essage><?php echo $message; ?> </textarea><br>
URL: <input type^text name=url size=74 valuec=<?php echo $url; ?»<br>
Прикрепить: <inputПрикрепить: <inputvinput <input 《input <inputvinput <input 《input <inputtype»submit type=hidden type=hidden type=hiddentype»submit type=hidden type=hidden type=hiddentype=file name=attach><br>name&send va1ие«Отправить >name^id_theme value«<? echo $ id_theme; ?» name=id_pGst value=<? echo $id_post; ?» name=id_forum value=<? echo $id_forum; ?»type=file name=attach><br>name&send va1ие«Отправить >name^id_theme value«<? echo $ id_theme; ?» name=id_pGst value=<? echo $id_post; ?» name=id_forum value=<? echo $id_forum; ?»
Прикрепить: <input
Прикрепить: <input
vinput <input 《input <input
vinput <input 《input <input
type»submit type=hidden type=hidden type=hidden
type»submit type=hidden type=hidden type=hidden
type=file name=attach><br>
name&send va1ие«Отправить >
name^id_theme value«<? echo $ id_theme; ?» name=id_pGst value=<? echo $id_post; ?» name=id_forum value=<? echo $id_forum; ?»
type=file name=attach><br>
name&send va1ие«Отправить >
name^id_theme value«<? echo $ id_theme; ?» name=id_pGst value=<? echo $id_post; ?» name=id_forum value=<? echo $id_forum; ?»
Информация из HTML-формы поступает обработчику, который расположен
в файле addtheme.php (листинг 20.6).
<?php
///////////////////////////////////////////////////////////
// Блок подготовки
/////////////////////////////////////////////////////////// // Устанавливаем соединение с базой данных
require__once (nconf ig. phpM);
// Подключаем библиотеку форугла
require_once("../utils/utils.php");
// Извлекаем значения, переданные методом POST из
// суперглобального массива $_POST
$author = trim($_POST['author *]);
$pswrd = $_PQST[Tpswrd*];
^message ^ trim($_POST[ "message* ]);
$ theme = trim($_POST [ * theme']);
$url = trim($_POST[,url*J); if (eirpty($url)) $url =匸”; $id_j>ost = $_POST[,id_post,]; $id_theme = $_POST [1 id__theme1 ]; $id_forum = $_P0ST [1 id_forum* ];
// Проверим корректность ввода имени посетителя и текста сообщения if (empty ($theme)) links__forum($id_forumr иНе указана тема сообщенияТ|); if(empty($author)) links_forum($id_forum, "Неправильно указано имя"); if (empty ($message)) 1 inks_forum($id_forum, 11 Сообщение не введено11); /////////////////////////////////////////////////////////// // Блок идентификации /////////////////////////////////////////////////////////// // По умолчанию считаем, что посетитель не зарегистрирован $±d_author « 0;
// Если идентификация проходит успешно, входам на форум, // а переменной $id_author присваиваем значение первичного // ключа записи таблицы authors
$query ^ "SELECT * FROM authors WHERE name = fauthor'";
$ath = mysql_query ($query); if($ath)
{
// Если имеется запись, следовательно, посетитель зарегистрирован // и необходимо сверить пароли if (mysql_num_rоws ($ath) >0) {
$auth ^ mysql_fetch_array ($ath);
// Если имя пользователя существует, но пароль не верен, // прекращаем работу с базой
if(!empty($auth[f name *]) && $auth[^assw1] != $pswrd) links_f orvunf $id_f orum,
° Ошибка идентификации, попробуйте еще раз1'); else
{
// Заодно осуществляем вход на форум этого пользователя setallcookie{$auth[*name,],$auth「passw1]);
}
$id author = $auth[* id author1];
} else puterror (мОшибочный запрос к таблице авторов'1);
〃〃/〃〃/〃〃////〃〃/〃〃〃〃〃〃〃〃/〃〃〃//〃〃/〃//〃
// Блок добавления новой темы
///////////////////////////////////////////////////////////
// Формируем SQL-эапрос на добавление темы
$query_theme = "INSERT INTO themes VALUES(
MULL,
,$theme',
^author1,
$id_author, 'show1, NOW(), $id_forum)";
if (! mysql_query ($query_theme))
{
echo $query_theme;
puterror("Ошибка при добавлении новой темы"};
}
Н Выясняем первичный ключ только что добавленной записи
// Это понадобится для добавления сообщения и файла
$id_theme = mysql_insert_id (};
// Выбираем настройки
$set = mysql__query ("SELECT * FRCK4 settings*');
if($set) $settings = mysql_fetch_array($set);
///////////////////////////////////////////////////////////
// Блок загрузки файла на сервер
///////////////////////////////////////////////////////////
$path = 0;
// Если поле выбора файла (рисунка) не пустое, // закачиваем его на сервер и переиме но выв аем if {! empty ($_FILES [' attach1 ] [1 W_name1 ]) &&
$_FILES「attach」「иф_пал1е1 ] !=“-“)
(
// Проверяем, не больше ли файл 512 Кб
if($_FILES[attachт][Tsize*] > ^settings[1size_file *]) links_forum($id^forum, ''Слишком большей файл (более 512 Kb} *f);
// Проверяем, не является ли файл скриптом на PHP, Perl или HTML, // если это так, преобразуем его в формат TXT
$ extent ions = array (м .php", ".phtml*\ n.php3n, M,html"f n.htmIT, f\pl'4 ;
// Извлекаем из имени файла расширение
$ext = strrchr($_FILES[attach*][^ame1],"・");
// Формируем путь к файлу if(in_array($ext, $extentions))
$path=Hfiles/$id_theme-M.date(nYmdHis",time())・".txt"; else
$path="files/$id_theme-H.date("YmdHis",time()}.$ext;
// Перемещаем файл из временной директории сервера // в директорию /files Web-приложения
if (copy ($_FILES [1 attach * 1 [f tn^)_name1 ] # $path)) {
// Уничтожаем файл во временной директории unlink ($_FILES「attach' ] U tmp name']);
// Изменяем права доступа к файлу chmod($path, 0644);
}
/////////////////////////////////////////////////////////// // Блок формирования и выполнения SQL-запроса
/////////////////////////////////////////////////////////// // В текстовых переменных заменяем одинарные кавычки обратными $message = str_replace (n 1 , ”'", ^message);
$ theme = str__replace (” * ”,”、",$ theme);
$author ^ str_replace(H1M, ”、”, $author);
// Формируем SQL-запрос на добавление сообщения $query_post = "INSERT INTO posts VALDES(
NULL,
*$messagef, 屮ш:1',
'$path I
'$author *, $id_author, 1 show*, NOW(), 0, $id_them宅"';
// Обновляем время последнего посещения автором и число // оставленных им сообщений
$query__author = "UPDATE authors
SET time = NOW(), themes = themes + 1 WHERE id_author = $id_author";
// Выполняем SQL-запросы
if(mysql_query($query_j)ost) E& mysql_query($query_author))
$thm = "Нова貝 тема на форуме ". $settings [1 name^forum1 ] .r, - $theme"; $msg = "Название теьзы: M.$theme.
н\пАвтор : ".$author.
"\пТекст сообщения : н.$message;
// Изменяем кодировку
$thm = convert_cyr_string(stripslashes($thm),,》,,,k,);
帛msg = convert_cyr_string{stripslashes($msg), *w', 'k1);
// Если на странице администрирования указан
// адрес отсылки сообщения - отправляем письмо
if ($set tings [ * sendjitail' ] = "yes"} mail($settings[*email*]r $thm, $msg);
// Отправляем письма, посетителям, которые захотели
// получать уведомление о новых темах
$query = "SELECT email FROM authors WHERE sendmail = 'yes'";
$sdm = mysql_query ($query);
if (!$sdm) puterror (**Ошибка при отправке уведомлений1*);
if (mysql_num_rows {$sdm) >0)
{
while ($sendemail = mysql_fetch_array ($sdm))
mail ($sendemail [1 email • ], $thm, $msg);
}
echo "<HTMLXHEAD>
<META HTTP-EQUIV=1Refresh1 CONTENT^*0; URL=index.php?id_forum=$id_forum,> </HEADX/HTML>M;
} else puterror("Ошибка при добавлении темы"); ?>
Значения полей HTML-формы передаются в обработчик методом post, при ЭТОМ обязательными ЯВЛЯЮТСЯ только $ theme, Sauthor И $message, которые Те должны быть пустыми. В этих переменных хранятся название темы, имя автора и текст сообщения соответственно.
Как видно из листинга 20.6, сразу после этого расположен блок идентификации, проверяющий соответствие имени паролю. В начале проверяется наличие имени в таблице authors. Если имя, введенное посетителем, не обнаружено, считается, что пользователь выступает от имени незарегистрированного посетителя.
( Замечание )
Имена, используемые незарегистрированными посетителями, часто называют "серыми никами". На ряде форумов вводятся ограничения для "серых ников'\ налример+ им разрешается оставлять сообщения в уже существующих темах, но запрещается инициировать собственные темы и т, п.
Если в таблице authors введенное посетителем имя уже присутствует, проверяется соответствие введенного посетителем пароля с паролем из таблицы authors. При их совпадении происходит дальнейшая обработка и добавление темы в базу данных, в противном случае скрипт прекращает свое выполнение. Переменная $id_author принимает значение 0 для незарегистрированного посетителя и значение, равное первичному ключу таблицы authors, для зарегистрированного посетителя.
Если авторизация прошла успешно, происходит автоматический вход на форум. Это достигается передачей имени посетителя и его пароля функции setallcooRiet) из utils/utils.php. Код функции представлен в листинге 20.7.
Листинг 20*7. Функция setallcookie ()
// Эта функция устанавливает cookies, необходимые для
// авторизации (входа на форум)
function setallcookie($authorr $wrdp)
$set = mysql_query("SELECT * FRM settings");
if($set) ^settings = mysql_fetch_array($set);
$tmppos = 31 rxpos ($_SERVER [1 PHP_SELF1 L " / 0) + 1;
$ path 二 substr($_SERVER「PHP—SELE1], 0, $ tmppos);
setcookie("current_authorH,
$author,
time() + 360Q*24*$settxngs[^ooktime1],
$path);
setcookie("wrdp1', $wrdp, time() + 3600*24*^settings[*cooktime1],$path);
Функция устанавливает имя автора и его пароль в cookies на количество дней, определяемых полем cooktime таблицы settings.
Сразу после блока идентификации следует блок добавления новой темы. SQL-запрос на добавление новой темы формируется во временной переменной $query__theme, которая передается в качестве параметра функции mysqi query {). В случае успешного выполнения запроса при помощи функции mysql_insert__id {) выясняется номер первичного ключа только что добавленной записи, который присваивается переменной $id_thame.
За блоком добавления новой темы следует блок загрузки файла на сервер, который практически ничем не отличается от аналогичных блоков в других приложениях, за исключением проверки расширения. Если расширение передаваемого файла совпадает с одним из следующих расширений: php, phtml, php3, html, htm, pl, то оно заменяется txt. Это необходимо для того, чтобы избежать исполнения вредоносного кода при добавлении в качестве прикрепляемого файла скрипта.
После этого добавляется текстовое сообщение. Перед добавлением во всех текстовых сообщениях одинарные кавычки преобразуются в обратные. SQL* запрос во временной переменной $query_post использует ранее полученные первичные ключи $id_author и $id_theme для посетителя и темы соответственно. SQL-оператор update в переменной $query_author предназначен для увеличения счетчика theme в таблице authors для посетителя, добавившего новую тему.
Перед тем как осуществить автоматический переход на главную страницу форума, отправляются почтовые уведомления о создании новой темы на адреса, указанных в поле email таблиц settings и authors. В случае таблицы authors отправка уведомления происходит лишь в том случае, если поле sendmaii принимает значение 'yes'.
Регистрация посетителя осуществляется при помоши HTML-формы registerfornLphp, внешний вид которой приведен на рис. 20.4. Содержимое данного файла представлено в листинге 20.8.
?联盟%出笈” НЕТ""戈Г•工史营罡声宀許К聲潜片幾写飞¥三券弾绻胱■芍曙■処""Уф*妙:旭r"里,¥ь》Л咚5岳上■證您号罩出承端時■, 举芻総舞部内豈尹男
Листинг 20.8. Файл registerform.php
**H*****“.*$2W*J**.;VW,'K、q^V.*W:;*.*К:/.*.;,『.、,..;、■*;*i*;.*、'**',.Gii^в'■ ■ v■♦**“**;*.**%*“',*;;* vV'A*hVtf*tf■'/**■*■'■ «*4 ti*■ *'■ *■*■■««rf*1 *f *'■ i X',:**'■'■ .;*¥・■■ вЧ%*!■ ■«■'kb«■*>t|fclt''»eV#Vw**fciHp#fc<ft1<#'3ii
<?php
// Устанавливаем соединение с базой данных require_once("config.php");
// Устанавливаем название страницы
$ names ct i on=T, Регистрация на форуме";
// Выводам "шапку1Т страницу
require once t11.. /utils/topforumaction.php,f);
if (! isset ($action)) $ act ion = 11 register. php'1;
if (! isset ($button)) $button = "Зарегистрировать11;
$id^_fornin = $_GET (' id—fCEwP ];
?>
<a href «index. php?id_f огхлп=<?= $id__forum ?»Вернуться к списку тем</а>
<form enctype= Multipart/form-data1 action=<?php echo $action; ?> method>=post>
Имя:* <input type=text name=author value=<?php echo $name; ?»<br>
Пароль:* <input tуретра ssword name=pswrd>
Повтор пароля: * <input type=password name^swrd_aga inxbr>
<?php
requiredonee (11. . /utils/card .phpn); ?>
Так же как и в листинге 20.4, управляющие элементы формы, которые могут быть использованы и при регистрации, и при редактировании регистрационных данных, вынесены в отдельный файл card.php, содержимое которого представлено в листинге 209
A ] ;*f; Avao 7.3、;》***諏4扁输詰源…;言-+,、, +" ……蓊**,***;, “*±轟,4^雪П "盘芍二 hЗТ***;2;r:;;^*“;;т『С;<;*; ;..«a.ta ; . I e-mil: < input type^text name=einail value=<?php echo $ email; ?»<br>
ICQ: <input type=text name=icQ value=<?php echo $icq; ?»<br>
URL: <input type^text name=url value=<?php echo $url; ?»<br>
О себе: <textarea со1з=^7б rows=3 name=about>
<?php echo $about; ?></textarea><br>
<input type=McheckboxH name="subscribe" v?php echo $subscribe; ?»
Получать сообщения о новых темах на почту<Ьг>
Фото: <input type=file name=photo>(не более 500 Кб)<br>
<input class=button typ—submit name=send V31u2=‘v?php echo $button; ?>f>
Данные из формы registerfbrm.php передаются обработчику register.plip, код которого представлен в листинге 20.10.
:宙,q W,f ■; im*[卄,:*,*",吃*"岑¥ ■ uft»,"" >*:■*: пу格;2勺*/f吃空中■京罡却土?eA2.*"已费*户*k賞能f黑,'第^,限f "一ир:小干"*:*,駕胃总m*g^^wT"'11Vl'、W '■
;Листинг 20.10. Файл register.php;
<?php
///////////////////////////////////////////////////////////
// Блок подготовки и проверки
///////////////////////////////////////////////////////////
// Устанавливаем соединение с базой данных
require once《11 config. php");
// Подключаем библиотеку форума require_once (" •・ /utils/utils.php1');
// Получаем данные, отправленные методом POST
$anthor = trim(?_POST「author1]);
$pswrd = $_POST[*pswrd1];
$pswrd__again = $_POST[ fpswrd_again* ];
$ email = trim($__POST[ * email* ]);
$icq = trim($_POST[,icq,J);
$about = trim ($_POST[1 about11);
$subscrik>e 二 $_POST [1 subscribe,];
// Проверяем правильность ввода данных
if (entity ($author)) links_forLim($id_forumr nHe указано имя");
if (^npty ($pswrd) I [
empty($pswrd_again) I I
$pswrd != $pswrd_again) links^forum($id_forum, "Ошибка в паролях; if(1empty($email)) (
if (!p《egjnatch(”/[0-9a-z_]+Q[0-9a-z_A\J+\・ 【&-z](2,3}/土"/ $email))
1 inks_forum f $ iJforum,
^Введите e-mail в виде <i>something@server .com</i>,T); I
// Проверяем, нужно ли отправлять письмо автору при добавлении // новой темы iff'empty($email)) {
if($subscribe == °onиI $subscribe = * yes *;
else $subscribe = *n。';
}
///////////////////////////////////////////////////////////
// Блок проверки регистрации имени
〃〃〃〃〃〃〃/〃〃〃/〃〃〃〃〃〃〃/〃〃〃〃〃〃〃/〃〃〃/
// Выясняем, не зарегистрировано ли уже это имя
// Выясняем, не создается ли новое имя для дискредитации
// Возможны три ситуации, которые необходимо предотвратить:
// 1. Вводится ник, полностью совпадающий с уже существующим
// 2. Вводится уже существующий кирилличеckw ник, в котором
// одна или несколько букв заменены на латинские
// 3 * Вводится уже сущеетвующий латинский ник, в котором
// одна или несколько букв эаменениы на кириллические
// Массив кириллических букв $rus =array(,rAHr"а","В”,"Е",При"с""с"“т”.// Массив латинских букв$eng =array(ПАИ,“а\“В","Е”,"pи"с"'七"”T"// Массив кириллических букв $rus =array(,rAHr"а","В”,"Е",При"с""с"“т”.// Массив латинских букв$eng =array(ПАИ,“а\“В","Е”,"pи"с"'七"”T"He", "K". "M”,"H", "О", 'О "P"He", "K". "M”,"H", "О", 'О "P"// Заменяем $eng_author // Заменяем $rus author// Заменяем $eng_author // Заменяем $rus authorкириллическиe буквы латинскими= str_replace($rusf $eng, $author); латинские буквы кириллическими= str_replace($eng, $rus, $author);кириллическиe буквы латинскими= str_replace($rusf $eng, $author); латинские буквы кириллическими= str_replace($eng, $rus, $author);// Формируем SQL-manpoc$query = MSELECT * FROM authors// Формируем SQL-manpoc$query = MSELECT * FROM authorsWHERE name LIKE пйгпв LIKEWHERE name LIKE пйгпв LIKEname LIKEname LIKE*$author* OR•$eng_author1 OR ,$rus author111;*$author* OR•$eng_author1 OR ,$rus author111;
// Массив кириллических букв $rus =array(,rAHr"а","В”,"Е",
При"с""с"“т”.
// Массив латинских букв
$eng =array(ПАИ,“а\“В","Е”,
"pи"с"'七"”T"
// Массив кириллических букв $rus =array(,rAHr"а","В”,"Е",
При"с""с"“т”.
// Массив латинских букв
$eng =array(ПАИ,“а\“В","Е”,
"pи"с"'七"”T"
He", "K". "M”,"H", "О", 'О "P"
He", "K". "M”,"H", "О", 'О "P"
// Заменяем $eng_author // Заменяем $rus author
// Заменяем $eng_author // Заменяем $rus author
кириллическиe буквы латинскими
= str_replace($rusf $eng, $author); латинские буквы кириллическими
= str_replace($eng, $rus, $author);
кириллическиe буквы латинскими
= str_replace($rusf $eng, $author); латинские буквы кириллическими
= str_replace($eng, $rus, $author);
// Формируем SQL-manpoc
$query = MSELECT * FROM authors
// Формируем SQL-manpoc
$query = MSELECT * FROM authors
WHERE name LIKE пйгпв LIKE
WHERE name LIKE пйгпв LIKE
name LIKE
name LIKE
*$author* OR
•$eng_author1 OR ,$rus author111;
*$author* OR
•$eng_author1 OR ,$rus author111;
$ath = mysql_query($query); if($ath)
if (mysql_nurn_rows ($ath) >0) links__foruin ($id_forum,
"К сожалению, данное имя уже зарегистрировано. Попробуйте другое *");
} else puterror(^Ошибка при регистрации нового посетителя");
///////////////////////////////////////////////////////////
// Блок загрузки файла на сервер
/////////////////////////////////////////////////////////// $url_photo ="”;
// Если поле выбора фотографии не пустое, / / закачиваем ее на сервер и переименовыв а ем if (! empty ($_FILES [1 photo Ml* tmp_name1 ]) &&
$_FILES[rphoto1 ] (1 tn^__name• ] !="-”) {
// Проверяем, не больше ли файл 512 Кб
$set = mysql_query ("SELECT * from settings; n);
if($set) ^settings = mysql_fetch_array($set);
if($__FILES['photo *][* size1] > $settings[1size^photo1]) links ($id_forLini,
$id_theme,
nСлишком большая фотография (файл более 512 КЬ)");
// Извлекаем из имени файла расширение
$ext 二 strrchr($_FILES['photo■]['name,],"・”);
// Разрешаем загружать файлы только определенного формата
$ extent ions = array (м. jpg",”. gifT,);
// Формируем путь к файлу if(in_array($ext/ $extentions)) {
Spath="phQto/".date("YmdHis",time{)).$ext;
}
// Перемещаем файл из временной директории сервера
// в директорию /photo Web-приложения
if (copy ($__FILES [1 photo1 ] [ * tnp_name1 ], $p&th))
(
// Уничтожаем файл во временной директории
unlink ($_F工LES [1 photo,]「tmp_name']);
/ / Изменяем права доступа к файлу chmod($path, 0644);
$url_photo = $path;
}
}
///////////////////////////////////////////////////////////
// Блок формирования и выполнения SQL-запроса
///////////////////////////////////////////////////////////
// Проверяем, нет ли одинарных кавычек
$auth0r = str_replace(",,,l 心”,$author);
$ about = str_r eplace О "、”,$ about);
// Формируем запрос SQL-оператора INSERT для
// добавления нового зарегистрированного посетителя
$query_author = мINSERT INTO authors VALUES(
NULL,
,$author I r$pswrdf, ^email* r '$subscribe I furl、 '$icql '$about I
^urljhoto1, NOW (),
NOW。,
Q, 0)";
if (mysql_query ($query_author))
// Осуществляем автематический вход посетителя на форум 〃 путем установки cookies setallcookie($author, $pswrd);
// Обновляем дату последнего вхождения settime($author,false);
// Осуществляем а в тематический переход на главную страницу форума echo H<HTML><HEAD>
<МЕТА HTTP-EQUIV« * Refresh1 CONTENT^*Q;
□RL-index. php?id_foruIll^$id_foruIn^ >
</HEADX/HTML>";
)else puterror ("Ошибка при регистрации нового посетителятт); ?>
После проверки правильности ввода данных осуществляется проверка уни- кальностн вводимого пользователем нменм, т. е. проверяется, что такое имя отсутствует в базе данных. Кроме того, при проверке имени следует учитывать, что оно может быть похожим на уже существующее. В последнее время на форумах появилась тенденция подделки имен с целью дискредитации посетителя. Подделка производится путем замены в нике пользователя ан・ гийских букв сходными по начертанию русскими буквамн или наоборот. Для решения проблемы в скрипте формируются два массива: $rus и $eng, содержащие кириллицу и латиницу, сходные по написанию. Так как ники обычно состоят полностью из кириллических или полностью мз латинских букв, проверяются три варианта, подтверждение каждого из которых при во* дит к прекращению регистрации:
口 имя нового посетителя полностью совпадает с имeнe^f из таблицы authors;
口 имя нового посетителя совпадает с одним из имен в таблице authors после замены всех латинских букв в имени кириллическими;
□ имя новго посетителя совпадает с одним из имен в таблице authors после замены всех кириллических букв в имени латинскими.
Если ни одно из условий не выполняется, новое имя посетителя уникально, и оно добавляется в таблицу authors. В случае успешного выполнения SQL- запроса осуществляется автоматический вход на форум при помощи рассмотренной в листинге 20.7 функции setallcookie о. Кроме этого, для
вновь зарегистрированного посетителя устанавливается время последнего посещения, равное текущему моменту. Это осуществляется при помощи функции settime О из utils.php. Код функции приведен в листинге 20Л1. Функция принимает два параметра: $author — имя посетителя и $enter —— флаг, значение true которого приводит к обязательному обновлению.
警憲察難瞿編鋼解■總陶储鹤蹄齡;
;Листинг 20.11. функция settimeO
,心冷 f^^M?XS^^i:W*M;i^??^iU: : . :«“ •ЯТЖул'mЯй 7?•.曰КТ房臂•5йУ!断iWk 出ii¥续需淋Ш;К隘詔沁:•■霁界北府・事??吊/Г?匕'靠 &;■f& .口;..“. :;;.;*:
// Эта функция производит обновление времени последнего посещения
// посетителя. При каждом просмотре страницы форума происходит
// обновление времени последнего посещения текущего посетителя, при этом // текущее время time сравнивается с предпоследним значением last_time — 〃 если оно больше 30 минутr last_time присваиваем старое значение time, // a time присваиваем текущее время.
// Sauthor - имя пользователя
// $enter - если true - принудательное обновление времени предпоследнего // посещения
function settime ($author, Renter)
{
$ath = mysql_query (иSELECT UNIX^TIMESTAMP (time)
FRM authors WHERE name =,^author1 H);
if{$ath)
{
$authr = mysql_fetch_array($ath);
$ter呼time = (int) $authr「UNIX_TIMESTAMP (time) * ];
// Если с момента последнего посещения прошло больше 20 минут
if ((time () - $ten^>time)/6020 | I Center) I
// Устанавливаем новое время
$query_update = "UPDATE authors
SET last_time=1" .date ("Y*-m-d Hr i:s*\ $temptime)."1 WHERE name = 1 $ author'; '*;
if (lmysql_query ($query__update)) puterror("Ошибка при обновлении времени {lasttime) *・•”);
//Ив любом случае обновляем базу данных
$queгy_updatе = "UPDATE authors SET time = NOW()
WHERE name = ^author* **;
if (!mysql_query ($query_update))
puterror("Ошибка прм обновлении времени (time! .*.");
} else puterror(^Ошибка при обращении к таблице авторов ...");
Представленная в листинге 20.11 функция сравнивает два поля time и last_tlme записи таблицы authors. Поле time хранит время последнего посещения, a last^tiine — предыдущего. Поэтому поле time обновляется в любом случае при каждом обращении к функции. Поле last time обновляемся лишь в том случае, если с момента последнего обращения прошло более 20 минут- При этом оно получает значение, равное предыдущему значению time. Такой механизм необходим для того, чтобы в течение одного сеанса просмотра (сопровождающегося активным просмотром страниц и ответом на сообщения), посетитель видел все новые сообщения форума. После прекращения сеанса, т. е. отсутствия активности посетителя более 20 минут ему будет предоставлена информация о новых сообщениях, появившихся с момента его последнего обращения к форуму.
〔 Заменан^е )
Файлы updateform.php и update.php, при помощи которых осуществляется редактирование регистрационных данных, во многом сходны с рассмотренным выше registerform.php и register.php, поэтому приводиться здесь не будут. Их код можно посмотреть на компакт-диске, прилагаемом к книге, или по адресу http://www.softtime.ru/info/downloads.php.
После того как посетитель зарегистрировался, он может осуществлять процедуру авторизации, т. е. входа на форум. Для авторизации используется форма enterfbrm.php, включающая два поля (для имени и пароля) и одну кнопку. Содержимое файла enterfbnn.php представлено в листинге 20.12.
浊曰出2。廿無空 enterf吧:胆京—謚,„姦譎„
<?php
// Устанавливаем соеданение с базой данных require_once{°config.php");
// Устанавливаем управляющие переменные HTML-формы
$nameaction 二 "Вход на форум (авторизация)11;
$action — "enter.php";
$auth = $_C8K工E [ *current_author1 ];
$pass = $_COOKIE U wrdp,);
$id_forum = $_GET [ * id^forum* ];
if(!isset($title)) $title = "Вко口”;
if (! isset ($button)) $button = 11 Войти11;
if (empty ($ return)) $ return = $_SERVER [ ,,HTTP_REFERERtl ];
// Выводим "шапку** страницы
include ” ・./ut i 1 s / topf orumact i on. php11;
?>
<p><a href33*<? echo $return ?>*>Вернуться в форум</а></р>
<form action=<?php echo Section; ?> method=spost>
Имя: <input type=text name=author value=,<?php echo $auth; ?>rxbr>
Пароль: <input type=password name=pswrd value=*<?php echo $pass; ?> * ><br>
<input type^submit value=<?php echo $button; ?»
<input type=hidden name=return value-'<?php echo $retum ?〉‘>
<input type«hidden name=id_forum value=*<?php echo $id_forum; ?>*>
</form>
<?php
include " . • /utils/bottomforumaction. php11;
?>
После заполнения полей и нажатия кнопки Войти данные из полей формы отправляются обработчику enter.php, код которого приведен в листинге 20.13.
;Листинг 20.13. Файл enter.php
Php
// Устанавливаем соединение с базой данных require_once(мconfig.php");
// Подключаем библиотеку форума
recjuire_once (11,. /utils/utils.phprt);
// Получаем переменные, переданные методом POST
$author = $_POST['author'];
$pswrd = $_POST(*pswrd*];
$id_forum ^ $_POST [1 i Jforum'];
// Проверяем правильность ввода пароля
$query = "SELECT * FROM authors
WHERE name 匸 1$authorfH;
$ath = mysql_querу($ query);
if($ath)
(
$auth = mysql_fetch_array ($ath);
if($auth[1passw1] !三 $pswrd)
include ”••/utils/topnull.php";
echo n<p clasз=Ьид>0шибка идентификацииr попробуйте еще раз*</р>"; echo "<р><а href=enterform*php?id__forum=$id_fогиш>Вход</аХ/р>и; exit ();
) else (
I/ Устанавливаем в cookie имя автора и его пароль setallcookie($auth['name*], $auth[^assw*]);
// Обновляем дату последнего вхождения
settime ($auth[ ^ame1 ], true);
// Осуще ст в л нем автоматический возврат обратно
if (en^ty ($retum)) $ return = "index * php?id_f oruIп^$ id_f orumM;
echo H<HTMLXHEAD>
<META HTTP-EQUIV=1 Refresh1 CONTENT=*Q; URL=$return,>
</HEAD></HTML>H;
}
} else puterror("Ошибка при обращении к таблице авторов ..."); ?>
Скрипт сравнивает пароль, введенный пользователем, с паролем, помещенным в базу данных, и если они совпадают, производится авторизация — при помощи рассмотренной в листинге 207 функции setallcookie () в cookie устанавливаются имя пользователя и его пароль, а при помощи рассмотренной в листинге 20.11 функции settime () обновляется время последнего посещения.
Выход с форума осуществляется за счет уничтожения cookie с именем посетителя и его пароля — за это несет ответственность скрипт exit.php, код которого приведен в листинге 20.14.
<?php
// Устанавливаем соединение с базой данных require_once(uconfig*php");
〃 Подключаем библиотеку форума require_once(м* */utils/utils.php**);
// Очищаем cookie
cleanallcookie £);
25 Зак, 1181
// Осуществляем автоматический переход назад
echo **<HTMLXHEAD>
<МЕТА HTTP-EQUIV='Refresh' CONTENT=,0; URL=".$_SERVER["HTTP_REFERERM J J'> </HEADX/HTML>M;
// Выводим сообщение о том. что пароль посетителя заОыт include ”. . /utils/topnull .php**;
echo "<р>Логин и пароль забыты.</р>н;
echo "<р><а href=index»php?id_foruirp=$id_fогит>Вернуться к списку тем</ах/р>н;
?>
Код exit.php сводится к вызову функции cieanaiicookieO из utils/utils.php с последующим возвратом в точку вызова, Такой эффект достигается за счет передачи в качестве URL реферера, извлекаемого из элемента суперглобального массива $_server「http_referer”]. Код функции cieanaiicookieO приведен в листинге 20.15.
function cleanallcookie《)
{
$tmppos = strrpos($_SERVER[1PHP_SELFf]ru/n) + 1;
$path = substr ($__SERVER[1 PHP^SELF1 ], 0, Stmppos);
setcookie (r,current_author°,0f $path);
setcookie("wrdpH, ”", 0, $path);
Уничтожение cookie осуществляется за счет установки времени жизни cookie, равного 0 секунд.
Далее будет рассмотрен файл read.php, который несет ответственность за представление отдельной темы. Содержимое файла представлено в листинге 20.16. Для отображения темы форума файл read.php принимает два параметра:
□ id_foruin — первичный ключ раздела форума;
口 ld_theme —— первичный ключ темы форума.
;Листинг 20.16. Файл read.php к'"....;*......:. ..—;к. .;;...;...*“. ;;『.;£;.,.,
<?php
// Помещаем все в буфер ob start();
// Устанавливаем соединение с базой данных require_once("config.php");
〃 Подключаем библиотеку форума require_once<"・・/utils/utils ・php”);
// Извлекаем параметры из строки запроса $id_forum = $_GET [f id_forum,];
$id_theme = §_GET[1idtheme,];
Spage = $_GET( градеЧ;
// Извлекаем имя посетителя из cookie $current_author = $_COOKIE[1current_authorr]; // Извлекаем вид форума из cookie $lineforum = $_COOK工E [ ^ineforum* ];
$ line forumdown = $_C8K 工 E [1 linefortundown * ];
// Обновляем дату последнего вхождения if(!empty($current_author))
{
settime($current_author,false);
$query = "SELECT ★ FROM authors WHERE name ^ '$current_author'”;
$ath = mysql_query ($query);
if($ath)
{
$anth = mysql_fetch_array ($ath);
$ last time = $auth [ * last_time1 ];
}
// Выводим название темы
$theme_prev ="";
$theme_next =
$query = "SELECT * FROM themes
WHERE id_theme = $id_theme AND hide ! =,hidH
$thm = mysql_query ($query);
if (!$thro) puterror(^Ошибка при обращении к теме: $id_theme ,.."); if (mysql_nwn_rows ($thm) >0)
{
$themes = mysql_fetch_array ($thm);
/ / Осуществляем запрос на выявление предыдущей темы
Squery = "SELECT * FROM themes
WHERE time > ," . $themes [1 time1 ] *111 AND
id_forum = $id_forum AND hide !工'hide' ORDER BY time LIMIT lw;
$thmprv = mysql_query ($query);
if ($thn^»rv) $theme_prev = mysql_fetch_array ($thn^>rv); else puterror($query);
// Осуществляем запрос на выявление последующей темы
$query = "SELECT * FROM themes
WHERE time < '"・ $ themes [1 time '].'*' AND
id_fomm ® $id_forum AND hide !^ 'hide *
ORDER BY time DESC
LIMIT Iй;
$thmnxt = mysqlquery($query);
if ($thmnxt) $theme_next = mysql_fetch_array ($thmnxt);
else puterror($query);
// Предварительно обрабатываем скобки и знаки абзаца в названиях тем // Обрабатываем теги [b],[/b],[i] и [/1]
$ then班 = theme__wor k_up ($ themes [1 name * ]);
$theme_prev[1 name“ ^ theme_work_up($theme_prev[1 name1 ]); $theme_next [ * name,] = theme_work_up ($theme_next [1 name * ]); }
// Если тема закрыта - вывода сообщение об ошибке else puterror ("Ошибка при обращении к теме: $id_themer,);
II Отображаем блок переключения между 11 линейным" и
// "структурным" форумами $show_switch = true;
// Показывать ссылку "Список тем”
$showlisttopics = true;
// Вывод линейки новых сообщений $showforumsline = true;
$ гeadforumline = true;•
$title = strip_tags($theme);
// Выводим "шапкум страницы
require_once (°.. /utils/ topforum.phpM);
echo "тема:"*$theme;
if (isset ($thCTne_prev [ * namef ]))
{
echo "<a
href=read. php?id_forum=$id_forum&id_therne= ". $theme_prev [1 id_th^ne'].
">"・ $theme^prev (' name' ] . "</a>";
}
if (isset ($theme_jiext [ * naroe1 ]))
{
echo "<a
hre^read.php?id_forum=$id_forшl&id_theme^l,, $theme_next [1 id_theme'].
H>". $theme__next [ *name,]. H</a>°;
}
?>
<table>
<?php
// Выбираем первое сообщение темы
$query = "SELECT * FROM posts
WHERE id_them£ = $id_theme AND
hide != *hidef
ORDER BY parent_post LIMIT Iм;
$pst = mysql_query ($query);
if($pst)
{
$posts = mysql_fetch_array ($pst);
// Рекурсивно выводим все подчиненные сообщения
@putpost ($posts【т idjpost,],
$id_theme,
2,
$lasttime,
$current_author,
$id_forumf $lineforum, $ 1 ine forumdown, $skin, $themes[fhide*]);
} else puterror(^Ошибка при обращении к теме: $id_themen);
?>
</table>
<?php
〃 Выводим завершение страницы include "../utils/bottomforum.php**; // Помещаем страницу из буфера в переменную $buffer $buffer = ob_get_contents();
// Очищаем буфер ob_end_clean();
// Отправляем страницу клиенту echo $buffer;
?>
Здесь, как и в файле index.php (см. листинг 20,1), весь вывод в окно браузера помещается в буфер, отправляемый клиенту единым блоком в конце скрипта. Помимо имени текущего посетителя из cookie извлекается также вид форума (переменная $lineforum) и направление сортировки (переменная $iineforumdown). Переменная $iineforum может принимать два значения: пустую строку и ,set_line_forum\ В первом случае сообщения в теме отображаются в структурном варианте, во-втором —— в линейном. Если принято линейное отображение форума, проверяется вторая переменная —— $iineforumdown. Если данная переменная принимает значение 1 sе t_i ine_f о гwn_down1 — сообщения в теме сортируются по убыванию даты добавления (старые сообщения вверху, новые — внизу), если же переменная пустая, сообщения сортируются по возрастанию даты (новые сообщения вверху, старые — внизу).
После обновления даты последнего обращения посетителя к форуму из таблицы themes извлекаются записи, соответствующие текущей, следующей и предыдущей темам. Все три названия тем обрабатываются функцией theme_work_up(), рассмотренной выше в листинге 20.2.
После включения т,шапки** страницы topfbrum.php, вывода названия темы и ссылок на следующие и предыдущие темы из таблицы posts запрашивается первое сообщение темы. Поле parentjost первого сообщения принимает значение 0, для всех последующих сообщений темы данное поле принимает значение первичного ключа сообщения, ответом на которое оно является. Одним из критериев выборки сообщений является условие hide != ^ide', позволяющее выбирать нескрытые и закрытые темы. После успешного обращения к базе данных вызывается функция putpost () из utils/utils.php, вьь полняютая рекурсивный вывод сообщений темы. Код функции приведен в листинге 20.17. Функция принимает 10 параметров:
口 $id_post ——идентификационный номер сообщения;
О $id theme — идентификационный номер темы;
口 $ indent — процент отступа: первый вызов всегда с 0, при рекурсивном спуске функция автоматически вычисляет значение этого параметра;
口 $last_time ——время последнего посещения для правильного отображения новых сообщений;
口 $current_author ——ИМЯ автора;
口 $id_forum — текущий раздел;
$lineforum — ВИД форума;
$ lineforumdown —— направление сортировки для линейного форума;
口 $skin — путь к папке со скином;
П1 $ themehide — статус темы.
// Рекурсивная функция вывода сообщений function putpost($id_post, $id__theme, $indent, $last_time, $current_author, $id_forumr $lineforumr 帛 1 ine f о ruindown, $skin,
$themehide)
{
// Если в cookie установлена переменная $lineforum/ выводим // линейный форум if (! empty ($linefoi?um)) {
// Если определена переменная $lineforumdown, сортируем // сообщения от старых к новым
if(!empty($lineforumdown)) $sort_msg ^ "”; else $sort_msg = "DESC";
$query = nSELECT * FROM posts
WHERE id_theme = $id_theme AND hide != Fidb
ORDER BY time $soEt_msg”; $pst = mysql_query ($query); if($pst)
while ($posts = mysql__fetch__array ($pst))
{
post_down ($posts [1 id_post1 ], $id__theme, $indent, $la5t_time, $current_author, $id_forum, $posts[fid_author1], $posts「author•], $posts[•time*]『 $posts[1putfiie'], $posts['name'], $posts [ * urU ], $skin,
$ themehide);
}
} else puterror("Ошибка при обращении к теме: $id_theme *..”); }
// Иначе структурный форум
else
{
// Выводим сообщение с id_j)ost = $id_post
$query = "SELECT * EROM posts
WHERE idjpost 二 $idjx)st ANDhide != 'hide'";
$psts = mysql_query($query);
if ($psts)
{
$posts = mysql_fetch_array($psts);
post_down($idjpost,
$id_theme,
$indent, $last_time, $current_authorr $id_for皿 $posts[fid_authorT], $posts[1author■],
$posts[•time*], $posts[rputfile1],
$posts["name1 ],
$po3ts[ lurl,J, $skin,
$themehide);
// Выводам подчиненные сообщения
$query = "SELECT * FROM posts
WHERE parent_post = $id_post AND hide != 'hide'
ORDER BY id』o§t”;
$psts = mysql_query ($query); if ($psts) {
$num_rows=mysql一nwn_rows ($psts);
while ($posts = mysql_fetch__array ($psts)) {
$ shap_indent=5;
if ($num_rows * $ shap_indent>350) $shap_indent = 3;
// Вычисляем отступ
if($indent<70) $temp 工($shap_indent + $indent*(95)/100);
else $temp = (5 + $indent*(100 - $indent)/100);
// Рекурсивно вызываем функцию putpost () для обработки // подчиненных постов
putpost($posts[*id_post1],
$id_theme,
$temp,
$last_time# $current_author, $id_£orum, $Linefcrum, $lineforumdown, $skinf
$themehide);
}
} else puterror("Ошибка при выборке сообщений тежт..* Iя);
)
else puterror ('^Ошибка при выборке сообщений темы.. . и);
Функция putpost о ответственна за линейное и структурное представление форума. Если переданная ей в качестве параметра переменная $iineforum не пуста, сообщения будут выведены в линейной форме. Для этого из таблицы posts запрашиваются все нескрытые сообщения, принадлежащие теме с первичным ключом $id_theme. Сообщения сортируются по полю time, направление сортировки определяет параметр $lineforumdown, пустое значение которого приводит к обратной сортировке, а не пустое — к прямой.
Если переменная $iineforum принимает пустое значение, формируется лестничная форма. Для этого выбираются все сообщения темы, для которых текущее сообщение является родительским. Для каждого из таких сообщений функция putpost о вызывается повторно. При этом в качестве $id_post передается первичный ключ сообщения, а параметр $ indent увеличивается на небольшую величину для создания отступа.
В каждом из двух случаев: при формировании и линейного, и структурного форума происходит вывод сообщения. Сообщение формируется и выводится в окно браузера при помощи отдельной функции post^down () из utils/utils.php. Код функции приведен в листинге 20.18. Функция принимает 14 параметров во многом совпадающих с параметрами функции putpost о. Отличие заключается в следующих параметрах, которые повторяют структуру таблицы posts:
口 $id_author — первичный ключ, соответствующий автору сообщения;
口 Sauthor —— нмя автора сообщения;
$time — время добавления сообщения;
$fiie ——путь к файлу, прикрепленному к сообщению;
$name — тело сообщения;
口 $puturi — URL ресурса, добавленного автором к сообщению.
Листинг 20*18. Функция post__down()
// Функция вывода поста на страницу function post^down ($icPpost, $id_theme, $indent, $last_time, $current_author, $id_forum, $id__author, $author, $time.
$file,
$name,
$puturl,
$skin,
$themehide)
// Таблица сообщения - под каждое сообщение - своя таблица
?>
<tr><td>
<?
// Если сообщение новое, то показать это выводом соответствующих
// изображения и стиля
if (eirpty ($last_time)) $last__time = date ("Y-m-d H:i:s", time () -3600*2);
$query_time = "SELECT 1 $ last_time*< time FROM posts
WHERE id_post 二 $id_postn;
$flg = mysql_query ($query_time);
if($flg)$is_new = mysql_fetch_array($flg);
else puterror (|гОшибка при выборке из базы данных сообщенийг,);
if £$is_new[Г| 1 $las Jtime' <time“])
(
$new = "<img src=,t. $skin. Himages/new>gif border=0>n;
$style^postbody^" class=postbodynew ”;
}
else
{
$new = ”七
$style_postbody=n class=postbody ”;
}
?>
<table border=”0" widths11100%" <? echo $style_j>ostbody; ?»
<?php
// Если есть прикрепленный файл, то формируем ссылку на него $writefile - "3
if ($file »= "“ && $file != ^" && is_file($file))
{
// Если файл не нулевой длины, выдаем на него ссылку
if (filesize ($file)) $writefile = Уз target=_blank href®'4$file/,>
<img border=0 src=".$skin.Himages/flopy.gif></а>и;
// Иначе уничтожаем его
else unlink ($file);
}
// Выводим заголовок сообщения: ник, время создания сообщения
// Если id_author не равно 0 — посетитель зарегистрирован -
〃 нужно выдать на его портрет
if($id_author != 0)
echo f,<tr>
<td width«'”•Sindent/1%1>$new</td>
<td>aBTop: <a href=infо.php?id__forum=$id_forumfiid_author=$id_author> ".htmlspecialchars($author)*"</a> <”.$tima.")
<a href=!inailfonn.php?id_forum=$id_forum&id_theme=$id_theine&
id_author=$id author>письмо автopy</a>$writefile</td>
<td width=50> </td> </tr>”;
else
echo M<tr>
<td width= f. $indent.и% *>$new</td>
<td>aBTop: °.htmlspecialchars($author)・"{"・$time.")$writefile/td>
<td width=50>snbsp;</td>
</tr>";
// Выводим тело сообщения
// Если прикреплен url - вывести
$tmpurl = is _Jittp(caturl($puturl));
if ($puturl •= "” && $ put url != "-") $url = "<brxbr><a href=$tmpurl targe^_blank>$tmpurl</a>M;
else $url ="”;
// Если на сообщение нет ответов и текущий пользователь является его
// автором - позволить ему редактировать это сообщение.
$edit ^ "";
$ query = "SELECT idlest FROM posts WHERE parent_post = $id_post";
$егф = mysql__query ($query);
if(!$emp) puterror("Ошибка при выборке сообщений (правка)・♦・");
$ empty = my sql_f etch array ($егф);
if (empty ($ empty [ * id_post * ]))
(
$query - иSELECT author FROT posts WHERE id_j3ost = $id_postM;
$ath = mysql^query ($query); if($ath)
$author - n^sql_fetch_array ($ath);
if(strtolower($author[1authorf])^strtolower($current_author)) $ed±t = "<a href=editpostform.php?id_forunp=$id_forum&id_j)ost=$id_post&
id_theme=$id__theme>Править сообщение</а>и;
}
}
// Обрабатываем текст сообщения
$ postbody = post_work__up ($nante);
/ / Формируем ссылку для ответа
if($themehide != 1lock1) $answer = '*<td>$edit<a href=answer.php? id__forum=$id_forum&id_theme=$id_theme&id_post=$id_post> Ответить </ax/td> ";
else $answer ^
// Выводим тело сообщения
echo H<tr><td width='” *$indent・ f ></td>
<td>$postbodyt, * $url. "</td>
<td></td>
</tr>M;
echo ”<tc
<td width®1 H.$indent*ri%r> </td> $answer
<td> </tdx/tr>M;
echo "</t;able>";
echo n</tdx/tr>tt;
Сообщение формируется в отдельной таблице. На протяжении всей функции информация из таблицы posts, переданная через параметры, подвергается обработке: определяется, является ли сообщение новым для текущего посетителя, и проверяется наличие прикрепленного к сообщению файла* Обрабатывается строка с URL, для чего она передается функции caturio (листинг 20.19) и is_http()(листинг 20.20). Обе функции расположены в файле utils/utiis.php.
и СТИНГ 20.19. Функция caturl()
// Эта функция вырезает из url угловые скобки function caturl($text)
{
return strip_tags($text);
;■,•电生w”.»•二ц史t・ v'^1Г 7.'V • “ЩТ::・7И? *£中止¥*> 代注.鵠
;Листинг 20.2Q. Функция 土s_http()
// Проверка наличия http:\\ function is_http($text) {
$text = html specialchars($text); if(trim ($text)!=”")
if (substr (trim($text), 0# 5) !=Hhttp: " && substr (trim($text), 0# 4) !=r,ftp: M) (
$text - nhttp:// *\ t rim($text);
return trim($text);
Функция is http()осуществляет проверку наличия имени протокола в начале URL, если он отсутствует, — подставляется “http: //”.
i З.менанье )
Если тема не является закрытой, под каждым из сообщений вн旧однтся ссылка Ответить, переход по которой позволяет добавить сообщение. За обработку этого события ответственны HTML-форма answer.php и ее обработчик addpost.php. Так как эти два файла практически эквивалентны файлам добавления новой темы (см. листинги 20.4—20.6), они рассматриваться не будут.
Текст сообщения, так же как и текст названия, обрабатывается отдельной функцией из utils/utils.php — post_work_up(), код которой приведен в листинге 20.21. Функция принимает н возвращает строковую переменную, являющуюся текстом сообщения.
;Листинг 20.21. Функция post_work_up ()
//////////////////////////////////////////////////////
// Функция для обработки текста сообщений, заменяет все теги на
// их НТМЪ^эквиваленты, обрабатывает встроенные теги:
// [Ь], [/b], [i], [/i], [code] и [/code]
////////////////////////////////////////////////////// function post__work_up ($postbody)
(
// Обрабатываем теги [code] # [/code]
$text = и[code]H;
$lastocc - 0;
$sndocc = 1;
$ result а '•”; while($sndocc)
$fstocc = strpos($postbody, M[code]", $lastocc);
$sndocc = strpos($postbody, "[/code]", $fstocc);
if(($fstocc>0 && $sndocc>0 && $lastocc>0) I I
($fstocc >= 0 && $sndocc>0 && $lastocc — 0))
$result .= nl2br(htmlspecialchars(
substr($postbodyf $lastocc, $fstocc - $lastocc)));
$ result 尸 ft<table border=0 ><tr><td>M.
highlight_string{
substr($postbody, $fstocc + 6, $sndocu - $fstocc - 6), true) .H</td></trx/table>,*;
$lastocc н $sndocc + 7;
else
I
^result .= nl2br(htmlspecialchars(
substr($postbodyr $lastocc,strlen($postbody)-$lastocc)));
break;
$postbody $postbody $postbody $postbody $postbody $postbody$postbody $postbody $postbody $postbody $postbody $postbody$result;
$postbody $postbody $postbody $postbody $postbody $postbody
$postbody $postbody $postbody $postbody $postbody $postbody
str_replace {" [b] "t ll<b>,\ $postbody);
str_replace (” [/bj ”「</b>",$postbody);
str_replace("[i]","<i>*\ $postbcdy);
str_r eplace (”【/i ] ”「v / i>", $ postbody); str_replace (" '",”'",$pcstbody);
return $postbody;
Функция post_work_up() предназначена для обработки встроенных тегов [b], [/b], [i], [i], [code] и [/code]. Первыми обрабатываются теги [code] и [/code], обработка их происходит в цикле while, так как в теле сообщения может быть несколько блоков с кодом. Текст между этими двумя тегами обрабатывается функцией highlight_string(), а вне этих тегов — htmlspecialchars (). Оставшиеся встроенные теги Qb], [/Ь], [i], [i]) за-
меняются их НТМL-эквивалентами. В конце функции обратные кавычки заменяются прямыми ——это необходимо, поскольку перед помещением сообщения в базу данных проводится обратная операция — прямые кавычки заменяются обратными, чтобы избежать конфликта при добавлении новой записи в базу данных.
Для предоставления информации о зарегистрированных посетителях в меню форума присутствуют два пункта: Все участники и Участники Online. Первый пункт предназначен для вывода списка всех участников форума, второй — участников, находящихся в данный момент на форуме. За генерацию данных списков несут ответственность файлы authorsKst.php (листинг 20.22) и online.php (листинг 20.23) соответственно.
潑聯轡裁掣咨а倒绐耀净婚哪齡深喻Мй1Ш11ИШ0^ШМйШйвШ^№^^^^囑
;Листинг 20.22. Файл authorsllst.php
<?php
〃 Устанавливаем соединение с базой данных require_once (r,config.phpn);
// Извлекаем параметры из строки запроса
$page = $_GET['page"];
?>
<а href^иjavascript: history.back()и>Вернуться назад</а> <table>
<trxtd>Участник ①орума</9>
<td>4Mono сообщений</1d>
<3〉Последнее noceqeHKe</td> vtd>CT атус</td>
</tr>
<?php
〃 Отображаем no $pnumber авторов на странице $pnumber = 25;
if{en^ty($page)) $page=l;
$begin = ($page - 1)*$pnumber;
$query = "SELECT * FROM authors ORDER BY themes DESC LIMIT $begin, $рпш1Ьеги;
$ath 二 mysql_query ($query);
$tot ^ mysql^query("SELECT COUNT(*) FROM authors");
if($ath $tot)
// Выводам список первых $pnumber авторов
while {$author = mysql_fetch_array ($ath))
{
// Проверяем статус автора
$status = ”";
if($author[* statususer'] = Moderator1) $status = "Модератор";
if{$author【” statususer1] = 1 adminU $status = "Администратор"; echo H<tr>
<td>
<a href=infо.php? id_forum=$ id_forum& id_author=".$author[1 id author1] Jf>" ,$author[fname']・"</a></td>
<td>M.$author[1 themes 1]."</td>
—$author[•time1]."</td>
<td>$status</tdx/tr>,f;
}
echo "<7 table»
// Выводим ссылки на следующие страницы списка авторов
$total = mysql^result($tot, 0);
$number = (int)($total/$pnumber);
if((float)($total/$pnumber)-$number != 0) $nuwber++;
echo "<tr><td class=bottomtablen colspani=4xp class=texthelp>,T;
for ($i = 1; $i <= $number; $i-H-)
{
if ($П1дпЬег = $i)
{,
if($page = $i)
echo ”["・(《$i - l)*$pnumber + 1) JTtotal]";
else
echo "<a href=authorslist *php?id_forunir=H. $id_forum. '4page^H. $i・”> 「.{僧i - l)*$pnumber + 1) .n-$total]</a>,r;
else
{
if ($page = $i)
echo ”「'・(($i - l)*$pnumber + 1) /l-'\$i*$pnumber.п] °;
else
echo "<a href =authorslist.php?id_f огипе=и»$ id_forunu "&page=” . $i •"> ["・<(«■ - l)*$pnumber + 1) J-”・$i*$pniOTb巳r J] v/a>";
echo r,</td></tr>";
} else puterror(,fОшибка при обращении к таблице авторов”); ?>
На каждой странице выводится по 25 участников форума, отсортированных по числу сообщений, размещенных ими на форуме. Последний столбец таблицы содержит статус участника форума, который в зависимости от значения поля statususer таблицы authors может принимать значения "Администратор” н “Модератор”.
gj^g1ypг^д^удд襲&产*^зд;д^д^щц^д^дщд^д^^^|^|^^^р^^^^А+罟"* 堤,,,缪鹏**3,产*:,讐 E,wj¥Ma*m^[3W"yfe*,,,*»r$jl^^ft*”,*「*:*yj*:-*,*±*V7k,t,;f,[F
Листинг 20.23. Файл onllne.php;
<?php
// Устанавливаем соединение с базой данных require_once(nconfig.php");
// Извлекаем из строки запроса первичный ключ
// форума $id_forum
$id_forum = $_GET (1 ic^forum1 ];
?>
<з href=rtjavascript: history.back()°>Вернуться назад</а>
<table>
<tr>
<td class=tableheadernxp class=11 fieldnamen>ABTOp</td>
<td class=tableheadernxp class="fieldname">Время посещения форумаv/td> </tr>
<?php
// Выводим участников, которые зашли на форум менее 5 минут назад
$query = "SELECT * FROM authors
WHERE time > NOW() - INTERVAL 5 minute
ORDER BY time DESC";
$ath = mysql_query($query);
if($ath)
(
while($authors - mysql_fetch_array($ath))
{ echo w<tr> <tdva h.ref=infо. php?
id_f ortjirF=$ id_forum&id__author~ ** • $ authors [1 id__author,]・"> ”・ html special chars ($ authors [ fname1 ]) , **</ax/td>
<td>" ♦ $ authors [' time* ]." </tdx/tr>H;
J
} else puterror(^Ошибка при выборке тем ①оруна");
// Выводам участников, которые зашли на форум в интервале
// от 5 до 10 минут назад (уходящие).
// От тех посетителей, что находились на форуме менее 5 минут назад,
// они отличаются стилем отображения
$ query н ** SELECT * FRCM authors
WHERE time > NOW() - INTERVAL *10* minute AND
time < NOW() - INTERVAL ‘5’ minute
ORDER BY time DESCM;
$ath = mysql_query($query); if($ath)
{
while($authors = mysql_fetch_array($ath))
{ echo n<tr>
<td><a href=info.php?
id_forum=$id_forum&id_author=lt * $authors (1 id_auth”']・"> 11 .html special chars ($avthors [ Fame,]) Jv/aX/td>
<td>" . $ authors [*Ь1те*] . M</tdx/tr>,';
}
} else puterror (пОшибка при выборке тем форума11); ?>
Таблица, формируемая скриптом из листинга 20.23, аналогична таблице, генерируемой скриптом из листинга 20.22. Отличие заключается в том, что выводятся не все зарегистрированные участники, а лишь те, чье поле time было обновлено менее чем 5 и 10 минут назад.
( Замечание )
Два временных интервала: от 0 до 5 минут и от 5 до 10 минут необходимы для того, чтобы выделить посетителей, находящихся на форуме (от Одо 5 минут), и посетителей, недавно покинувших форум (от 5 до 10 минут).
Любой участник форума может послать зарегистрированному посетителю письмо, осуществив переход по ссылке Отправить письмо, которая выводит-
ся рядом с каждым сообщении посетителя. За отправку письма несут ответственность два файла:
口 mailform.php — HTML-форма для формирования текста и темы письма (листинг 20.24);
□ mail.php — обработчик формы mailform.php (см. листинг 20.25).
;Листинг 20.24. Файл mailform.php
晒♦*、Л 當F*;・X;. >■■■*• Л°«{^а'а> ^><г^^Л^*7^'^Л*г!ЯЯ9тИ№^Л№л{17№:11« ;*'落扌禽1t^U%*M;Ti7fi it«Л • м » #*'•«»• iKwiiiPmTO «rW1* •«rt«.^< i'K n'lWMmWFEVnTMwi
<?php
// Устанавливаем соединение с базой данных
requitejnce ("config. php");
// Извлекаем значения параметров из строки запроса
$id_forum = $_GET [ * id^forum* ];
$id_theme = $_GET [1 id_theme'];
$id_author = $_GET[1id_author4 ;
if(егфЪу($id_author)) $id_author = 0;
// Извлекаем данные автора из базы данных
$query ^ "SELECT * FROM authors
WHERE id__author = $id_author";
$ath = mysgl_query($query);
if ($ath)
{
$author = mysql_fetch_array f$ath);
if(trim($author[femail1]) = "” || $author[1emailr] = "-")
links_forum($id__forum, "<i>e-mail</i> отсутствует1');
} else puterror("Ошибочный запрос к автору: $id_authorM);
if (empty ($ return)) $ return = $_SERVER[,,HTTP_REFERERr,];
?>
<a hre£=Y? echo $ return ?>1 >Вернуться в форум< / a> <br >
<fom enctype= *multipart/form-data 1 action^mai 1.php method=post>
<table>
Тема: <input type=text name=theme>
Сообщение: <textarea cols=63 rows=10 name=message></textarea><br>
<input type=submit name=send value^Orправить>
<input type=hidden name=id_author value«<? echo $id_author; ?»
<input type^hidden name= id_theme value=<? echo $id_theme; ?»
<input type=hidden name=id__fоrum value^<? echo $id_forum; ?»
</table>
</form>
Форма содержит текстовое поле theme, текстовую область message и кнопку для отправки содержимого полей обработчику. Помимо видимых элементов управления, форма содержит три скрытых поля для передачи первичных ключей раздела форума, темы и посетителя, которому отправляется письмо.
;Листинг 20.25. Файл mail.php
<?php
// Устанавливаем соединение с базой данных re(guire_once ("config.php");
〃 Извлекаем значения переменных
$id_f orum = $_FOST 厂 id—forum1 ];
$id_theme ^ $_POST「id_theme1 ];
$id__post = $_POST [ * id__post'];
$id_author = $_POST[1id_author *];
$ theme = trim($_POST [ * theme1 ]);
$message = trim($_POST [1 message* ]);
// Проверяем введенные данные на соответствие if(empty($theme)) links($id_forum, $id_theme#
"Тема сообщения не введена*1);
if (empty ($message)) links ($id_fo匸皿 $id__theme,
"Сообщение не введено1');
$query = "SELECT * FROM authors
WHERE id_author = $id_author,1;
$ath н mysql_query ($query);
if ($ath)
{
$ author 二 mysql_fetch_array ($ath);
if (trim($author [1 email • ]) — "” I I $author [ •emailf ] 一 "-”)
{
$ namepage ^ H<i>e-mail</i> отсутствует*1, exit 0 ;
}
if (@wail ($author [ *eniail1 ], $ theme, $message)) (
echo H<HTML><HEAD>
<META HTTP-EQUIV=1Refresh1 CONTENT^0;
URL=read* php?id_forum-$id_forumSiid_theme=$id_thenie&id_post=$id_post * >
</HEADx/HTML>";
}
else links($id_forum, $id_theme, "К сожалению, письмо не было отправлено");
}
else
{
puterror("Ошибочный запрос к таблице авторов автору: $id_authorn);
}
?>
Скрипт в файле maiLphp извлекает e-mail посетителя из базы данных и отправляет сформированное в форме mailfbrm.php почтовое сообщение при помощи функции mailt). В случае успешной отправки письма скрипт осуществляет автоматический переход на исходную страницу.
Часто, когда в текущий момент на форуме находится несколько посетителей, осуществляютих размещение новых сообщений, пометка старых сообщений как новых мешает работе. Для этого на форуме введена возможность пометки всех сообщений как прочитанных. Это необходимо, если посетитель не намерен ждать 20 минут, для того чтобы сообщения были отмечены как прочитанные по тайм-ауту. За осуществление этой функции ответствен файл readall.php, код которого представлен в листинге 20.26.
;Листинг 20.26. Файл readall.php
<?php
// Устанавливаем соединение с базой данных require_once ("config.php**);
// Подключаем библиотеку форума require_once(и* */utils/utils.php");
// Отмечаем все сообщения автора как прочитанные
if (isset ($_COOKIE [ * current__author1 ]))
{
settime ($_COOKIE[1 current_author1 ], true);
}
// Осуществляем автоматический переход обратно
echo "<HTMLXHEAD>
<META HTTP-EQUW='Refresh “ CONTENT-' 0; URJ"・ $_SERVER【"HTTP_REFERER" ] . "1 >
</НЕАО></НТМЪ>и;
?>
Эффект пометки всех сообщений как прочитанных достигается принудительным обновлением времени последнего посещения при помощи функции settime о(СМ. ЛИСТИНГ 20.11).
Вывод правил форума осуществляется при помощи файла rules.plip, вызываемого при переходе по ссылке Правила форума. Код файла rules.php представлен в листинге 20.27.
<?php
// Устанавливаем соединение с базой данных
require_once ("config.php");
// Подключаем библиотеку форума require_once (и. ./utils/utils.phpM);
/ / Выводим ссылки на другие форумы
$query = "SELECT * FROM forums
WHERE id_forum=” • $_GET「id_forum* ];
$rls = mysql_query($query);
if($rls)
{
$rules = mysql_fetch_array($rls);
?>
<a href=иj avascript: history>back()">Вернуться назад</а>
<?php
echo M<p>M.$rules「rule■]・"v/p>";
)else puterror(^Ошибка при обращении к таблице форумов...11); ?>
Переключение между линейным и структурным режимами осуществляется при помощи скрипта в файл setstruct.php, код которого представлен в листинге 20.28.
\ Листинг 20.28. Файл setstructphp
<?php
// Устанавливаем соединение с базой данных require_once("config•php");
// Подключаем библиотеку форума
require once С1../utils/utils.php");
/ / Извлекаем значения параметров из строки запроса
$id__forum = $_GET [ * id_forum * ];
$id_theme = $_GET [,id__theme,];
$page = $_GET「page*];
$struct = $_GET[* struct,];
$down = $_GET[1downr];
// Определяем путь к форуму
$tnippos = strrpos ($__SERVER [' PHP_SELFI 1, °/H) + 1;
$path = substr($_SERVER[*PHP__SELF']r 0, $tn^pos);
if(^struct)
{
$query = "SELECT * FROM settings'*;
$set = mys ql_que ry ($ que г у);
if($set) $settings = mysql_fetch_array($set);
setcookie(HlineforumH,
"set_line_forumt,, time () + 3600*24*$settings ( ^ooktime* ], $path);
/ / Если определена переменная $down, сортируем новые
// сообщения — от старых дат к новым
if(!empty($down)) setcookie("lineforumdown*\ Hset_line_forum_downM, time () + 3600*24 *$settings [ • cooktime * ], $path);
else setcookie("lineforumdown”,
0, $path);
} else setcookie("linefогшпи, "”, 0, $path);
// Осуществляем автоматический переход к теме
echo "<HTMLXHEAD>
<МЕТА НТТР-EQUIV»1Refreshт CONTENT='0;
URI>read. php?id_forum=$ id_forum&id_theme=$ id_t heme&page=$ page' > </HEAD></HTML>";
Наконец, последний сервис, который будет рассмотрен в этой главе,——по- / иск. Полнотекстовый поиск по названиям тем и сообщениям форума осуществляется при помощи файла srchform.php, включающего как HTML-
форму, так н ее обработчик. Код файла srchfbrm.php представлен в листинге 20.29.
:Листинг 20.29. Файл srchform.ph?
<?php
// Устанавливаем соединение с базой данных require_once("config.php”);
// Извлекаем переменные, переданные методом POST
$ number themes = $_POST【* number themes1 ];
Sname = $_POST [' name * ];
$srchwhere = $_POST[*srchwhere'];
$logic = $_POST[1logic*];
$id_forum = $_POST「id_forumT ];
if (!empty($_GET[,referee1])) $ referer=$_GET[,referer1];
else $referer=$_POST[*referer']; ?>
<a href=<?php echo $referer; ?»Вернуться</а><Ьг>
<fonn action=srchform.php method=post>
Ключевые слова <input type-text name^name value='<?= $name; ?>*><br> Количество выводимых тем < input typeMext
name=numbertheme s
value=<?php if($numberthemes = "”) echo 30;
else echo $ number themes; ?»<br>
Искать:
Oelect type=text name=srchwhere>
<option value=2 <?php if($srchwhere 3 2) echo "selected"; ?» в сообщениях
<option value=l <?php if($srchwhere = 1) echo "selectad"; ?» в названиях тем
</select>
Искать в форуме
<select type=text name=id_forum>
<?php
$query = "SELECT * EROM forums WHERE hide ^ 'show' ORDER BY pos,
$frm = mysqljquery ($query);
if($frm)
while ($forums = mysql_fetch_array ($frm))
(
if ($forums [ *id_forumf ] = $id_forum) $strtmp = "selected";
else $strtmp ="”;
echo H<option value=" * $ forums [1 id^forum* ]." $strtn^>>" .$forums [ * name1 ]; }
J
?>
</select>
Логика <input name=logic
type^radio
value=0
<?php if($logic=0) echo "checked,?»ИЛИ
<input name=logic
type=radio
valuer1
<?php if ($logic=l) echo **checked1*; ?»И
<input type=submit name^send value=HaitTM>
</table>
<input type=hidden name=referer value=<? echo $referer ?»
<input type=hidden name^id__fоrum value®1<?php echo $id_forum; ?>*> </for7n>
<?php
〃 Обработчик формы
if('empty($name) && !enpty($nmnberthemes))
(
switch($srchwhere)
(
case 1: 〃 Поиск в названиях тем и авторах
(
$name = trim($name);
$temp = strtok($name,° ”);
if($logic == 0) $logstr = "cr";
else $Iogstr = "and”;
while ($temp)
// Полнотекстовый поиск в столбцах name и authors
if($is_query)
$Шф1 •= ” $logstr MATCH (name,author) AGAINST (ftempl"; else
$咖工.="MATCH (name, author) AGAINST ( ^temp») и; $is_query > true;
$temp = strtok(n ");
}
// Формируем SQL-запрос
$query = "SELECT * FROM themes
WHERE $tn$>l AND
j.d_forwi = $id_forum AND hide = 'show'
ORDER BY time DESC LIMIT $ number themes11;
$thm = mysql_query ($query);•
break;
}
case 2: // Полнотекетовый поиск по сообщениям
(
$name = trin<$name);
Stemp = strtok($name, ” ”); if($logic = 0) $logstr = "or"; else $logstr = "and";
while {$terop)
{
// Полнотекстовый поиск в столбцах name и authors
if($is_query)
$泣工•= ” $ logstr MATCH (name, author) AGAINST (1 $ten^')r,; else
$tnpl *= "MATCH (name,author) AGAINST「$te呷,)"; $is_query = true;
$temp = strtok(” ”);
}
// Формируем SQL-запрос
$query = "SELECT id_theme FROM posts WHERE $tropl AND hide -= f show *
GROUP BY id__themen;
$pcst a mysql_query ($query);
if($post)
{
$numtot 工 mysql_num_rows ($post);
if($numtot>0)
{
$ query = "SELECT ★ FROM themes WHERE id_theme IN (”; while($posts = mysql_fetch_array($post))
$query .= $posts[1id_theme1],”,“;
}
$ query ・=”0) AND id_£orum = $id_forum AND
hide = 'show'
GROUP BY id_theme
ORDER BY time DESC
LIMIT $ number themes '*;
$thm - mysql_query ($query);
}
else
{
echo '*<p class=result>no запросу ничего не найдено.</р>м; exit ();
卜
}else puterror (^Ошибка при выборке тем форума. ..м); break;
}
} // Конец оператора switch($srchwhere)
if($thm)
(
// Выясняем количество тем
$numtot = mysql_nuin_rows ($thm);
if($numtot>0)
{
〃 Начало таблица с темами
?>
Результаты:
<table>
<tr>
<td>Kan-Bo</nobr> coo6q.</td>
<td>Название темы</3>
<td>ABTop</td>
<Ьс1>Последнее сообщение</1d>
</tr>
<?php
if (empty ($lasttime))
{
$lasttirne = date (^Y-m-d H: i:sri, time ()-3600*2);
}
while ($themes = mysql_fetch_array ($thm))
{
// Считаем количество сообщений в данной теме.
$query = мSELECT COUNT(*) FROM posts
WHERE id_theme = ". $ themes [' id_theme' ] • " AND hide - 'show'”;
$pst = mysql_query ($query);
if($pst) $ posts = mysql_fetch_array ($pst);
else puterror (TtОшибка при подсчете количества сообщений темы...и);
// Количество сообщений в теме
echo "<tr><td>" • $ posts [ * count (*) * ] . **</td>";
// Выводим нему
// Предварительно обрабатываем угловые скобки и знаки абзаца $name = htmlspecialchars($themes[*name1]);
// Выводим список тем
if(!entity <$page)) $strpage = H&page=n * $page;
// Название
echo "vtd>。
href er ead, php? id_forum=n. $id_forum. n&id^theme^',. $ themes [f i d_theme , ]. $strp age. “>". $name ・'y/a></td>'';
// Автор
if ($themes [ ^d^author1 ] != 0) echo "vtdxa href=info.php? id_forunv=". $id_forum・ "&id_&uthor=" • $ themes {1 id_author' ] • ”> и. htmlspecialchars ($themes [T author1 ]) . n</ax/td>n;
else echo f,<td>f,. htmlspecialchars ($themes [1 author' ]) . "</td>";
// Время последнего обновления темы
echo ”<td>".substr($themes[1time*], 0,10)*
” в n>substr($themes[1 time*],10f 6) J* </td></tr>M;
// Конец таблицы по выводу тем форума
// Выводам ссыпки на другие темы форума
echo "vtrxtd colspan=4>Сообщения: [l-$numtot] </td></tr>n;
// Конец вывода ссылок на другие темы форума echo "</table>";
} else { echo "К сожалению, по Вашему запросу ничего не найдено* Попробуйте изменить условия поиска*";
}
)else puterror(нОшибка при выборке тем форума..,");
} ?>
В начале файла расположена HTML-форма, состоящая из следующих элементов:
口 двух текстовых полей, паше н number themes, для ключевых слов и максимального числа тем в результате поиска;
口 двух выпадающих списков, srchwhere и id_forum, для выбора режима поиска (в названиях тем или в текстовьгх сообщениях) и для выбора раздела форума, в котором будет осущетствлен поиск;
двух переключателей, позволяющих выбрать логику поиска: ”И” или 'ИЛИ';
кнопки, позволяющей отправить данные из формы обработчику.
В качестве обработчика выступает сам файл srchfbrm,php, обработка данных в котором расположена после формы.
SQL-запрос на поиск формируется в зависимости от значения переменной $srchwherej которая принимает значение 1, если поиск осуществляется в названиях тем, и 2 — если поиск ведется по всем сообщениям форума. При помощи функции strtoko строка поискового запроса разбивается на отдельные слова, которые в цикле while присоединяются к SQL-запросу. Между конструкциями match ... against . t. в зависимости от выбранной логики (определяемой переменной $ logic) вставляется либо оператор "and” (И), либо "or" (ИЛИ). После того как темы, в состав которых входят слова из поискового запроса, найдены в таблице, выводятся ссылки на их содержимое.
На этом разработка "двн*ка” форума завершена. В следующей главе будет рассмотрена система администрирования.
20.3. Дизайн форума
Дизайн форума практически полностью определяется стилевыми таблицами. Благодаря этому становится возможным изменение дизайна без изменения файлов, содержащих PHP-код, и создание скинов — вариантов оформления. Примеры разных вариантов оформления приведены на рис. 20.8 и 209 Единственный файл (diztop.php), где требуется изменение HTML-кода, содержит верхнюю часть ("шапку”)страниц форума — index.php и read.php. В данном файле происходит компоновка подключаемых внешних файлов, которые содержат: управляющие ссылки, приветствие, название форума, переключатели между разделами форума, элементы дизайна и т. п. Таким образом, с помощью данного файла можно практически полностью изменить внешний вид и компоновку "шапки" форума. Значимого РНР-кода данный файл не содержит.
Текущий скин форума можно установить на странице администрирования. Для этого нужно перейти по управляющей ссылке Настройки, выбрать нужный скин из выпадающего спнска внизу страницы и нажать кнопку Обновить информацию.
Рис. 20.8. Основной дизайн форума — скин "base*'
Рис. 20.9. Дополнительный дизайн форума •'Агрессия'* — скин "aggression"
Скины форума, или иначе — варианты его оформления, содержатся в директории skin. Каждый скин содержится в собственном каталоге. Название каталога является названием скина. Каталог со скином содержит следующие файлы и сложенные каталоги:
О images — каталог с изображениями, которые используются в форуме;
diztop.plip — "шапка'* страниц index.php и read.php. Содержит элементы оформления и определяет компоновку в структуре дизайна подключаемых внешних файлов;
О forum.css ——содержит общие для всех файлов стили;
ma instyle.css ——содержит стили, применяемые для оформления "шапки" страниц index.php и read.php;
read.css — содержит стили, используемые при выводе сообщений темы (файл read.plip);
口 act ion.css — содержит стили, применяемые во всех оставшихся файлах (формы для ввода новых тем, сообщений, отображения информации о
посетителях и т. п.), за исключением файлов index.php, read.php и скрип- тов-обрабочиков;
口 operatexss —— содержит стили, используемые в скриптах-обработчиках для отображения возможных ошибок.
Рассмотрим дизайн на основе базового скина "base”,который расположен в одноименном каталоге в директории skin. В листинге 20.30 приведен код файла diztop.php.
<body style&Hpadding: 2px; padding-top: 2px” topmargin=MOtt
bottommargin=°0H leftmargin="0** marginheight=HO" marginwidth=lr0" tightmargini。" bgcolor^^white1^
<!— Таблица co ссылками ——>
〈table border=M0" cellspacing=MOr, cellpadding=H0H width=n100%n>
<tr align=rTcenter°>
<td class^toplinksxp class=ptoplinks>
<a class=toplinks href=n-*/index.php'^
IT-студия SoftTime</a></px/td>
<td class=toplinks><p class=ptoplinks>
<a class^toplinks hraf="・•/infc/index.php”>
SoftTime INFO</a></p></td>
<td class=toplinksxp class=ptoplinks>
<a class=toplinks href=H../soft/about.phpH>
Разработка no</a></p></td>
<td class^toplinksxp class=ptoplinks>
<a class==toplinks href=° -. /bookphp/gll_l .php">
Учебник PHP</a></p></td>
<td class=toplinksxp class=ptoplinks>
<a class=toplinks href^11../bookphp/help.phpu>
Справочник функций PHP</a></p></td>
</tr>
</table>
<?php
// Отображение "линейки11 с разделами форума и выводом количества
// новых сообщений для каждого раздела
if ($shownewpost) include 11.. /utils/nev>rpostslist .php'1;
?>
<!— Таблица с синим фоном и белыми точками ——>
<table class=Hdiztabletop" border="0" cellpadding^,tOn cellspacing=и0M>
26 Зак. 1181
<tr>
<td width=5%x/td>
<td width=70¥ valign=MbottomH>
<?
// Название текущего форума
include ".・/utils/currentforum.php"; ?>
</td>
<!—— Управляющие ссылки форума ―>
<td class=infotd><p class=null>
class=menuinfo href=1enterform.php?
id_forum=<?php echo $id_forum; ?>&update=update1 > Обновить визитку</a><br>
<a<a<a<aclass==menuinfo id_forum==<?php class=rnenuinfo id_£orum=< ?php class=menuinfoclass==menuinfo id_forum==<?php class=rnenuinfo id_£orum=< ?php class=menuinfohref=rules.php?echo $id_forum; ?»Правила&пЬзр;форума</а><Ьг> href=online.php?echo $id_forum; ?»Участники&пЪ5р; <Online>< /a><br> href=authorslist.php?href=rules.php?echo $id_forum; ?»Правила&пЬзр;форума</а><Ьг> href=online.php?echo $id_forum; ?»Участники&пЪ5р; <Online>< /a><br> href=authorslist.php?
<a
<a
<a
<a
class==menuinfo id_forum==<?php class=rnenuinfo id_£orum=< ?php class=menuinfo
class==menuinfo id_forum==<?php class=rnenuinfo id_£orum=< ?php class=menuinfo
href=rules.php?
echo $id_forum; ?»Правила&пЬзр;форума</а><Ьг> href=online.php?
echo $id_forum; ?»Участники&пЪ5р; <Online>< /a><br> href=authorslist.php?
href=rules.php?
echo $id_forum; ?»Правила&пЬзр;форума</а><Ьг> href=online.php?
echo $id_forum; ?»Участники&пЪ5р; <Online>< /a><br> href=authorslist.php?
id_forum=C?php echo $id_forum; ?»Все&пЬзр;участники</а>
</td>
</tr>
<!—- Светло-серая линия внизу синей таблицы. Элемент дизайна -->
<trxtd class=dizline colspan="3,'> </tdx/tr>
</table>
<div class=switchforwndiv>
<?php
// Выпадающий список с разделами форума и кнопка
/ / пере ключения между ними
if ($show__switch_fonam) include ".. /utils/switchforuIn.php^^; ?>
</div>
<!— Элемент дизайна. Рожицы над названием форума —>
<div class=images> </div>
<!-- Таблица, а которую включена вся оставшаяся часть страниц index.php и read.php
Таблица не может быть удалена, так как ее продолжение находится в других файлах —>
<table border®"О" width^,'100%,• cellpadding="O" 811spauing="0">
<trXtd class=bodydiv>
// Приветствие посетителям форума include " • • /utils/salutation.php1';
// Меню форума (Новая тема, Регистрация, Вход, Анонимно, // Поиск, Отметить все) include "../utils/menu.php**;
Верхнюю часть страниц index.php и read.php можно оформить совершенно иначе. Изменяя соответствующие стилевые таблицы» можно варьировать оформление элементов, а изменения в файле diztop.php повлияют на положение элементов н их компоновку. Файл diztop.php содержит только одну таблицу (выделена полужирным), которая не может быть удалена при изменении дизайна, так как ее продолжение находится в других файлах, и в нее включена вся оставшаяся часть страниц index.php и read.php. Все остальное содержание файла diztop.php может быть подвергнуто изменению.
Файл diztop.php подключается в файл util/topfbrum.php, который, в свою очередь, подключается в файлы index.php н read.php. Подключение стилевых таблиц происходит в файле util/topforum.php (листинг 20.31). Этот файл не требует редактирования для изменения дизайна.
^^^^Ц^^^УЩ^^^^^^^^}^^^ ,mwa^^YW?y^rt^*4W^y %勺齡銀^岁券陷蟄^^喳军抬験“遡界咆般:將幽军 ;Листинг 20.31. Подключение стилевых таблиц в файле topforum.php
<link rel^^StyleSheet1* type=Mtext/cssH href=H<?php echo $skin; ?>forum.cssrt>
<link rel=**StyleSheetn type="text/css,1 href="<?php echo $skin; ?>mainstyles.cssw>
<?php
if (basename ($_SERVER[1 PHP_SELF,])—七ead.php”)
{
?>
<link rel-11 StyleSheet" type="text/css** href=r'<?php echo $skin; ?>read,C3sw>
<?
}
?>
</head>
<?
include $skin."diztop.php";
?>
Как видно из листинга, к файлам index.php и read.php обязательно подключаются стилевые таблицы из файлов fbmmxss и mainstylesxss. Затем происходит проверка названия текущего файла — если оно соответствует файлу read.php, то подключаются стилевые таблицы нз файла read.css. После стилевых таблиц подключается файл с оформлением — diztop.php* Для определения пути перед каждым файлом со стилевыми таблицами и файлом diztop.php включается переменная $skin. Именно благодаря ей становится возможным применение скинов. Значение переменной берется из настроек форума и содержит путь к каталогу с текущим скином. Для базового скина значение этой переменной равно и.. /skins/base/". Изменение этой переменной изменяет текущий скин форума. Также переменная $skin участвует в формировании путей ко всем изображениям (теги <img>) во всех файлах форума.
Перед тем как приступить к рассмотрению стилевых таблиц, рассмотрим стили, которые в ннх применяются. Свойства семейства css представлены в табл. 20.1.
Таблица 20.1. Применяемые свойства css
Название свойства
Описание
font-family
Назначение семейства шрифтов
color
Цвет текста
font-size
Размер шрифта
Font-style
Начертание шрифта: нормальное, курсивное или наклонное (normal Jtalic, oblique)
fonbweight
Толщина шрифта: нормальный, полужирный (normal, bold)
text-indent
Отступ первой строки
text-align
Выравнивание текста влево, по центу, вправо (left, center, right)
padding
Отступ элемента снаружи от контейнера для всех 4 сторон. Также можно указать отступ для каждой стороны: padding- top, padding-left, padding-right, padding-bottom
margin
Отступ элемента внутри контейнера от 4 сторон последнего. Также можно указать отступ для каждой стороны: margin-top, margin-left, margin-right, margin-bottom
border-style
Стиль границы контейнера, в который заключен элемент. Возможные значения: dotted, dashed, solid, double
border-width
Толщина границ контейнера, в который заключен элемент. Возможно указание толщины границ отдельных сторон: border-top-width, border-left-width, border-rightwidth, border-bottom-width
Таблица 20.1 (окончание)
Название свойства
Описание
border-color
Цвет границ контейнера, в который заключен элемент. Возможно указание цвета границ отдельных сторон: border-top- color, bordepleft-color, border-righbcolort bordeг-bottom-color
background-color
Цвет фона элемента
background-image
Определяет фоновое изображение элемента
backg round-re peat
Тиражирование изображения, являющегося фоном. Возможны варианты: не тиражировать, тиражировать по оси X, тиражировать по оси Y (no-repeat, repeat-x, repeat-y). Если данное свойство не установлено, то фановое изображение тиражируется по осям X и Y
background-position
Если фоновое изображение определено, то данное свойство указывает его исходное положение относительно его контейнера
width
Ширина элемента
height
Высота элемента
top
Вертикальное смещение от верхнего края абсолютно позиционированного элемента
left
「оризонтальное смещение от левого края абсолютно позиционированного элемента
bottom
Вертикальное смещение от нижнего края абсолютно позиционированного элемента
right
Горизонтальное смещение от правого края абсолютно позиционированного элемента
float
Позиционирует элемент рядом с левой или правой границей его контейнера и разрешает обтекание элемента текстом
Далее рассмотрим содержание стилевых таблиц. В описании стилей, по возможности, будет указано, для каких элементов используется данный стиль, где эти элементы расположены и как оформлены.
[ Замечание )
Дизайн и стилевые таблицы рассматриваются на примере скина "base". Каждый скин определяет свое оформление форума, в том числе цвет, форму и по* ложение отдельных элементов. Поэтому описание стилей, где обозначено по-
ложение, форма и цвет элементов, может не совпадать с соответствующими свойствами других скинов. Общими для всех скинов являются название стилей и элементы (теги), к которым они относятся.
( Замечание )
Пояснения к стилям даны непосредственно в листинге после символов //, Следует помнить, что формат стилевых таблиц не поддерживает подобное комментирование. Комментарии в данном формате применены только для удобства чтения листинга.
Файл fbrum.css содержит стилевые таблицы» общие для всех файлов форума (листинг 20.32),
飽処眦般驅叫н弊 XAUAHHaHlttHiiMiWMaMttMHMftMWfliiiMMiftW«MMMIIfMmft*MfM*ffMfifMIMIWMIifty竺MMaMfcmw^Ewi^MMM^WilliHMMY*1 Г Листинг 20.32. Файл forum.css.
// Стиль параграфа
Р<
font-family: Arial, Helvetica, sans-serif; font-size: 12px}
// Цвет для всех ссылок
а{
color: #19308С}
а:hover{
color: #010103}
// Стиль текста пояснений и примечаний
•texthelp{
color: #5В5В5В; margin: Орх}
// Стиль для имен зарегистрированных авторов
・ authorreg(
font-size: 12рх; font-weight: bold; color: #446343;
padding-left: Юрх; padding-right: 10px}
// Стиль для имен зарегистрированных авторов,
// применяется только к тегам ссылок
а.authorreg{
font-size: 12рх; font-weight: bold; color: #446343;
paddxng-left: Opx; padding-right: Opx)
// Стиль хотя имен зарегистрированных авторов
// Цвет подсветки ссылок при наведении на них указателем мыши
а.authorreg:hover{
color: #182D0B}
// Стиль для иь^н незарегистрированных авторов
*author{ font-style: normal; font-size: 12px; color: #446343; padding-left: lOpx; padding-right: lOpx)
〃 Стиль кнопок <input type=submit>
.but ton {
font-size: llpx; color: #264973; background-color: #D6E1E2; padding: Ipx; padding-left: lOpx; padding^right: lOpx)
// Стиль примеров кода в сообщениях .codeblock{
font-size: 12рх; background-color: #E3E5E3;
background-image: url(images/code2.gif); background-repeat: repeat^у;
border-style: solid; border-width: ipx; border-color: #B8C1B7;
padding: lOpx; padding-left: 35px)
Рис. 20.10. Карта стилей для страницы index.php
Файл mainstyles.css содержит содержит стилевые таблицы для страницы index.php, стили для "山апкм" и окончания страниц форума (листинг 20.33). ''Шапка" и окончание страниц форума являются общими для файлов index.php и read.php. На рис. 20.10 представлена карта стилей для страницы index.php.
Й«жай^
// Класс для ячеек с названиями столбцов таблицы списка тем // (страница index・php) .headertable{
text-align: center; height: 35px; background-color: #ADC0D0; border-bottom-style: solid; border-bottom-width: 2px;
border-bottom-color: #AFBDC0;
border-left-style: solid; border-left-width: Ipx; border^left-color: #F0F0F0;}
// Класс для ячеек самой нижней строки таблицы списка тем
// на странице index.php. (Нижняя строка содержит ссылки // на номера тем) .bottomtabletета(
height: 40рх; background-соlor: #F8F8F8; border-top-style: solid; border-top-width: Ipx; border-top-color: #D3D3D3;
border-bottom-style: solid; border-bottom-widthi lpx; border-bottOTn^color: #C1C1C1;
padding-left: 20px; padding-top: 5px; padding-bottom: 5px}
// Имя форума, выводимое большими буквами в верхней левой части 〃 страницу ,name f orum (
font-size: 32px; font-style: italic;
margin: Opx; padding: Opx; padding-bottom: 5px; padding-left: Opx} // Стиль для номеров тем, страница с которыми открыта на // текущий момент. Номера тем расположены в последней строке // та&лицы списка тем* em.currentpage(
font-size: 14рх; font-weight: bold; font-style: normal; color:用737780} // Описание форума — расположено под названием форума.
* aboutforum {
color: #3D6275; margin: Opx; margin-left: 30px; padding-bottom: 15px}
// Светло-серая линия в нижней части шапки форума. Лежит поверх голубой // широкой полосы
•dizline{
height: ЗОрх; background-image: url(images/diz line.gif); background-repeat: repeat-x; margin-bottom: 5px}
/ / Класс для слоя (div), содержащего форму для переключения
// между разделами форума
.switchforumdiv{
position: absolute; top: 145px; Left: 55*}
// Стиль для формы переключения между разделами форума .switchforumf
background-соlor: #FFFBC8;
background-image: url (images /yellowfon .gif);
background-repeat: repeat-x;
border-style: solid; border-width: Ipx; border-color: #BFB98D; padding: 10px}
// Стиль "шапки" форума — широкая синяя полоса ,diztabletop{
height: 125px;
background-color: #6DCFF6; background-image: url(images/bluedot.gif);
background-position: left; width: 100%;
border-bottom-style: solid; border-width: Ipx; border-color: #95BCCB;
border-top-style: solid; horder-top-color: #84CCF2; margin-top: Opx}
// Фоновый рисунок на шапке форума,
// Далее отображаются рожицы
• images{
position: absolute; top: 60px; left: 30%; width: 200px; background-image: url(images/faces?*gif);
background-repea11 no-repeat}
/ / Приветсвие посетителям 中орума в пшапке11 страниц .salutation{
font-style: italic; color: #000000;
margin: Opx; margin-bottom: 15px; font-weightt bold; padding-left: 30px}
// Стиль ячейки, в которой находится весь список тем
// (страница index.php) и все сообщения (страница read.php)
// В данном случае определяются ширина и отступ от границ окна браузера *bodydiv{
width: 95%; padding: 25px; padding-top: 15px; margin: Opx}
// Стиль имени темы в таблице списка тем (страница index.php) .nametemaf
color: #084E82; padding-left: Юрх; padding-right: 10px}
// Стиль имени новой теьлы в таблице списка тем (страница index.php) ・ namenewtema {
color: #081272; padding-left: lOpx; padding-right: 10px}
// Стиль для строки таблицыr в которой расположена тема
.trtema{
background-color: #ееееее;}
// Высота строки таблицы, в которой расположена тема .trtemaheight{
height: 27р*}
// Стиль таблицы списка тем. Определяет фон и бордюры
.temamenu{
background-color: #B9B9B9;
border-top-style: solid; border-width: Ipx; border-color: #5C8EB3}
// Стиль, определяющий оформление ячеек, в которых находятся
// названия разделов форумов (в верхней части страниц index.php и
// read.php).
.numberforum{
font-family: Arial, Helvetica, sans-serif; font-size: llpx;
color: #44657A; background-color: #7BCCEB;
border-style: solid; border-right-style: none; horder-width: 2px;
border-color: #81D7F8; border-bottom-color: #77C5E3 padding-left: 5px)
// Определяет стиль 2-х крайних ячеек таблицы с разделами форумов
// (в верхней части страниц index.php и read*php.
// Ячейки содержат слова - "Форум1' и 11 Новые сообщения11
• ni^nberheader {
font-family: Arial, Helvetica, sans-serif; font-size: llpx;
text-align: right; color: H76B83; background-color: #74C0DD;
border-style: solid; border-right-style: none;
border-width: 2px; border-color: #77C6E4;
border-bottom-color: #6DB5D0; padding-left: 2px}
// Стиль для названий столбцов таблицы списка тем
• fieldnameindex{
font-size: llpx; font-weight: bold; color: #768799}
// Стиль для параграфов, куда входят тематические
// ссылки в самом верху страницы — IT-студия SoftTime, SoftTime~Info# // Разработка ПО и др.
・ ptoplinks{
font-size: llpx; color: #B3B3B3; margin: Opx}
// To же, что и предыдущий стиль, но для тегов <а>
a.topiinks{
color: #ВЗВЗВЗ;}
а・ toplinks:hover{ color: #558BB3;)
〃 To 哀а, что и предыдущие стили, но для ячеек таблицы, // куда входят данные ссылки
td ・ toplinks{
height: 2Opx; background-image: url(images/topiinks.gif); background-repeat: no-repeat; background-position: left border-style: solid; border-width: Ipx; border-color: ttECECEC}
// Стиль на ссылки меню форума - 11 Новая тема1', "Вход",
// "Регистрация11, "Анонимно" и др.
.menu(
color: #A8130F}
.menu:hover{
color: #0F48A8}
〃 Стиль на информационные ссылки форума — иОбновить визиткуп, // г,Правила форума", ^Участники Online" и "Все участники". .menuinfo{
color: #0Е4ЕА5}
.menuinfo:hover{ color: #000000)
// Стиль для слоя, б который входит ссылка
〃 на IT-студию Softtime, как на разработчика форума
// Ссылка расположена в правом нижнем углу страницы* div.linksofttimef
float: right; font-family: verdana; color: #5F5D48;
font-size: lOpx; padding-right: 10px}
// Стиль ссылки на IT-студию Softtiroe, как на разработчиков форума .linksofttime{
font-family: verdana; color: #5F5D48;
font-size: lOpx; padding-righti lOpx }
Файл read.css содержит стили для страницы read.php (листинг 20.34). Карта стилей из файла read.css для страницы read.php приведена на рис. 20.1L
Рис. 20.11. Карта стилей для страницы read.phpРис. 20.11. Карта стилей для страницы read.php
Рис. 20.11. Карта стилей для страницы read.php
Рис. 20.11. Карта стилей для страницы read.php
^щд^|д^^ц|^^^^|^д^^^|^^^^^^^^^^д^^《Т 勺,,
:Листинг 20.”. Файл read.css
3№^^Ж^«тЛ^^^Л^«^^гО^^Го^в .嚥F^^ II I i 11 ■'?5'iV'ii^?»r»*^^"?iI 播
// Стиль на таблицу, которая содержит имя просматриваемой темы .readmenu{
border-top-style: solid; border-left-style: solid; border^width: lpx; border-color: #6B8699}
// Стиль названия просматриваемой темы
.namet ema read {
font-family: Arial, Helvetica, sans-serif; font-size: 12px;
font-weight: bold; font-style: oblique; color: #000000;
padding: 3px; padding^left: 20px)
// Стиль слоя/ в котором находится название просматриваемой темы div. nametema read«
float: left; margin: Opx}
// Стиль слоя, который содержит ссылки на предыдущую и
// следующую темы
div.nextback{
float: right; padding-right: 2Opx}
// Стиль параграфа, в котором находится ссьспка на предыдущую тему
р.linkback{
font-size: llpx; text-align: left;
background-image: url(images/backpage.gif);
background-repeat: no-repeat; background-position: left;
margin: Opx; margin-left: 2Opx; padding-left: 2Opx}
〃 Стиль ссыпки на предыдущую тему
а *linkback{
color: #21353D}
// Стиль параграфа, в котором находится ссылка на следующую тему
р.linknext{
font-size: llpx; text-align: left; color: #E3D41B;
background-image: url(images/nextpage•gif);
background-repeat: no-repeat; background-position: left;
margin: Opx; roargin^left: 60px; padding-left: 2Opx)
// Стиль ссылки на следующую тему
а.linknext f
color: #21353D)
// Стиль текста сообщений
.posttext{
font-size: 12рх; color: #000000; line-height: 16px}
// Стиль таблицг в которых выводятся сообщения
.postbody{
background-color; #F8F8F8}
// Стиль таблиц, в которых выводятся новые сообщения
,postbodynew(
baсkground-color: #FFFTFF}
// Стиль таблицы, в которую входят все таблицы с отдельными сообщениями.
.fonposts{
background-соlor: #D1D1D1;}
/ / Стиль ячейки таблицу, где в каждом сообщении присутствует информация
// об авторе и его сообщении: имя автора, время добавления сообщения,
// ссыпка ^Отправить письмо** автору.
.infopost{
font-family: Arialr Helvetica, sans-serif;
font-size: llpx; color: #969696}
// Стиль ячейки таблицы, где в каждом сообщении находятся ссылки
// "Ответить” и "Править сообщениен.
.postmenuj
font-family: Arial, Helvetica, sans-serif; font-size: 12px;
text-align: right; color: #969696}
// Стиль блока кода в сообщениях
• codeblock{
font-size: 12рх; baсkground-colог: #ЕЗЕ5ЕЗ;
background-image: url(images/code2.gif); background^repeat: repeat-y;
border-style: solid; border-width: lpx; border^color: #B8C1B7;
padding: lOpx; padding-left: 35px}
// Стиль слоя, который содержит ссыпку на файл,
// загруженный посетителем на форум (отображается справа)
•attachfilef float: right}
// Стиль на переключатели между структурным и линейным вариантами форума
* switchtypefonim{
width: ЮОрх; background-color: #F0F2F6;
border-style: solid; border-width: Ipx; border-color: #E0E2E5}
Файл action.css содержит стили, применяемые во всех оставшихся файлах (формы ввода тем, сообщений, отображения информации о посетителях и т. п.), за исключением файлов index.php, read.php и скриптов-обработ' чиков. Общими для данных страниц являются файлы topfbmmaction.php и
bottomfbrumaction.php — в них содержатся "шапка" и окончание страниц. Карта стилей action.css для страницы answer.php представлена на рис. 20.12. Код файла actionxss приведен в листинге 20.35*
Рис. 20*12. Карта стилей для страницы answer.phpРис. 20*12. Карта стилей для страницы answer.php
Рис. 20*12. Карта стилей для страницы answer.php
Рис. 20*12. Карта стилей для страницы answer.php
Листинг 20.35. Файл action.css
// Стиль имени посетителя .authortext{
font^size: 12рх; font-style: normal; font-weight: bold; color: #446343》 // Стиль для ячейки» в которой содержится название страницы .headerfопп {
height: 59рх; background-color: #84A4B3;
background-image: url(images/header.gif); background-repeat: repeat-x;
border-style: solid; border-width: Ipx; border-color: #A2A2A2;
border-top-color: #C4D2C6}
// Стиль тега hl, в котором находится название странищ^ .nameactiont
font-weight: bold; font-size: 16px; color: #ffffff;
text-align: center; margin: Opx; padding-top: 5рк}
// Стиль для ячейки, которая включает содержательную часть страниц .bodyfопп {
height: 200рх; background-color: #FFFFFF;
bac kg round-image: url(images/paper2.gif);
padding: 40px; padding-bottom: lOpx; padding-top: lOpx;
border-style: solid; border-width: Ipx; border-color: #C2C2C2;
border-top-width: Opx}
// Стиль для верхней части фона страниц
•topiineaction{
background-color: # 96D0E8;
background-image: url(images/fonactiontop.gif)}
// Стиль дая тега <body>
.bodyaction{
background^color: #ECECEC;
background-image: url(images/fonbodyaction.gif}}
// Стиль названия форума, к которому относится открытая страница.
// Название расположено в левой верхней части страницы
.narnepageaction{
font-family: Arial, Helvetica, sans-serif; font-size: llpx;
color: #4B6367; inargin-top: 20px}
// Стиль для блока комментариев, например, при добавлении новой темы
// в блоке комментариев находится список тегов офорьдления, которые может // использовать участник форума в своем сообщении .blockremark{
width: 100%; text-align: right; background-color: #F5F5F5;
border-style: solid; border-color: #CFCFCF; border-width: Ipx;
padding: Юрх; margin-left: 20px; margin-bottom: 10px}
// Стиль для текста комментариев 4 Используется в файле rules * php
П для вывода правил форума
.remark{
width: 100%; background-colог: #F5F5F5;
border-style: solid; border-color: #CFCFCF; border-width: Ipx;
padding: lOpx; margin-left: 20px; margin-right: 20px; margin-bottom: 10px}
// Стиль для параграфа, в который входит ссылка возврата
//на предадущую страницу либо на главные страницы форума р.linkbackbig(
font-size: 12рх; baсkground-image: url(images/backpagecolor-gif); background-repeat: no-repeat; background-position: left;
margin: Opx; margin-left: lOpx; margin^botton: 15px; padding-left: 2 Opx}
// Стиль ссыпки возврата на предыдущую страницу
// либо на главные страницы форума
а ・1inkbackbig{ color: #21353D}
// Стиль для параграфа, в котором содержатся названия полей ввода текста ,fieldname{
font^weight: bold; font-size: llpx; color: #768799}
// Стиль для тегов <input>
* input{ font-family: Arial, Helvetica, sans-serif; font-size: 12px; background: #FFEDE6; background-color: #FFFDE6}
// Применяется в файле answer.php.
// Стиль для блокаf в котором содержится сообщение,
// на которое отвечает посетитель
.blockanswer{
width: 100%; background-color: #ECFBFF; padding: Юрх;
border-style: solid; border^width: Ipx; border^color: #000000}
// Применяется на страницах authorslist.php и online.php
// Стиль для таблицы с именами участников форума .tablen{
background-color: #B9B9B9;
border-top-style: solid; border-width: lpx; border-color: #5C8EB3}
// Применяется на страницах authorslist,php и online,php
// Стиль для строк таблицы, в которой выводятся имена участников форума
•trtablen{
background-color: #FFFEF8}
// Применяется на страницах authorslist.php и online.php
// Стиль для ячеек, в которых содержатся названия столбцов таблицы
tableheadem {
text-align: center; background-color: #ADC0D0;
border-bottom-style: solid; border-bottom-width: 2px;
border-bottom-color: #AFBDC0;
border-left-style: solid; border-left-width: Ipx;
border-left-color: tEAEAEA}
// Применяется на страницах authorslist.php
// Стиль для последней строки таблицу, в которой содержатся ссылки
//на следующие страницу с участниками форума
.bottomtablen{
background-color: #F8F8F8;
border-top-style: solid; border-top-width: 2px;
border-top-color: #F0F0E9;
border-bottom-style: solid; border-bottom-width: Ipx;
border^bottom-color: #C1C1C1;
border-left-width: Opx;
padding-top: 3px; padding-left: 20px}
// Стиль имени посетителя. Применяется на странице online.php
// для отображения участников форума, посещавших его более
// 5 минут назад
.authorhide{
color: #888888}
// Стиль, применимый для решения задач верстки HTML-кода
pic{
font-size: Ipx; margin: Opx; padding: Орх}
//〃
// Нижеприведенные стили применяются
// на странице поиска 一 srchform.php
////
// Применяется на странице srchform.php.
// Стиль для таблицы с результатами поиска
.srchtable{
background-color: #В9В9В9;
border-top-style: solid; border-width: Ipx; border-color: #5C8EB3}
// Применяется на странице srchfonn.php.
// Стиль для ячеек таблицы, в которой находятся заголовки столбцов .headertable{
text-align: center; height: 35px; backgrcund-coloE: #ADC0D0;
border-bottom-style: solid; border-bottom-width: 2px;
border^bottom-color: #AFBDC0; border-left^style: solid;
border-left-color: #F0F0F0; border-1eft-width: ipx}
// Применяется на странице srchform. php.
// Стиль для параграфов, в которых содержатся названия столбцов таблицы .fieldnameindex{
font*-weight: bold; font-size: llpx; color: #7687 99} // Применяется на странице srchform.php.
// Стиль для строк, в которых содержатся названия найденных тем .trtema(
background-color: #ееееее}
// Применяется на странице srchform.php.
// Стиль последней строки таблицы с найденными темами.
// В последней строке содержатся номера найденных тем * bottomtabletema {
height: 40рх; baсkground-соlor: #F8F8F8;
border-top-style: solid; border-top-width: Ipx;
border-top-color: #D3D3D3; border-bottom-style: solid;
border-bottm-width: Ipx; border-bottom-color: #C1C1C1;
padding-left: 20px; padding-top: 5px; padding^bottom: 5px}
// Стиль для параграфа; б нем содержится текст нРезультаты:" .zagtext{
font'-size: 14рх; font-weight: bold;
margin-top: 15px; margin-bottom: 10px}
// Применяется на странице srchform.php.
// Стиль для сообщения о том, что поиск завершен, но ничего не найдено .result{
text*align: center; color: #920CQC}
Адрес; J ④ htt.p;//www .softtlme.m/foHMn/wcWonn.phpАдрес; J ④ htt.p;//www .softtlme.m/foHMn/wcWonn.phpФайл [^завка 艮нд Избранное Сирене СправкуФайл [^завка 艮нд Избранное Сирене Справку鶴 Поиск на форуме - Mkrosolt Interwt Explorer鶴 Поиск на форуме - Mkrosolt Interwt ExplorerМедиа 干Медиа 干Поиск на форуме ;Поиск на форуме ;«<a卽曲0④1
Адрес; J ④ htt.p;//www .softtlme.m/foHMn/wcWonn.php
Адрес; J ④ htt.p;//www .softtlme.m/foHMn/wcWonn.php
Файл [^завка 艮нд Избранное Сирене Справку
Файл [^завка 艮нд Избранное Сирене Справку
鶴 Поиск на форуме - Mkrosolt Interwt Explorer
鶴 Поиск на форуме - Mkrosolt Interwt Explorer
Медиа 干
Медиа 干
Поиск на форуме ;
Поиск на форуме ;
Нмчвт1m ",皿 |php*mysqLdii
Копнчп««»
Пп1Е|>днпы)1)eu I
Mcharii в„I в сообщениях—三]
llciuib n 4ivpyu«?... |PHP
По«*«мG ИЛИ Г И
class-zagtextРезультаты:?Н*"\г*м>**ч *•/¥■』cia&£=be edertabieа 1 clas/Geldnamelndex •MYSCL 凯,€弓弓 dentedbsisk"20 04-09-16 8 14,09-叫??,ft# f ДйУГОЙКЫUffiE2004-QM5B 13:57■К^шддлль уравнение в загюм&-к rd?囹2004-08*14 в 17;20门叫 пт £|>1Ш грации njweui^ia^ЦщР2004-08-14 »15:51Сообщения p-*jclass » boilomtab}etemaclass-zagtextРезультаты:?Н*"\г*м>**ч *•/¥■』cia&£=be edertabieа 1 clas/Geldnamelndex •MYSCL 凯,€弓弓 dentedbsisk"20 04-09-16 8 14,09-叫??,ft# f ДйУГОЙКЫUffiE2004-QM5B 13:57■К^шддлль уравнение в загюм&-к rd?囹2004-08*14 в 17;20门叫 пт £|>1Ш грации njweui^ia^ЦщР2004-08-14 »15:51Сообщения p-*jclass » boilomtab}etemaРис. 20.13. Карта стилей для файла srchform.php
class-zagtext
Результаты:
?Н*"\г*м>**ч *•/¥■』
cia&£=be edertabie
а 1 clas/Geldnamelndex •
MYSCL 凯,€弓弓 dented
bsisk"
20 04-09-16 8 14,09
-叫??,ft# f ДйУГОЙКЫ
UffiE
2004-QM5B 13:57
■К^шддлль уравнение в загюм&-к rd?
囹
2004-08*14 в 17;20
门叫 пт £|>1Ш грации njweui^ia^
ЦщР
2004-08-14 »
15:51
Сообщения p-*jclass » boilomtab}etema
class-zagtext
Результаты:
?Н*"\г*м>**ч *•/¥■』
cia&£=be edertabie
а 1 clas/Geldnamelndex •
MYSCL 凯,€弓弓 dented
bsisk"
20 04-09-16 8 14,09
-叫??,ft# f ДйУГОЙКЫ
UffiE
2004-QM5B 13:57
■К^шддлль уравнение в загюм&-к rd?
囹
2004-08*14 в 17;20
门叫 пт £|>1Ш грации njweui^ia^
ЦщР
2004-08-14 »
15:51
Сообщения p-*jclass » boilomtab}etema
;Листинг 20.36. Файл operate.css;Листинг 20.36. Файл operate.cssСтили из файла operate.css применяются для вывода ошибок, сообщений и предупреждений в с кр и птах- обработч и ках (листинг 20.36).
;Листинг 20.36. Файл operate.css
;Листинг 20.36. Файл operate.css
// Стиль для информационных сообщений
.result{color: #920С0С; text-align: center}
// Стиль для отображения ошибок выполнения скриптов .bug{color: #920С0С; text-align: center}
Глава 21
Создание системы администрирования
Система алм ин и стрирован ия предназначена для создания и обновления, как независимых форумов, так и разделов форума. На страницах системы администрирования также осуществляется модерирование сообщений форумов и их общая настройка.
Описание системы администрирования
Система ад ми н и стр ирова н ия форума состоит из нескольких страниц и предназначена для выполнения административных задач: инсталляции форума, управления разделами форума, модерирования, предоставления статистики, информации об участниках форума и установки глобальных настроек форума. Для каждой из этих задач предусмотрена отдельная страница, перейти на которую можно по ссылкам, расположенным в меню системы администрирования.
Страница администрирования форума
Внешний вид страницы администрирования форумов представлен на рис. 2LL Главная задача страницы администрирования — инсталляция новых и удаление уже существующих форумов. Для инсталляции форума необходимо ввести параметры соединения с базой данных.
Первое поле формы (см. рис. 21.1) предназначено для сетевого адреса сервера MySQL. В качестве такого адреса может выступать localhost, если сервер расположен на локальной машине, или сетевой адрес вида mysqll.mysite.ru, если он расположен на отдельном узле в сети.
Во втором текстовом поле помешается имя пользователя, который осуществляет работу с базой данных (на локальной машине, если не проводилась дополнительная настройка учетных записей, в качестве имени пользователя следует набрать ’Toot").
Третье текстовое поле предназначено для ввода пароля пользователя базы данных. Если после инсталляции сервера MySQL на локальной машине пароль для пользователя root не менялся, это поле следует оставить пустым.
Четвертое поле необходимо для задания имени создаваемой базы данных* В случае, если база данных для форума уже создана, то ее имя также должно быть вписано в это поле.
Последнее поле задает название директории, в которую будет помещен форум. Если данное поле оставить незаполненным, имя директории совпадет с именем базы данных. Допустим, файлы форума расположены в папке forum. Адресом Web-сервера является www.mysite.ru. тогда адресом форума на данном сайте будет www.mysite.ru/fbrum/. В этом случае в пятое поле следует вписывать имя директории — forum.
После заполнения всех полей необходимо нажать на кнопку Создать форум. В случае успешного создания форума внизу страницы будут выведены информационные сообщения м 2 ссылки, по первой можно будет попасть на только что созданный форум, а вторая ссылка перегружает страницу администрирования для дальнейшей настройки форума.
Результатом создания форума будет новая строка в таблице на странице администрирования форума. Данная таблица позволяет удалить форум или выбрать его для дальнейшей настройки на других страницах.
( Замечанме )
В системе может быть создано несколько независимых форумов, каждый из которых будет иметь собственную базу данных. Для работы с отдельным форумом в таблице на странице администрирования форума следует перейти по ссылке Выбрать напротив нужного форума.
Страница Разделы форума
Страница Разделы форума показана на рис. 21.2. Существуют два подхода к работе с форумом Lite Forum:
口 создание нескольких независимых форумов, каждый из которых имеет собственную базу данных н отдельную директорию;
口 создание на базе одного форума нескольких разделов с возможностью переключения между ними при помощи выпадающего списка. Этот подход удобен, когда количество используемых баз данных ограничено.
Страница администрирования Разделы форума предназначена для управления разделами форума. Переход по ссылке Добавить новый раздел приводит к форме, заполнив поля которой, можно организовать новый раздел форума. Посетители смогут переключаться на него при помощи выпадающего списка или ссылок в верхней части форума.
Замечанье )
По умолчанию при создании форума создается лишь один раздел: Общий фоРУМ.
Каждому разделу форума соответствует строка таблицы, расположенной после ссылки Добавить новый форум. Таблица состоит из восьми столбцов, первые четыре из которых отображают параметры форума: название, правила форума, краткое описание (выводится на главной странице форума) и порядок следования в меню и выпадающем списке. Следующие четыре столбца содержат управляющие ссылки: Скрыть/Отобразить. Удалить, Исправить и Переместить, позволяющие скрывать и отображать раздел, удалять его из системы вместе со всеми сообщениями, редактировать название, правила, описание п порядок следования и объединять темы раздела с темами другого раздела.
Страница Модерирование форума
На данной странице осуществляется модерирование форума. При загрузке страницы выводится таблица с темами форума. Имеется возможность редактировать название темы и имя автора. Кроме того, тема может иметь три статуса:
Открытая (досгупная для просмотра и добавления сообщений посетителями);
П Скрытая (недоступная для просмотра и добавления сообщений посетителями);
Закрытая (тема доступна для просмотра, но посетители не имеют возможности отвечать на сообщения и продолжать дискуссию).
Переход по ссылке в виде названия темы приводит к структурированному содержанию темы, которое разбивается на сообщения (рис. 21.3)* Каждое сообщение может быть отредактировано (для этого необходимо перейти по ссылке Править) и, как и в случае с темами, предоставляется возможность х задания сообщению трех приведенных выше статусов.
Рис. 21.3. Страница Модерирование форума
Страница Статистика форума
На данной странице предоставляется статистическая информация по выбранному форуму (рис. 21,4). В первой таблице приводится количество зарегистрированных посетителей. Во второй таблице приводится количество доступных, закрытых и скрытых тем и сообщений форума.
Страница Посетители форума
Страница Посетители форума показана на рис. 21.5. На данной странице имеется возможность задать статус участника форума (ссылки Посетитель, Модератор. Администратор).
Кроме этого, о каждом из участников форума предоставляется статистическая информация: число оставленных нм сообщений и дата последнего посещения форума. Имя посетителя является гиперссылкой, переход по которой приводит к открытию страницы с контактной информацией.
Рис. 21.4. Страница Статистика форума
: Замечание )
Помимо контактной информации, в ^портрете" посетителя приводится его пароль. Это предусмотрено на тот случай, если посетитель забудет пароль и обратится за ним к администратору.
Страница Настройки форума
Страница Настройки форума показана на рис. 21*6. На данной странице производится установка разнообразных параметров выбранного форума.
Первое текстовое поле задает название форума, которое выводится в области заголовка окна браузера и на некоторых страницах форума.
Второе текстовое поле определяет количество выводимых на одной странице тем (изменение этого значения отражается как на блоке представления форума, так н на странице администрирования).
Рис. 21.6. Страница Настройки форума
Следующие два поля определяют максимальный размер прикрепляемых к сообщению файлов н фотографий посетителей (в байтах).
Пятое текстовое поле задает приветственное выражение, которое выводится перед списком тем на главной странице форума.
Шестое текстовое поле определяет срок действия cookie в сутках. Данный параметр, определяет, сколько времени форум будет "помнмть” имя посетителя и его пароль без повторной аутентификации*
После этого идет группа флажков, первый из которых определяет, выводятся или нет переключатели между структурным (ступенчатым) и линейным (последовательным) видом форума.
Второй флажок необходим для сокрытия и отображения инструментов переключения между форумами (выпадающий список и верхнее меню с числом новых сообщений в разделах форума)* Данный флажок необходимо сбросить в том случае, если в форуме имеется только один раздел.
Последний флажок сообщает, требуется ли отправлять уведомление по e-mail при появлении новой темы на форуме. Следующее за ним текстовое поле определяет e-mail, на который необходимо переправлять сообщение.
Выпадающий список в конце формы позволяет выбрать скин форума (вариант дизайна).
Разработка системы администрирования
Все файлы системы админмстрирования расположены в папке admin:
О addpartfbrm.php — HTML-^форма для добавления нового раздела в форум;
口 addpart.php — обработчик формы addpartform.php;
口 authorslist.php — страница управления аккаунтами зарегистрированных посетителей;
bottomfbnjm.php — мшапка" страницы адм и н истри рования;
口 changpartfornrphp ——HTML-форма для объединения одного из разделов форума с другим;
changpart.php — обработчик формы changpartfbrm.php;
口 createconfig.php ——скрипт создания конфигурационного файла для нового раздела форума;
createfbrum.php — скрипт создания нового раздела форума;
createtables.php — скрипт развертывания таблиц в базе данных;
delpart.php — скрипт удаления раздела форума;
口 delunit.php — скрипт удаления форума;
editpartfbrm.php — HTML-форма для редактирования параметров раздела форума;
口 editpart.php ——обработчик формы editpartform.php;
editpostfdrm.php ——HTML-форма для редактирования сообщения на странице модерирования;
editpostphp — обработчик формы editpostfbrni.php;
editihemefbrni.php — HTML-форма для редактирования названия темы форума;
editthem^php — обработчик формы editthemeform.php;
hidepartphp — скрипт, позволяющий скрыть раздел форума;
口 hidepost.php — скрипт, позволяющий скрыть сообщение на странице модерирования;
hidetheme.php ——скрипт, позволяющий скрыть тему на странице модерирования;
П index.php — главная страница системы администрирования;
口 info.php — скрипт, генерирующий страницу с контактной информацией зарегистрированного посетителя;
lockpost.php —— скрнпт, позволяющий закрыть сообщение;
О locktheme.php ——скрипт, позволяющий закрыть тему;
П menu.php ——вспомогательный файл, содержащий меню системы администрирования;
paits.php —— страница администрирования разделов форума;
П posis.php — страница модерирования темы;
settings.php — страница глобальных настроек формы;
口 set.php — скрипт, устанавливающий настройки форума;
口 setadmin.php ——скрипт, позволяющий назначить посетителю статус "Ад- министратор*1;
口 setmoder.php —— скрипт, позволяющий назначить посетителю статус "Модератор";
口 seiuserphp ——скрипт, позволяющий назначить посетителю статус "Обычный посетитель4;
О showpart.php ——скрипт, позволяющий отобразить раздел форума;
口 showpost.php ——скрипт, позволяющий отобразить сообщение;
口 showtheme.php ——скрипт, позволяющий отобразить тему;
口 statistics.php ——страница, предоставляюшая статистическую информацию по форуму;
themes.php — страница модерирования форума;
口 topfbrum.php — "завершение11 страницы администрирования форума.
1.2.1. Страница администрирования форума
Рассмотрение системы администрирования будет начато со страницы администрирования форума (см. рис. 21.1), за отображение которой несет ответственность файл index.php. Содержимое файла index.php представлено в листинге 21.1,
:«^ч^’;^^謳銀箪:^^i^^y2^^^^^t^^*^jtfj^; i Листинг 21 Л. Файл Index.ph };. …三 …%" .布".…г;工;干不 “g:审 " ;Xm. .;?九 ;" , 超工
/////////////////////////////////////////////////////////////
// Блок поиска ранее созданных форумов
〃〃/〃〃//〃/〃〃〃〃/〃〃〃/〃〃〃〃〃〃〃/〃〃//〃〃〃〃〃
// Анализируем и помещаем в массив $confname имена
// конфигурационных файлов (они совпадают с именами форумов) г
// подсчет производим по числу файлов с расширением inc
// в директории admin
$ admin = opendir (rt /'); 〃 Открываем текущую директорию
while《($file = readdir($admin)))
if(is fiie($file))
$ext = substr($file, strpos($file, '\ °));
if($ext — ”.inc")
{
// В массив $ conf name помещаем имена всех
/ / конфигурационных файлов
$confname[] = substr($file, 0, strpos($file, ”・”));
)
}
}
// Закрываем директорию
closedir ($admin);
if (isset ($_POST [1 forum11)) $_GET [1 forum1 ] = $_POST【* forum'];
// Если форум не выбран - выбираем первый
if(en^ty ($ GET[1 forum1]) && count($confname)>0)
帛 _GET [1 forum1 ] = $ conf name [0];
}
// Устанавливаем соеданение с базой данных,
// если файл config.inc уже создан
if (@file_exists ($_GET[1 forum1 ] *'\1пс") && en^>ty($_POST[ 'dblocation1 ]))
{
reqtiire_once ($_GET「forum1 ]. °. inc");
$dir = $_GET[fforum1];
)
// Если соеданение невозможно установить/ данная переменная
// примет значение false
$connect = true;
// Выводам "тапку" страницы
include Mtopforum.php";
// Выводим таблицу с обнаруженными в системе форумами
echo "<р class=help>B системе обнаружены следующие форумы: </р>п;
?>
<table>
<tr>-
<td>Ha звание</td>
<td co1span^2 >Действия</1d>
</tr>
<?php
If(count($confname)>0)
(
foreach £ $confname as $value)
{
echo l,<trxtd>$value</td>
<td align=center><a href=index*php?forum=$value>Bbi6paTb</a></td>
<td align=centerxa href=delunit, php?forum=$value>yflajiMTb</a> v/td></tr>";
}
}
echo rl</taЫe>',;
/////////////////////////////////////////////////////////////
// Блок создания нового форума
/////////////////////////////////////////////////////////////
?>
<form action^index.php method=post>
Адрес сервера MySQL
〈input type=tiext name=dblocat ion value^<?php echo $dblocation; ?»<br> Пользователь :
〈input type=text name=dbuser value=<?php echo $dbuser; ?»<br>
Пароль : <input type-text name^dbpasswci value=<?= $dbpasswd; ?»<br> Имя базы данных :
<input typ«=text name-dbname values=<?php echo $dbnaine; ?>><br>
Имя директор]Имя директор]:<input type=text name=dir value=<?php echo $dir; ?»<br>
Имя директор]
Имя директор]
$ GET[»forum1!; ?»$ GET[»forum1!; ?»< input class^button type=submit value=r* Соз дать ,r>
$ GET[»forum1!; ?»
$ GET[»forum1!; ?»
<input type=pidden name^forum value=<?php echo </form>
<?php
данныхданныхпожалуйста,пожалуйста,// Проверяем корректность соединения с базой
данных
данных
пожалуйста,
пожалуйста,
if(!$connect)
echo ,fКойфигурационный файл был создан,
соединение установить не удалось;
обратитесь к <a href-http://www.softtime*ru/forum/> разработчикам</a>.";
// Обработчик HTML-форлы, расположенной выше if(!empty(^_POST[1dblocation11) &&
J empty($_POST[1dbuser *]) &&
!empty(Ь POST I1dbname']))
// Устанавливаем соединение с сервером базы данных Slink = ;@mysql_cormect ($_POST (*dblocation'],
§_POSTUdbusaL],
$_POST f'dbpasswd1D;
echo 11 Невозможно установить соединение с сервером MySQL.
Проверьте правильность ввода адоеса сервера г имени и пароля пользователя・";
exit ();
)
echo мСоединение с сервером установлено.…”;
// Проверим, существует ли уже такая база данных
// Запрашиваем список существующих баз данных
$dblist = mysql_list__dbs ($link);
// Преобразуем его в массив и проверяем, не существует ли
// уже такая база данных
$db_exists = false;
〃 Определяем число баз данных на сервере $cnt = iriysql_num_rows ($dblist);
// В цикле проверяем, совпадает ли имя текущей базы данных
// с уже существующими
for ($i==0; $i < $cnt; $i++)
(
if (mysql_db_name ($dblist, $i) = $_POST[rdbname* ])
(
echo trВыбранная нами база данных уже существует
$db_exists = true;
break;
}
// Если база данных не существует - пытаемся ее создать
if (!$db__exists)
// Пытаемся создать базу данных, указанную в параметре dbname
if (@TnysqI_create_db ($_p0ST (• dbname * ]))
echo "Езза данных ”.$_POST['dbname*]-" успешно создана…•";
else
echo "Вазу данных ".$_POST[fdbname1]." не удалось создать•・・”;
// После того как база данных создана - получаем доступ к базе данных if (! @гауsql_sеlect_db ($_POST [1 dbname' 1, Slink))
echo MHe удалось получить доступ к базе данных
M.$_POST[ 'dbname*) . *\
возможно, пользователь "»$_POST[dbuser,]・” не имеет прав доступа для работы с ней.</р>";
exit О ;
} else echo иПолучен доступ к базе данных ”. $_POST [ * dbname'];
// Развертываем таблицы форума
require_once (Hcreatetables *phpH);
27 Зак, 1181
// Если переменная $create_table после развертывания
// таблиц принимает значение false - операция потерпела неудачу
if (! $create__table}
echo "He удалось развернуть таблицы в базе данных
".$_POST[ *dbname,]•"・.・ Возможно, они уже существуют•";
} else echo “Таблицы форума успешно развернуты в базе данных
n.$_POST[『dbname']•”・・・";
// Создаем конфигурационный файл для доступа к базе данных
// со страницы администрирования
require_once (иcreateconf ig. php1*);
echo "Создан конфигурационный файл системы администрирования. .. н;
// Создаем форум путем копирования файлов движка из директории skel //в директорию, заданную в текстовом поле "Имя директории" (dir), // если это поле пустое - директория получает имя базы данных requireonce (11 create forum. phpH);
/ / Выводим ссылку на новый форум
echo "Проверьте правильность создания
<а href^«. / н. $_POST [1 dir1 ] . ,г/>форума</а>и;
echo иДля дальнейшей работы перегрузите
<а href=index.php?forum^u.$_POST[1dir1].м>данную страницу</а></р>11;
}
?>
При создании нового форума в директорию admin добавляется конфигурационный файл, название которого совпадает с названием директории размещения форума. Файл имеет расширение inc. В начале скрипта, представленного в листинге 21.1, в текущей директории (admin) происходит поиск файлов, имеющих расширение inc,——их названия помещаются в массив $con£name*
Файл index.php может принимать параметр forum, в котором передается название текущего форума; если параметр пуст, он принимает значение первого элемента массива имен $confname.
Далее происходит соединение с базой данных посредством включения конфигурационного файла $_get [ * forumт ].n. incfl при помощи инструкции require once ().
Каждый конфигурационный файл с расширением inc совпадает по структуре с ранее рассмотренными файлами config.php (листинг 21.2). Единственное различие заключается в том, что при неудачном соединении скрипт не
останавливает работу, а присваивает переменной $connect5 которая по умолчанию имеет значение true, значение false.
( Замечание )
Такой механизм необходим для того, чтобы на других страницах администрирования детектировать ситуацию, когда текущий форум не выбран.
<?php
// Текущая версия Web-приложения
$version =丨2*。・ О';
// Имя сервера базы данных
$dblocation = 1localhost1;
// Имя пользователя базы данных
$dbuser =,root1;
// Его пароль
$dbpasswd = *';
〃 Имя базы данных
$dbname = f forum1;
〃 Устанавливаем соединение с сервером базы данных
$dbcnx = @mysq】_8nnect($dbl0catiorb $dbuser, $dbpasswd);
if (!$dbcnx)
{
echo *<p>B настоящий момент сервер базы данных не доступен, поэтому корректное отображение страницы невозможно.</р>1;
$connect = false;
}
// Выбираем базу данных
if (! @mysql_select_db ($dbnamer $dbcnx)) {
echo 1 <p>B настоящий момент база данных не доступна, поэтому корректное отображение страницы невозможно.</р>1;
$connect = false;
}
// Небольшая вспомогательная функция, которая выводит сообщение
// об ошибке в случае ошибки запроса к базе данных
function puterror($message)
echo * <p>$message</p>';
echo ,<p><b>Error: '. mysql_error().*</b><p>';
exit ();
} ?>
После соединения с базой данных в файл index.php (см. листинг 21.1) включается “шапка” страницы ——topfbrum.php. Файл topfbrum.php содержит оформление верхней части страницы и включает меню системы администрирования (menu.php)*
Затем, если массив $ conf пате содержит хотя бы один элемент, при помощи цикла foreach формируется таблица с присутствующими в системе форумами. Каждая строка таблицы соответствует отдельному форуму и состоит из трех ячеек, первая из которых содержит название форума, совпадающее с именем конфигурационного файла, и директории размещения, а две другие управляющие ссылки —— Выбрать и Удалить — позволяют назначить форум текущим в системе администрирования и удалить форум из системы.
Вслед за блоком формирования таблицы форумов расположен блок добавления нового форума, который включает HTML-форму и ее обработчик* Форма СОСТОИТ ИЗ ПЯТИ текстовых Полей: dblocation, dbuser, dbpasswd, dbname и dir для адреса сервера базы данных MySQL, имени пользователя базы данных, его пароля, имени базы данных и имени директории форума соответственно. В форме также присутствует кнопка для отправки данных обработчику» в качестве которого выступает сам файл index.php.
( Замечание )
Если в настоящий момент в системе администрирования выбран текущий форум, то в поля формы подставляются значения из соответствующего ему конфигурационного файла.
Добавление форума начинается в том случае, если суперглобальный массив $_POST содержит следующие элементы: dblocation, dbuser И dbname. Для этого осуществляется попытка установить соединение с сервером базы данных. Если соединение успешно установлено, при помощи функции mysqi_iist_dbs () возвращается список имен всех существующих баз данных. При помощи цикла for и функции mysql_db_name ()происходит обход списка баз данных. Если название введенной в HTML-форме базы данных совпадает с названием уже существующей —— выводится предупреждение, а флаг $db_exists, принимающий по умолчанию значение false, получает значение true.
Если база данных с именем» переданным в параметре dbname ($db_exists = false), не существует, предпринимается попытка ее создания при помощи
функции mysql_create_db (). Если попытка заканчивается неудачно — выводим предупреждение.
( Замечанье )
На серверах хост-провайдеров, пользователям запрещено создавать собст* венные базы данных, поэтому в качестве параметра dbname следует передавать имя уже существующей базы данных.
Независимо от результатов создания базы данных, при помощи функции mysqi_seiect_db () осуществляется ее выбор в качестве текущей. Если попытка заканчивается неудачей, выводится соответствующее предупреждение, а работа скрнпта останавл и вается.
После этого при помощи скриптов createtables.php, createconfig.php и createfbrum.php осуществляется развертывание таблиц форума в базе данных dbname, создание конфигурационного файла форума admin/dbname.inc и копирование файлов из директории skel в директорию, название которой совпадает с параметром dir.
Содержимое файлов createtables.php, createconfig.php и createforum.php представлено в листингах 21.3—21.5 соответственно.
№КЦШШШШШШим、,:^^Листинг 213 I < ж ^К^л lreV»ei№№КЦШШШШШШим、,:^^Листинг 213 I < ж ^К^л lreV»ei№<?php// Скрипт создания таблиц форума// Таблица с личными данными посетителей форума $query[] = "CREATE TABLE authors (id_author int(6) NOT NULL auto_increment, name tinytext NOT NULL, passw tiny text NOT NULL, email tinytext NOT NULL, url tinytext NOT NULL, icq tinytext NOT NULL, about mediumtext NOT NULL,<?php// Скрипт создания таблиц форума// Таблица с личными данными посетителей форума $query[] = "CREATE TABLE authors (id_author int(6) NOT NULL auto_increment, name tinytext NOT NULL, passw tiny text NOT NULL, email tinytext NOT NULL, url tinytext NOT NULL, icq tinytext NOT NULL, about mediumtext NOT NULL,
№КЦШШШШШШим、,:^^
Листинг 213 I < ж ^К^л lreV»ei№
№КЦШШШШШШим、,:^^
Листинг 213 I < ж ^К^л lreV»ei№
<?php
// Скрипт создания таблиц форума
// Таблица с личными данными посетителей форума $query[] = "CREATE TABLE authors (
id_author int(6) NOT NULL auto_increment, name tinytext NOT NULL, passw tiny text NOT NULL, email tinytext NOT NULL, url tinytext NOT NULL, icq tinytext NOT NULL, about mediumtext NOT NULL,
<?php
// Скрипт создания таблиц форума
// Таблица с личными данными посетителей форума $query[] = "CREATE TABLE authors (
id_author int(6) NOT NULL auto_increment, name tinytext NOT NULL, passw tiny text NOT NULL, email tinytext NOT NULL, url tinytext NOT NULL, icq tinytext NOT NULL, about mediumtext NOT NULL,
photo tinytext NOT NULL,
time datetime NOT NULL default 10000-00-00 00:00:00',
last_time datetime NOT NULL default ‘0000-00-00 0。:。。:001 themes int (10) NOT NULL default *0\
statususer enum(1 *,^moderator1,1admin1) NOT NULL default 1 PRIMARY KEY (id_author)
)TYPE=MyISAM1';
// Таблица с разделами форума $query[] = "CREATE TABLE forums (
id__forum int (6) NOT NULL auto_incrementr name text NOT NULL, rule mediumtext NOT NULL, logo mediumtext NOT NULL, pos smallint(6) NOT NULL default ・0) hide enum('show1,1 hide *) NOT NULL default "show\ PRIMARY KEY (id_forum)
)TYPE^MylSAM;";
// Создание раздела "Общий форум"
$query[] н "INSERT INTO forums VALUES (1, 'Общий форум', 'Правила', * Краткое описание форума *, 1, 'show')"; // Таблица с сообщениями форума $query[] = "CREATE TABLE posts (
id__post int (11) NOT NULL auto__increment, name text NOT NULL, url tinytext NOT NULL, putfile tinytext NOT NULL, author tinytext NOT NULL, id__author int (6) NOT NULL default '01 hide enum('show I 'hide1,1lock') NOT NULL default 'show', time datetime NOT NULL default 10000-00-00 00:00:001 parentjpost int(11) NOT NULL default 'O', id_theme int (11) NOT NULL default 'O', PRIMARY KEY (ijpost), FULLTEXT KEY search (name,author)
)TYPE=MyISAM;n;
/ / Таблица с настройками форума $query[] = "CREATE TABLE settings ( name_forum tinytext NOT NULL, number__themes int (3) NOT NULL default 101, size__file int(10) NOT NULL default O, size_photo int(10) NOT NULL default ‘0’, sendjmail enum(TyesT( Tnof) NOT NULL default 'no', email tinytext NOT NULL,
s how_s t ruet_swi t ch enum (* yes1,1noT) NOT NULL default 'yes', show_fоrum__switch enum(1 yes1, 1 no1) NOT NOLL default 'yes', hello tinytext NOT NULL, cooktime int (10) NOT NULL default ‘01
skin tinytext NOT NULL )TYPE==MyISAM;H;
// Настройки форума по умолчанию
$query[] = "INSERT INTO settings VALUES (*LiteForum 2.0.0*, 30, 824288, 824288, *no* f 'my@yandex・ru\ 'yes', 'yes', 1Доброго времени суток,1, 3, *base*);";
〃 Таблица с темами форума
$query[] = "CREATE TABLE themes (
idjheme int (11) NOT NULL auto_increment, name tinytext NOT NULL, author tinytext NOT NULL, id_author int(6) NOT NULL default ‘0。, hide enum(1show',‘ hide*,1lockf) NOT NULL default 1 show1, time datetiine NOT NULL default 10000-00-00 00:00:00', id__forum int(2) NOT NULL default *0', PRIMARY KEY (id_theme), FULLTEXT KEY search (name,author)
)TYPE^MyISAM;,,;
// Следующая переменная остается равной true, // если все запросы выполнены успешно $ createatable = true;
// Выполняем запросы в цикле foreach($query as $value) {
if(!@mysql_query($value)) $createatable = false;
В листинге 2L3 SQL-запросы, создающие базу данных форума, рассмотренную в главе 79, помещаются в массив $query. В конце скрипта SQL-запросы выполняются в цилке foreach. Если хотя бы один из запросов не выполняется, флаг $create_table, принимающий по умолчанию значение true, получает значение false.
■圖继?¥。,!^~‘韜*У£.;雌f,学饕SH生儲—^为С纯心舔&津冬生悔? ♦ ,./:.
I^wnt№*»l t'lUVfiii %0^!&s、n^rf.^VWllW«jT?*flflfTH<i Ai >>I^wnt№*»l t'lUVfiii %0^!&s、n^rf.^VWllW«jT?*flflfTH<i Ai >>:Листинг 21.4. Файл createconfig.php
I^wnt№*»l t'lUVfiii %0^!&s、n^rf.^VWllW«jT?*flflfTH<i Ai >>
I^wnt№*»l t'lUVfiii %0^!&s、n^rf.^VWllW«jT?*flflfTH<i Ai >>
<?php
if (empty ($_POST [1 dir1 ])) $_POST[ Mir • ] = $_POST [1 dbname1 ];
// Данный скрипт создает конфигурационный файл
// системы администрирования форума
$name 二"<?php
// Текущая версия Web-приложения
\$ version = *2.0.1,;
// Имя сервера базы данных
\$dblocation = 1 **. $_POST [1 dblocation']."';
// Имя пользователя базы данных
\$dbuser ^ ,n.$_POST[,dbuser']•”';
// Его пароль
\$dbpasswd ^ '"・ $_POST[1dbpasswd']•"';
// Имя базы данных
\$dbname ='"・ $_POST [ ^name* ]. п * ;
// Устанавливаем соединение с сервером базы данных
\^dbcnx = 0mysql_connect(\$dblocation# \$dbuser# \$dbpasswd); if (! \$dbcru<) {
echo 1 <p>B настоящий момент сервер базы данных не доступен, поэтому корректное отображение страницы невозможно.</р>1;
\$connect ^ false;
}
// Выбираем базу данных
if (! taysql__select_db (\$dbname, \$dbcnx)) {
echo *<p>B настоящий момент база данных не доступна, поэтому корректное отображение страницы невозможно.</р> *;
\$connect = false;
}
// Небольшая вспомогательная функция, которая выводит сообщение об
// ошибке в случае ошибки запроса к базе данных
function puterror(\$message)
(
echo * <p>\$message</p>,;
echo '<p><b>Error: 1.mysql_error().T</b><p>1;
exit ();
}
?>M;
$file = fopen($_POST[1dir•]> ° * inc1^ HwM);
fputs ($file, $name);
fclose($file);
chmod ($_POST[ • diL ] J ・ incr,, 0644 " ?>
Как видно из листинга 21.4» переменной $name в качестве значения присваивается содержимое будущего конфигурационного файла, параметры которого получают значения из формы. В конце скрипта в текущей дире кто* рии (admin) создается конфигурационный файл с расширением inc, права доступа к которому соответствуют 0664.
;Листинг 21.5.;Листинг 21.5.:逑М-"4^ 上*j^替*4;^ду1tj^f.^^yMy^^yfj^^^'^ff*!]^1^^^旨*,■^]^^^*|^ДJ!^yj|Д^l^|^^tЩf>Д>^^y 2^* 岁笠圉':# ⑴嗅涉火气疑**:俗整储我打CSH& Файл createforum.php:
;Листинг 21.5.
;Листинг 21.5.
<?php
// Создаем доректорию нового форума
@mkdir (".. /11 • $_POST [ • dir1 ] / 0700};
// Копируем из директории skel все файлы в директорию назначения
$skel = opendir (и. ./skel**);
while(($file = readdir($skel)))
(
// Если текущий объект является файлом - копируем его
// в директорию назначения
if(is^file(”.・/skel/".$file)) copy("・•/skel/"•$filef
”・./”・§_POST「dir'] •”/”•帛filB);
}
// Закрываем директорию skel closedir($skel);
// Для успешного функционирования форума создаем в новой
// директории поддиректории photo и files
@mkdir(H../H.$_POST[*dir,]."/photoH, 0700);
@mkdiL”_/L$_POSTrdir']「/files”,0700);
// Создаем конфигурационный файл нового форуъ^а
$name = 1<?php
$dblocation = "1.$_POST(1dblocation1]*f n;
$dbnanue = ” ・ $_POST ['dbname'].'";
$dbuser = ”'・ $_POST「dbuser*].»M;
$dbpasswd = *M .$_POST[*dbpasswd']•'";
// Устанавливаем соеданение с сервером базы данных
$dbcnx = @mysql connect ($dblocation, $dbuser, $dbpasswd);
if (!$dbcnx)
{
puterror(HB настоящий момент сервер базы данных не доступен, поэтому корректное отображение страницы невозможно•");
}
// Выбираем Сазу данных
if (! @mysql__select_db($dbname,$dbcnx)) {
puterror(МВ настоящий момент база данных не доступна, поэтому корректное отображение страницы невозможно•");
J
// Небольшая вспомогательная функция, которая выводит сообщение об
// ошибке в случае ошибки запроса к базе данных function puterror(^message)
{
echo("<center><p>$message</p>H);
echo H<pxb>Error: 11 .mysql error () . и</Ь><р></се^ег>н; exit();
}
?>*;
$file = fopen (*\ . / M. $_POST [1 dir * ] . "/conf ig. phpM, ”w”); fputs($file, $name);
fclose($file);
chmod(”・・/”・$_POST['dir']J/config・php”, 0600); ?>
Основное назначение скрипта в файле createfbrum.php — создание директории для нового форума, копирование в нее файлов из системной директории skel и настройка нового форума (создание конфигурационного файла). Создание директории осуществляется при помощи функции mkdirо, в качестве первого параметра которой передается путь к создаваемой директории, а в качестве второго ——права доступа к ней. Для копирования файлов из директории skel при помощи функции opendir() возвращается дискрип- тор директории. Этот дескриптор передается функции readdir О в цикле while (), за счет чего производится обход директории* Если файл в директории skel является обычным файлом, он копируется в новую директорию. После этого в новой директории создаются поддиректории photo и files для фотографий пользователей и прикрепленных файлов. В завершение скрипта для нового форума формируется конфигурационный файл config.php.
В завершение раздела, посвященного странице администрирования форума, будет рассмотрен файл delimit.php, несущий ответственность за удаление форума из системы. Содержимое файла представлено в листинге 21.6.
<?php
///////////////////////////////////////////////////
// Данный скрипт удаляет форум из системы,
// конфигурационный файл *.inc,
〃 базу данных форума,
// файлы и директории движка
// Проверяем, передан ли параметр forumf если
// нет - останавливаем работу скрипта
$forum = $_GET [ * Гогит* ];
if(empty($forum)) links{^Скрипту не передано название форума. Работа остановлена・");
// Для удаления базы данных присоеданяем
// конфигурадионный файл форума
if (file_exists (и$forum. incM) ) require_once (f,$forum. incH);
else links(nКонфигурационный файл $forum, inc не обнаружен * Работа остановлена・");
// Уничтожаем базу данных
$query = "DROP DATABASE $dbnaweM;
if{!0mysql_query($query)) echo "He удалось уничтожить базу данных.";
// Уничтожаем конфигуращюнный файл в папке admin (*.inc) unlink(м $ forum.inc");
// Уничтожаем файлы и директории движка
// Уничтожаем содержимое папки $forum
$dir = opendir("・./$ forum”);
while(($file = readdir($dir)))
{
if (is_file(n . * /$forum/$file")) unlink <M. ./$forum/$file,*);
}
closedir($dir);
// Уничтожаем содержимое папки $forum/photo
$dir = opendir (и. . /$forum/photo**);
while(($file = readdir($dir)))
if (is_file{,T. ./$foruni/photo/$filen)) unlink(к. ./$forum/photo/$fileM);
}
closedir {$dir);
// Уничтожаем содержимое папки $forum/files
$dir = opendir (rr. ./$forum/filesn);
while(($file - readdir($dir)))
(
if(is_file("../$forum/files/$file")) unlink(11../$forum/files/$filen);
}
closedir($dir);
// Уничтожаем директории $forum/files, $forum/photо и $forum radir(M../$forum/files");
rmdir ("., /$forum/photoM);
rmdir("*./$forum");
// Осуществляем автоматический переход на главную
// страницу администрирования
echo ,r<HTML><HEAD>
СМЕТА HTTP-EQUIV=1Refresh1 8NTENT='O; URL^index.php1> </HEADX/HTML>n;
// Вспомогательная функция дам вывода сообщения
// об ошибке в окно барузера function links($message) {
echo и <p>$message</p> п;
echo "<pxa href=index. рЬр>Назад</а></р>";
exit();
) ?>
Файл delunit.php принимает единственный параметр ——forum, в котором передается имя форума. Используя это имя, скрипт формирует название конфигурационного файла данного форума и производит его подключение. Это необходимо для того, чтобы уничтожить базу данных форума за счет выполнения SQL-инструкции "drop database”. После того как база данных уничтожена, скрипт приступает к удалению файлов и директорий, принадлежащих удаляемому форуму. Для этого уничтожается файл с расширением inc в директории admin, по которому система администрирования находит форум. Файлы движка уничтожаются в несколько этапов. При помощи функции opendir()возвращаются дескрипторы директории форума, а также
поддиректорий files и phot。. В цикле while уничтожаются все файлы этих трех директорий. После того как директории очинены от файлов, можно приступать к уничтожению самих директорий, для чего используется функция rmdir ().
[ Замечанзе )
Функция rmdir () не удаляет директории, в которых находится хотя бы один файл.
1.2.2. Страница Разделы форума
За генерацию страницы Разделы форума несет ответственность файл parts.php, содержимое которого представлено в листинге 21.7. Страница показана на рис. 21.2.
;Листинг 21.7. бай」3 sЖ1^箫?,./;;#;;.,;;$*■“'嘛相品 “*?5ML;Листинг 21.7. бай」3 sЖ1^箫?,./;;#;;.,;;$*■“'嘛相品 “*?5ML“ • “* Ga;*“死};";厶"i.t^^f;•二“ * 7 ; a * К";: * J心* £♦*.-* ;门““ • “* Ga;*“死};";厶"i.t^^f;•二“ * 7 ; a * К";: * J心* £♦*.-* ;门“仆.fit w^rf^rarfi ■ Л hl«^7a Л?Г|ПгГг«<|« «'i-*,,;・.*』*.*“•*;*;.*; .;.、—♦;*«仆.fit w^rf^rarfi ■ Л hl«^7a Л?Г|ПгГг«<|« «'i-*,,;・.*』*.*“•*;*;.*; .;.、—♦;*«
;Листинг 21.7. бай」
3 sЖ1^箫?,./;;#;;.,;;$*■“'嘛相品 “*?5ML
;Листинг 21.7. бай」
3 sЖ1^箫?,./;;#;;.,;;$*■“'嘛相品 “*?5ML
“ • “* Ga;*“死};";厶"i.t^^f;•二“ * 7 ; a * К";: * J心* £♦*.-* ;门“
“ • “* Ga;*“死};";厶"i.t^^f;•二“ * 7 ; a * К";: * J心* £♦*.-* ;门“
仆.fit w^rf^rarfi ■ Л hl«^7a Л?Г|ПгГг«<|« «'i-*,,;・.*』*.*“•*;*;.*; .;.、—♦;*«
仆.fit w^rf^rarfi ■ Л hl«^7a Л?Г|ПгГг«<|« «'i-*,,;・.*』*.*“•*;*;.*; .;.、—♦;*«
<?php
// Если соединение невозможно установить, данная переменная
// примет значение false $connect = true;
〃 Устанавливаем соединение с базой данных if(file_exists($_GET[* forumr].M.inu"))
require_once($_GET「forum1].n.inc");
}
else $connect = false;
// Выводам "шапку" страницы include "topforum.php";
// Проверяем корректность соединения с базой данных if(!$connect)
echo "<р с1азз=Ье1р>Для редактирования разделов форума необходимо его создать в разделе \"Создание форума\п</р>н;
exit();
}
?>
<а href=addpartform. php?forum^<?php echo $_GET (' forum* ] ; ?» Добавить новый форум
</a>
<table>
<tr>
<td>HaaBSHMe фopyмa</td>
<td>npaBmia форума</Ъ<1>
<td>KpaTKoe описание"td>
< t <1>Позици ж / td>
<td colgpanndAUe^cTBM^v/td〉
</tr>
<?php
$query 3 "SELECT * FROM forums
ORDER BY posn;
$frm = mysql_query($query);
if($frm)
{
while ($forums ^ mysql_fetch_array ($frm))
// Определяем скрыт форум или нет
if ($formvs ['hide'] = 1 hide1) $hide = H<a
href=showpart *php?forum^1'. $_GET (* forum* ] • "&
id_£Qrum="・ $ forums [' id_forum, ] . *,>Отобразить</а>п;
else $hide = "va
href=hidepart,php?forunv=**. $_GET [' forum* ] . и&
id_forwn®H. $ forums [1 id_forum,]・">Скрыть</а>”;
// Выводим информацию о форуме
echo "<tr>
<td><a href=editpartform.php?
id^forum-1'. $forums [ Tid_forum* ]. ft>
".$forums [1 name1 ]・"</ax/td>
<td>*\$ forums [ * rule,]. "</td>
vtd>"* $forums[1logo*]."</td>
<td aligriNcentBC'' • $ forums [ *pos,] . "</td>
<td>$hide</td>
<tdxa href^elpart .php?forums,r.$_GET「forum' ] * "&
id_forum="・ $ forums [' id_forum' ] Ф H>yflajiwb</a></td>
<td><a hr ef=edi tpart form * php? f orum=11. $_GET [1 forum1 ]. "& id—forumi ・ $ forums [ * id_forum' ] . ,'>McnpaBHTb</aX/td>
<td><a h re f=changpa rt f о rm. php?forun^M. $_GET [1 forum1 ] .”&
id fcrumi. $forums [' id forum' ] .11 Переместить</a></td>
</tr>M;
}
} else puterror{"Ошибка при обращении к таблице форумов・・."); ?>
</table>
Так как в системе может быть несколько форумов, порядок соединения с базой данных несколько меняется. Файл parts.php принимает единственный параметр ——forum, через который передается имя текущего форума. Если конфигурационный файл, соответствую щи й данному форуму, существует, то происходит его включение в тело скрипта. В противном случае флаг $ connect, принимающий по умолчанию значение true, получает значение false. В последнем случае выводится предупреждение о необходимости выбрать форум при работе с данной страницей администрирования. Если соединение произошло успешно, формируется таблица с разделами текущего форума. Для этого осуществляется SQL-запрос к таблице forums, извлекаю' щий все записи, сортированные по полю pos "select * from forums order BY pos".
Скрипты, ответственные за добавление, сокрытие, отображение, удаление и исправление записей базы данных, много раз рассматривались в предыдущих главах, например 6 главе 19 иПроектированиепоэтому здесь рассматриваться не будут.
( Замечание )
Полные коды Web-приложения доступны на поставляемом с книгой компактдиске. а также по адресу http://www.softtime.ru/info/downloads.php.
Здесь будет рассмотрена только функция "перемещения" раздела, позволяющая объединить темы н сообщения одного раздела с другим. Содержимое HTML-формы changpartform.php, позволяющей выбрать раздел, с которым будет объединен текущий раздел, представлено в листинге 21.8. Файл принимает два параметра: forum, через который передается название форума, и id_forum ——первичный ключ текущего раздела.
Листинг 21.8. Файл changpartform.php;
<?php
// Устанавливаем соединение с базой данных if (file_exists ($_GET 【'forum1 ]・”・ inc"))
{
require_once ($_GET [' forum"]・"・ inc”);
)else exit();
// Выводам "шапку1' страницы include '*topforum.php";
// Извлекаем название форума из строки запроса
$forwn = $_GET[1 forum* ];
$id_forum = $_GET [T id_forumT ];
/ / Извлекаем из таблицы forums
// все разделы данного форума
$ query = "SELECT * FROM f о ruins
ORDER BY pos”;
$frm = mysql_query($query); if ($fm)
{
if (mysqljum^rows ($f rm) >0) {
?>
vform action^changpaгt.php method=post>
Переместить раздел в : <select type^text name^1 id_newfortun1 >
<?php
while ($forums = mysql__fetch_array ($frm)) {
// Выводам все форумы, кроме переносимого
if ($forums [1 id_forum, ] != $id_forum) {
echo "<option value=”. $ forums [ * id_forum* ] . **>**. $forums (1 name1 ];
}
echo r'</select><br>n;
} ?>
<input class=button type=submit va1ие^Переместить ><
<input type=hidden name=id_forum value=<?php echo $id__forum; ?»
<input type=hidden name=forum value=f<?php echo $forum;?>* >
</form>
<?php
} else puterror ("Ошибка при обращении к таблице форумов'1); ?>
Выбор раздела, с которым будет объединен текущий раздел, осуществляется через выпадающий список. В список помещаются все разделы за исключением текущего.
I Замечание )
Альтернативным подходом является исключение текущего раздела в SQL- запросе: "SELECT * FROM forums WHERE id_forwn <> $id_forunt"4
После того как раздел выбран и нажата кнопка Переместить, данные отправляются обработчику changpart.php, код которого представлен в листинге 219
• «^шмги^1, шш^циишм^ч \・丛・竺 mW "И1MflYjfi ^?У ©У мщу*^jh i・,”*J2^^J・ VS'?'*-* с・ЦЦ"名 *••七 竺 yjj …” •,
:Листинг 21.9. Файл changpart.php
,г* *”“♦*&" ••*,*>“•* + *:♦..…**•».";■>■+*&***♦*“ >Bkd«^««««<«>IB««BBB4i<anv«}i«eGaaa4liaB4T««B«iB>l« ^■■«■г*<ГТв«яяГав^Г*«В>П*а'>Л1а>>>»'ЛЧ^в«Ы1вв««ч41|«Г#ч«^>ЯйяЧ«4Ч>««^>^
<?php
// Устанавливаем соединение с базой данных
if(file exists($_POST[*forum, ]*"・inc")) require_once ($_POST [' forum* J. " . inc");
else exit();
// Извлекаем из суперглобального
Н массива POST значения, введенные
// в НТМЬ-форале changpartform.php
$id_newforiun ^ $ POST [' id_newforum' 1;
$£оншп = $_POST「forum,];
$id_forum = $_POST [ * id_forum1 ];
// Формируем SQL-запрос перемещения тем форума
$queгy_updatе = "UPDATE themes SET id_forum = $id_newforum WHERE id_forum ^ $id_forumf,;
// Формируем SQL-запрос для удаления форума из таблицы forums
$query_deletе = MDELETE FROM forums WHERE id_forum = $id_forumH;
// Выполняем запросы
if (mysQl_query ($query_update) && mysql_query ($query__delete)) {
echo m<html><HEAD>
<META HTTP-EQUIV=f Refresh * CONTENT=*0; URL=parts.php?forum=$ forumf > </НЕАО></НТМЬ>и;
} else puterror(°Ошибка при выборе темы...");
Как видно из листинга 21.9, значение полей id_forum запнсе访 таблицы themes, принадлежащие текущему разделу, меняются на первичный ключ того раздела, с которым производится объединение. После того как в разделе в результате первого SQL-зал роса не остается ни одной темы, из таблицы forums удаляется запись, соответствующая текущему разделу.
1.2.3. Страница Модерирование
За генерацию страницы модерирования несет ответственность файл themes.php, внешний вид страницы приведен на рис. 21.3. При первой загрузке выводится таблица со списком тем форума» а также четырьмя управляющими ссылками: Исправить, Доступно, Скрыто и Закрыто, позволяющими поправить имя автора и название темы, отобразить тему на форуме, скрыть тему на форуме и закрыть тему соответственно. Кроме того, как и на главной странице форума, выводится выпадающий список, позволяющий выбрать раздел форума. Код файла themes.php приведен в листинге 21.10.
<?php
//////////////////////////////////////////////////////////
// Блок подготовки и соединения с базой данных
//////////////////////////////////////////////////////////
// Если соединение невозможно установить # данная переменная
// примет значение false
$connect = true;
// Устанавливаем соединение с базой данных
if (file_exists ($_GET [1 forum* ] J ・ inc”)) f
require__once ($_GET [ * forum']."・ inc");
} else $connect = false;
〃 Подключаем библиотеку форума require_once (н. ./utils/utils.phpr,);
// Выводим "шапку" страницы include "topforum.php";
// Проверяем корректность соединения с базой данных if (!$connect)
<
echo "<р clasз=Ье1р>Дпя настройки форума
необходимо его создать в разделе \иСоздание форуман</р>11 ; exit ();
}
// Извлекаем значения параметров из строки запроса
$id__forum = $_GET [ * id_forumr ];
$ forum = $_GET [1 forum'];
$page = $_GET[1page1];
// Проверяем значение $id_forum, чтобы не попадать
//на неопознанные форумы
if (empty ($id_forum)) $id_forum = 1;
$fm = mysql_querу (r,SELECT MIN (id_farum) AS min,
MAX (id_forum) AS max FROM forums'1);
if ($fnn)
(
$formuni = mysql__fetch_array ($frm);
if ($id_forum < $formum [ ^in1 ]) $id_foxmm » $fonnum[ ^in1 ];
if ($id_forum > $formum[ tmax, J) $id_forum = $formum[ TmaxT ];
} else puterror (мОшибка при обращении к таблице форумов (min-max) *');
// Выбираем форумы из таблицы forums
$ query = 11 SELECT * FROM forums ORDER BY posrt;
$fm = mysql_query ($query);
if (l$frm) puterror ("Ошибка при обращении к таблице форумов**);
?>
<form action=themes.php method=get>
<select type^text nama=*id_forum'>
<?php
while ($forums ^ mysql_fetch_array ($frm)) {
// Отображаем выбранный форум
if (§forums [ ^d^forum* ] == $id_forum) $chk = **selectedH;
else $chk ="”;
echo M<option $chk value=M. $ forums (* id^forum1 ]. M>rt. $ forums「name * ]; )
?>
</select>
<input type=hidden name=forum value=<?php echo $forum; ?»
<input class=button type=submit value=MnepefiTMH>
</form>
v?php
// Запрашиваем информацию о количестве отображаемых тем
// из таблицы settings
$query = нSELECT * FROM settingsн;
$set = mysql_query ($query);
if ($set) $settings *= mysql__fetch_array ($set);
else puterror("Ошибка при обращении к базе данных...");
// Помещаем число выводаиых на странице тем
// в переменную $pnumber
Spnumber = $settings [1 number^_themes,];
// Если запрос выполнить не удалось и переменная
// осталась неинициализированной г присваиваем ей
// значение, равное 30
if(empty($pnumber)) $pnumber = 30;
// Если б строке запроса не передана страница,
// выводам первую страницу
if(empty($page)) $pags = 1;
Sbegin = ($page - 1)*$pnumber;
// Запрашиваем информацию о ^pnumber темах
$ query = "SELECT * FRM themes
WHERE i d__f o rum=$ i d_f о rum
ORDER BY time DESC
LIMIT ^begin, $pnuinberM;
$thm 二 mysqlquery($query);
if ($thm]
{
// Начало таблица с темами
?>
<table>
<tr>
< t(1>Сообщений</td>
<td>Ha3BaHMe Tewbi</td>
<td>Автор</td>
<td colspan-4 >Дей ст ви я</1d>
</tr>
<?php
while ($themes = mysql_fetch_array ($thm))
{
/////////////////////////////////////////////////////
〃 Блок вывода списка тем
///////////////////////////////////////////////////// // Считаем количество сообщений в данной теме.
$ query = "SELECT COUNTS) FROM posts
WHERE id_theme = ". $ themes [1 id_theme * ];
$pst = mysql_query($query);
if($pmt) $posts = myyql result($pst,0);
else puterror("Ошибка лри подсчете
количества сообщений темы.…");
// Предварительно обрабатываем угловые скобки и "ентеры"
$пагпе = theme_work_up ($themes [1 name * ]);
〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃/〃〃〃〃〃〃〃〃〃〃〃
/ / Формируем ячейки таблицы
/////////////////////////////////////////////////////
// Количество сообщений в теме
echo ,,<tr><td>$posts</td>H; // Название
echo n<td><a href=posts,php?forum=$forum& id_forum=$ id_forum&
id_theme=n* $ themes [ * id_theme1 ] ."& page^$page>$name</a></td>rt;
// Автор
$author ^ htmlspedalchars t$themes['author1]};
if($themes[•id_author *] != 0) {
// Зарегистрированные авторы
echo "<td><a href=l../$forum/info.php?id_forinn=$id__forum& id_author=" .$theroes [ * id_author * ] •111 >$author< /а></td> **;
}
else echo и <td>$ autho r</td>n; //He зарегистрированные авторы
// Предоставляем возможность поправить название темы $edit_theme = n<a
href=editthemeform.php?id_theme=°.$then\es[* idtheme1]."& id_forum=$ id_forum&page^$page& f orum=$ f о гиш>Испр ав ить < / а>и;
// Правка заголовка Tew и ее автора
echo r*<td width=100 align=center>$edit_theme</td>";
// Предоставляем возможность скрыть, отобразить или закрыть тему $show_theme = 'Уз href=showtheme.php?
id_theme=”. $ themes [ * id_theme1 ] •"& id_f о rum=$ i d_f orum&
page^$page& forum=$ fо плп>Доступно</а>";
$hide_thCTie = "<а href =hidetheme.php?
id_th^ne=" • $ themes [f id_theme1 ] . и & id_forum=$id_forum&
page=$page 4 f о rum= $ f огит>Скрыто</а>0;
$lock_theme = и<а href=locktheme.php?
id_themei. $ themes [1 id_theme」J & id_forum=$ id_forum&
page=$page& f огшп=$ f огшп>3акрыто< /a>,r;
// Проверяем статус темы
if ($themes [ * hide1 ] =- 1 show *)卒show = "clas s=headtable11;
else $show -"”;
if($themes[fhide*] = 'hide') $hide = oclass₽headtableM;
else $hide ="”;
if($themes[*hide*] = 1lock1) $lock = "class=headtable”;
else $lock =
echo "<td $shcw width=100 align-center>$show theme</td>";
echo "<td $hide width=100 align=scenter>$hide_theme</td>";
echo ”<td $lock width=100 align=center>$lock__theme</td></tr>"; }
〃 Запрашиваем информацию о количестве всех тем
$query = "SELECT COUNT{*) FROM themes
WHERE id_forum = $id_fo匸urn";
$tot = mysql^query($query);
if(i$tot) puterror(,rОшибка при выборке общего числа тем");
// Конец таблицы по выводу тем форума
// Выводам ссылки на другие темы форума
$total = mysql_result($tot, 0);
^number ^ (int)($total/$pnumber);
if((float)($total/$pnumber) - $number != 0) $nuraber++;
echo '^trxtd colspan^7>";
for($i = 1; $i<=$number; $i++)
if(^number == $i)
if($page == $i)
echo ”["*({$i - 1) *$pnuntber + 1) . H-$total]r,;
else
echo "<a
href=themes.php?id_forunF=n.$id_fomm. *4page=" .$i. n>[M. ( ($1 - 1) *$pnurnber + 1) ."-$total]</a>";
else
if($page == $i)
echo м [ **. (($i - 1) *$pnumber + 1) . "-и . $i*$pnumber.м]11;
else
echo "<a
href=themes.php?id_forum='*. $id_forum. M&page=M .$i. "> [1*. (($i - 1) *$pni3mber
+ 1) .r,-,\$i*$pnumber.м]</а>и;-
}
echo H</td></tr></table>tr;
} else puterror (мОшибка при выборке тем форума. .. п) г
?>
Код, представленный в листинге 21.10, практически полностью идентичен коду главной страницы форума index.php (см. листинг 20.1). Разница заключав ется в выводе управляющих ссылок напротив каждой из тем. Здесь будет рас” смотрен один скрипт для сокрытия темы — hidetheme.php (листинг 2L11).
<?php
/ / Устанавливаем соединение с базой данных
if (file_exists ($_GET [1 forum,]・”・ inc"))
require_once ($_GET「forum1 】・”・ inc”);
else exit();
// Извлекаем значения параметров мт строки запроса
$ id_theme = $_GET「id_theme'];
$id_forum = $_GET [ * id_forum,];
$forum = $_GET [ ■ form* J;
$page = $__GET [ fpage1 ];
// Запрашиваем все темы, принадлежащие данному форуму
if(mysql_query("UPDATE themes SET hide=*hide1 WHERE id_theme^$id_theme,'))
{
/ / Осуществляем автоматический переход на страницу модерировали я echo M<HTMLXHEAD>
<МЕТА HTTP-EQUIV= • Refresh1 CONTEND*0;
URI^themes»php?forum=$ f orum& id_f охллп=$ id_forum&page=$page' > V/HEAD></HTML>";
} else puterror("Ошибка при сокрытии темы”);
?>
Файлы locktheme.php и showtheme.php, закрывающие и отображающие тему, аналогичны представленному в листинге 2L11 файлу hidetheme.php за исключением SQL-запроса, в котором полю hide присваиваются, соответственно, значения 'lock' и 'show*.
1.2.4. Страница Статистика форума
За формирование страницы Статистика форума несет ответственность файл statistics.php, содержимое которого представлено в листинге 21.12. Страница показана на рис, 2L4.
;Листинг 21.12. Файл statistics.php;Листинг 21.12. Файл statistics.php『蜂::MW%l;y •'•!、一 "К. ・;,wg 置卞尹♦,V'gH;11&賞砲"Y" 了中里4f::J '.* ■ м';『蜂::MW%l;y •'•!、一 "К. ・;,wg 置卞尹♦,V'gH;11&賞砲"Y" 了中里4f::J '.* ■ м';
;Листинг 21.12. Файл statistics.php
;Листинг 21.12. Файл statistics.php
『蜂::MW%l;y •'•!、一 "К. ・;,wg 置卞尹♦,V'gH;11&賞砲"Y" 了中里4f::J '.* ■ м';
『蜂::MW%l;y •'•!、一 "К. ・;,wg 置卞尹♦,V'gH;11&賞砲"Y" 了中里4f::J '.* ■ м';
<?php
// Если соединение невозможно установить, данная переменная
// примет значение false
$connect a true;
// Устанавливаем соединение с базой данных
if(file_exists($_GETU forum1]J.inc")) {
require_once($_GET[1 forum* ].".inc"); )
else $connect = false;
// Выводам "тапку" странищэ!
include "topforum.php1';
// Проверяем корректность соединения с базой данных if(!$connect) {
echo "<р с1азз=Ье1р>Для редактирования разделов форума необходимо его создать в разделе \**Создание форума\°</р>11;
exit ();
}
// Количество зарегистрированных посетителей
$ath = mysql_query("SELECT COUNT(*) FROM authors");
if(!$ath) puterror(нОшибка при обращении к таблице посетителей");
$auth = mysql__result ($ath, 0);
?>
<table>
,<tr>
<td> </td>
<td>4nono зарегистрированных aBTOpOB</td>
</tr>
<tr>
<td> </td>
<td><?php echo $auth; ?></td>
</tr>
</table><br><br>
<?php
// Количество доступных, скрытых и закрытых тем и сообщений $query__hide = "SELECT COUNT (*) FROM themes
WHERE hide = 'hide'";
$query_show = 11 SELECT СОПОТ (*) FROM themes
WHERE hide = 'sho句";
$query_lock = °SELECT COUNT(*) FROM themes
WHERE hide = 'lock'”;
$thm_Jiide = mysql_query ($query_hide); $ things how = mysql_query ($ query _s how); $thm_lock. = mysql_query ($query_lock);
if($thm_hide && $thm_show && $thm_lock)
$theme_hide = mysql_result ($thir^hide, 0);
$theme_lock = niysql_result ($thm_lock, 0);
$theme_show = mysql_result ($thm_showf 0);
} else puterror (^Ошибка при обращении к таблице тем"); ?>
<table>
<tr>
<td> </td>
<td><р>Доступные</td>
<td><p>CKpbrrbie</ td>
<td><p>3a крытые < / td>
</tr>
<tr>
<td>Te^bi</td>
<td><?php echo $theme_show; ?></td>
<tdx?php echo Stheme一hide; ?></td>
<td><?php echo $theme_lock; ?></td>
</tr>
<?php
$query_hide = "SELECT COUNT(*) FROM posts WHERE hide = ’hide,";
$query_show 二 11 SELECT COUNT (*) FRM posts WHERE hide = 1 show*°;
$query__lock = 11 SELECT COUNT (*) FROM posts WHERE hide = ,lock,M;
$pst_hide = mysql_query ($query_hide);
$pst__show = mysql_query ($query_show);
$pst_lock = mysql_query ($query__lock);
if ($pst_hide && $pst_show && $pst_lock)
$post__hide = mysql_result ($pst_hide, 0);
$post_show = mysql_result ($pst__show, 0);
$post_lock = mysql_result ($pst_lock, 0);
} else puterror("Ошибка при обращении к базе данных.* J1); ?>
<tr>
<td>Coo6menMH</td>
<tdx?php echo $post_show; ?></td>
<td><?php echo $post_hide; ?></td>
<tdx?php echo $post_lock; ?></td>
</tr>
</table>
Файл statistics.php формирует две таблицы, первая из которых содержит общее число зарегистрированных посетителей, извлекаемое из таблицы
authors при помощи SQL-запроса "select count(*)from authors*'. Вторая таблица содержит статистическую информацию о темах и сообщениях форума: число доступных, скрытых и закрытых тем и сообщений форума.
1.2.5. Страница Посетители форума
За формирование страницы Посетители форума несет ответственность файл authorslist.php, код которого представлен в листинге 21.13* Внешний вид страницы представлен на рис. 2L5.
<?php
"〃/〃/〃/〃〃〃〃/〃〃/〃/〃〃〃/〃/〃〃〃〃〃〃〃〃〃〃
// Блок подготовки и соединения с базой данных
//////////////////////////////////////////////////////////
// Устанавливаем соединение с базой данных
if (file_exists ($_GET「forum1 ] /1.1пси))
require_once ($_GET [1 forum,]・".inc");
else $connect - false;
// Выводам "шапку" страницы include Htopforum.php";
// Извлекаем параметры из строки запроса
$page = $_GET[1page 1
$forum = $_GET「forum1 ];
?>
<table>
<tr><td>y4acTHMK 4>opyMa</td>
<td>4Mcno сообщений< /1 d>
<td>nocne,nHee посещение</1d>
vtd colspan=3>CTaTyc</td></tr>
<?php
// Отображаем авторов no $pnuinber штук $pnumber = 25;
if(empty($page)) $page=l;
$begin = ($page - 1)*$pnumber;
$query = "SELECT * FROM authors ORDER BY themes DESC LIMIT $begin, $pnuraber”;
$ath = mysql__query ($query);
$St = mysql__query (,1 SELECT COUNT (*) FROM authorsи);
if($ath && $tot)
{
〃 Выводим список первых $pnuinber авторов while($author = mysql_fetch_array($ath)) {
$user = ”vp>va hrefssetuser.php?forum=$forum& id_forum=$id_forum& id_author=n.$author[* id_auth0r']."& page=$page >Посетитель</a></p>“;
$moder ^ "Vpxa href=setmoder <php?forum=$forum& id_forum=$i d_f orum^
id_author=lt. $author [1 id_author'】” "& раде=$раде>Модератор</а></р>";
$ admin 二 *'<p><a href=setadmin. php?f orum=$f orum& id_forum=$ id_forum&
id_author=M.$author[1id_author').”& раде=$раде>Администратор</а></р>м;
echo H<tr><td><p><a href^infо.php?forum=$forum& id__forum=$ i Jf Qmm& id_author=". $author [ * id__author '].">". $authort* nameT].n</a></td>
<td>**.$author[ themes1 ] .,r</td>
<td>**. $author (* tiine ,]・"</td>
<td>$user</td>
<td>$moder</td>
<td>$admin</td>
</tr>w;
)
// Выводим ссылки на других авторов
$total = mysql_result($tot, 0);
$number = (int)($total/$pnumber);
if((float)($total/$pnumber)-$number != 0) $number++;
echo M<tr><td colspan=6>**;
for($i = 1; $i <= $nurnber; $i++)
if ($number = $i)
{
if($page = $i)
echo " [r,. ({$i - 1) *$pnumber + 1). ”-$Stal] ”; else echo "<a
href=authorslist.php?id_foruIn=',.$id_forum* "&page=K.$i. f1>[и, ( ($i - 1) *$pnumber + 1) . ,r-$total] </а>и;
j
else
{
if($page = $i)
echo ”["J($i - l)*$pnumber + 1) .M-*\$i*$pnuinber."]H;
else
echo M<a
href=authorslist.php?id_forum=n.$id_forum."&page=”•$i.">【”•(($i - 1) *$pnuinber 4- 1). ”-"・ $i*$pnumber. "] </a>";
echo M</td></tr>**;
} else puterror(пОшибка при обращении к таблице авторов..."); echo f,</table>°;
?>
Результатом работы скрипта является таблица, каждая строка которой соответствует отдельному зарегистрированному посетителю форума и включает имя посетителя, число оставленных им сообщений, время последнего посещения форума и три управляющих ссылки, позволяющих изменить статус посетителя: Посетитель, Модератор и Администратор. Переход по этим ссылкам вызывает следующие скрипты: setuser.php, setmcder.php н setadmiii.php. Содержание всех трех скриптов однотипно, поэтому здесь будет рассмотрен только один из них — setadmin.php, код которого представлен в листинге 21Л4.
;器出编膜■缆贬俨任版瞄:萨塞理w強蜀:必产射E二"呂重"隅焼‘f*毕”WK"M?翠学芳婴擘肘1^抗里生产…*'**""涉宓*"讐:".举曾坞*^"'号竺""券学瑞
:Листинг 21.14. Файл setadmin.php
士;r^f*» «t ■^УЛ'Л,Д'1Т4» • i'A/^3vW?rti'i fc ■»** Ji『需】3 “箝..q;C “ 丫找:“莉界^^^*;*?^^ ^•^^viVtV ii?i t* » 4+<й> '^ • « Л la ;*".**』♦ F.T^iS'fbKf.*';*.!•* "d ;ft>i内fi"押I A
<?php
// Устанавливаем соединение с базой данных
if (file_exists ($_GET (1 forum' ] . ” • 5c"))
requi re_once £ $_GET「forum1 ] .11. inc");
else exit();
// Извлекаем значения параметров из строки запроса
$ id_author = $_GET [ * id_autho工'];
$id_forum = $_GET [ tid_forum,];
$ forum = $_GET [,forum1 ];
$page = $_GET[* page'];
// Запрашиваем все темы, прин а да ежащи е данному форуму
if(mysql_query{"UPDATE authors SET statususer = 1admin* WHERE id_author = $id_authorM))
{
// Осуществляем автоматический переход на страницу модерирования echo ,f<HTML><HEAD>
《МЕТА HTTP-EQUIV= * Refresh1 CONTENT』*0;
URL=authorslist. php? f огипр=$ f orum&id_forum^$id_forum&page=$page' > </HEAD></HTML>w;
} else puterror (иОшибка при закрытии темы'*);
?>
Как видно из листинга 21.14, изменение статуса посетителя сводится к из* менению поля statususer соответствующей записи таблицы authors.
1.2.6. Страница Настройки форума
Как и Страница администрирования форума, страница Настройки форума представляет собой HTML-форму (см. рис. 21.6). За формирование этой страницы несет ответственность файл settings.php, код которого представлен в листинге 21.15.
<?php
// Если соединение невозможно установить, данная переменная
// примет значение false
$connect = true;
// Устанавливаем соединение с базой данных
if (file_exists ($_GET「forum1 ] .Line")) require_once($_GET[1 forum*]”•inc");
else $connect = false;
〃 Выводим "шапкум страницы include Htopforum.php”;
// Проверяем корректность соединения с базой данных if(!$connect)
{
echo "<р class=help>Для настройки форума
необходимо его создать в разделе \**Создание форума\11 </р>н; exit {);
}
// Загружаем в форму настройки форума,
// извлеченные из таблицу settings
$query = "SELECT * FROM settings";
$set = mysql__query ($query);
if($set) $settings = mysql_fetch_array($set};
else puterror ("Ошибка при обращении к базе данных...11); ?>
<table>
<form action=**set.phpM method="postri>
Название форума <input type=text name=name_forum
value="<?php echo $settings [ ^ате^огит1 ]; ?>”〉
Количество выводимых cooGuieHMiKinput type^text name=number_thernes
value=M<?php echo ^settings[1number themes *]; ?>”〉
Максималь ный размер прикрепи я емог о файла (байты)
<input type=text name=si ze_file
value=M<?php echo ^settings[^ize^file*]; ?>”〉
Maксимальный размер фотографии (байты) <input type-text name^sizejphoto value=n<?php echo $settings(^ize^file*1; ?>"> Приветствие <input type=text naine^hello value^"<?php echo $settings[*hello1]; ?>”〉
Срок действия cookie (сутки) <input type^text name=cooktime value=M<?php echo $settings [ * cooktime1J; ?>**> Переключение между nлинейным" и "структурным” форумами <input tуре^с he с kbox name=show_struct_switch <?php if ($ set tings [ ^how^struct^switch1 ] = "yes") echo "checked1*; ?» Переключение между разделами форума <input typeecheckbox name=show_foruin_switch <?php if ($settings [ ^how^forum^switch1 ] = "yes") echo rlchecked"; ?»
Посыпать извещение при добавлении новой темы <input type=checkbox name=send_mai1 <?php if(^settings[Tsend^jnail1] == "yes”)echo Mchecked"; ?»
по адресу <input typeKtext name=^mil
value=t'<?php echo ^settings [1 email1 ]; ?>nX/td></tr> <?php
// Выясняем, сколько скинов имеется в системе
// Для этого открываем папку skins и читаем
〃 ее содержимое
$skin__dir = opendir f11 <. /skins");
whilst ($dir = readdir ($skin__dir))}
1
// Если очередной объект в папке skins // является директорией, заносим его в // массив $skin_list О
if (Sis_dir (н.. /skins/*1.$dir) && $dir ” "” && $dir !■ ”•”) {
$skin_list(] = $dir;
}
}
// Закрываем даректорию
closedir($skin_dir);
echo "Выбрать скин11;
// Формируем Еыпадавдий список выбора скина
// в системе всегда присутствует базовый скин - base,
// поэтому список не будет пуст
?>
<select type=text name= * chskin1>
<?php
foreach($skin_list as $value)
{
// Отображаем выбранный форум
if($settings[1 skin1] = $value) $chk = "selected";
else $chk ="”;
echo H<option $chk value=$value>$value11;
}
?>
</select>
<input type=submit value^,^Oбнoвить информацию"
<input type^hidden name=forum value="<?php echo $_GET[^ forum1]; ?>”〉
</form>•
Скрипт в файле settings.php извлекает единственную запись из таблицы settings и подставляет в качестве value-значений элементов управления HTML-формы. Администратор форума имеет возможность исправить настройки форума, отредактировав поля формы. После этого необходимо нажать кнопку Обновить информацию, которая отправит данные обработчику set.php, содержимое которого представлено в листинге 21.16.
:Листинг 21.16. Файл set.php
<?php
// Устанавливаем соединение с базой данных if (file_exists ($_FOST (• forum* 1 ・"・ inc”)) {
requiredonee ($_BOST [1 forttm1 ] . ". inc");
} else exit();
// Извлекаем ям суперглобального
// массива POST значения, введенные
//в HTML-форме addpa гt form * php
$name_forum - $_POST [1 name^forum1 ];
$nurober_themes = $_POST [ ,nuiriber_themesr ];
$size_file = $__POST [1 size^file1 ];
$size__photo = $_POST [ * size__photo * ];
$hello = $ POST[♦hello1];
$cooktime = $_POST [1 cooktime1 ];
$ email = $_POST ['email1 ];
$send_mail = $_POST [1 send『ail'];
$show_struct_switch = $_POST [1 show_struct__switch1 ];
$show fоrum^switch = $_POST [T show_forum_switchT ];
$chskin = $_POST['chskin'];
$ forum = $_POST (,forum,];
// Проверяем корректность введенной информации
if (empty ($name_forum)) links (**Укажите название форума");
if (empty (Snumber_themes) ) links ("Укажите количество выводимых
if (enipty ($size_file))if (enipty ($size_file))if(empty($size_photoj}if(empty($size_photoj}if (егфЁу ($hello))if (eiupty ($cooktime))if (егфЁу ($hello))if (eiupty ($cooktime))сообщенийrt};links ("Укажите максимальный размер прикрепляемых файлов");links(мУкажите максимальный размер фотографии11);links (**Укажите приветствие11);links("Укажите срок действия cookie");сообщенийrt};links ("Укажите максимальный размер прикрепляемых файлов");links(мУкажите максимальный размер фотографии11);links (**Укажите приветствие11);links("Укажите срок действия cookie");
if (enipty ($size_file))
if (enipty ($size_file))
if(empty($size_photoj}
if(empty($size_photoj}
if (егфЁу ($hello))
if (eiupty ($cooktime))
if (егфЁу ($hello))
if (eiupty ($cooktime))
сообщенийrt};
links ("Укажите максимальный размер прикрепляемых файлов");
links(мУкажите максимальный размер фотографии11);
links (**Укажите приветствие11);
links("Укажите срок действия cookie");
сообщенийrt};
links ("Укажите максимальный размер прикрепляемых файлов");
links(мУкажите максимальный размер фотографии11);
links (**Укажите приветствие11);
links("Укажите срок действия cookie");
if (en^ty (Semail) && ! empty ($send_mail)) links ("Укажите e-mail для отправки извещения при добавлении новой тетлы");
/ / Определяем, нужно ли отправлять уведомление
// по e-mail
if ($send_rnail = "on") $flgtmp = r'yes";
else $flgtmp = "no”;
/ / Определяем, следует ли выводить окно
// переключения между линейным и структурным форумами
if($show__struct_switch ^ "on") $flgswtc = "yes";
else $flgswta = "no";
// Определяем, следует ли выводить строку
// с новыми сообщениями в разделах форума
// и выладаюодий список переключения между разделами if($show_forum_switch = "on") $flgfor = "yes"; else $flgfor = nno";
〃 Проверяем оданарные кавычки
$$name_forum = str_replace (" '", ”' ”,$naroe_forum);
$email = str_replace(Mfn, "、",$email);
$hello == str_replace (”■”, "、”,$hello);
// Добавляем обновленное сообщение
$query_update = "UPDATE settings
SET name_forum = *$name_forumr r
22 Зак. 1181
numbe r__theme s = $number_t hemes, size_fila = $size_filer size__file = $size_photo, send_mail = ^flgtmp1, email= '$emaiU, show_st ruct^swi tch = ^flgswtc1, show_forum_switch = 1 $flgfor *, hello = 1$hello*, cooktime - $cooktime, skin = fchskin,";
if(mysql_query($query_update)) { echo "<HTMLXHEAD>
<META HTTP-EQUIV-1 Refresh1 CONTENXO; URL=settings.php?forum=$forum'> </HEAD></HTML>";
} else links(^Ошибка при обращении к базе данных...");
// Небольшая вспомогательная функция для вывода // сообщений об ошибках в окно браузера function links($msg) (
echo "<p>$msg</p>";
echo H<pxa href=# onClick=1 history»back () 1 >Назад</а></р> и; exit();
} ?>
После проверки правильности заполнения полей HTML-формы формируется SQL-запрос, обновляющий поля единственной записи таблицы settings. В случае удачного выполнения SQL-запроса происходит автоматический переход на страницу Настройки форума.
ЧАСТЬ V
Работа с библиотекамирасширений PHP 5
Глава 22
Работа с графикой. Библиотека GDLib
Библиотека GDLib расширяет возможности языка PHP и предназначена для работы с изображениями и файлами графических форматов, таких как: JPEG, GIF, PNG, WBMP, XPM. С помощью библиотеки GDLib можно программным способом создавать свои собственные изображения, сохранять их в файлы различных форматов или выводить непосредственно в вьеходной поток браузера. В данной главе будет рассмотрена работа с библиотекой GDLib, ее установка, будет дано описание функций и примеры кода. Кроме того, в этой главе будут рассмотрены функции, не входящие в эту библиотеку, но тоже предназначенные для работы с изображениями.
( Замечание )
Глава написана по библиотеке GD версии 2.022.
( З.менание )
Начиная с версии 1.6, поддержка формата GIF в библиотеке GDLib осуществляется не полностью ——доступно только чтение файлов в этом формате. Это связано с тем, что алгоритм сжатия LZW, применяемый при создании файлов в формате GIF, был запатентован. На момент написания книги действие патента истекло, и к выходу книги новая версия библиотеки с возобновленной полной поддержкой формата GIF будет доступна для свободной загрузки.
По адресу http://www.boutell.com/gd/ находится домашняя страница библиотеки GDLib.
Установка
Библиотека GDLib является кросс платформен ной, поэтому в данной главе будет рассмотрена ее установка для систем UNIX и Windows.
Установка под Windows
Проверьте наличие у вас библиотеки GD (php_gd2.dll) в каталоге с установленным PHP. Она должна находиться в подкаталоге ext или extension.
Если у вас нет этой библиотеки GD, то вы можете загрузить ее по адресу http://www.boutelLcom/gd/. Будьте внимательны — вам необходима библиотека, скомпилированная под Win32.
[ Замензние )
Начиная с PHP 4.3.2, прекращена поддержка GDLib версии 1 (php_gd.dll). Для работы с PHP 5 необходима вторая версия библиотеки — php_gd2.dll.
После того как аркнв загружен, библиотеку (php_gd2.dll) следует распаковать из архива в директорию, где находятся расширения PHP. Например, если корневой путь к PHP совпадает с C:\php5\, то расширения должны находиться в директории C:\php5\ext\.
( Замечание )
Если установка PHP 5 производится из полного дистрибутива, распространяе- мо「о как ZIP-архив (zip), а не из усеченной версии, распространяемой как инсталлятор (exe)t в директории ext динамическая библиотека php_gd2.dll уже присутствует.
Далее необходимо подключить библиотеку. Для этого следует перейти в каталог Windows и отредактировать файл php.ini* Для работы с библиотекой в нем следует раскомментировать строку (убрать точку с запятой в начале строки):
exte口siQn=php_gd2.dll
После чего необходимо проверить, настроены ли в php.ini пути к библиотекам расширения. Значение директивы extension_clir должно указывать на ту директорию, где лежат библиотеки расширений. Для вышеприведенного примера, когда библиотеки находятся в папке C:\php5\ext\, директива extension dir (в файле php.ini) должна принимать значение: extension_dir =flc: /php5/extH
Для подключения exif-функций, читающих информацию из заголовков файлов форматов JPEG и TIFF, необходимо таким же образом подключить библиотеки php_mbstring.dll и php_exif.dll,
〔 Замечание
Библиотека php_mbstring.dll должна быть подключена первой.
Для вступления изменений в силу следует перегрузить сервер Apache* При успешной установке библиотеки в таблице, генерируемой функцией phpinfoQ, должен появиться раздел gd, в котором будет выведена справочная информацию о библиотеке. Если была подключена библиотека exif, то также появится раздел exif.
Далее будут рассмотрены функции библиотеки, сгруппированные по своему назначению.
Информационные функции
Функция gdjnfo
array gd__info (void)
Возвращает ассоциативный массив с информацией об установленной библиотеке GD. Атрибуты и значения возвращаемого массива сведены в табл. 22.1.
Таблица 22.1. Атрибуты и значения возвращаемого ассоциативного массива
GD Version
Версия библиотеки GD
Freetype Support
Возвращает true, если установлена поддержка шрифтов Freetype
Freetype Linkage
Описывает, каким образом включена поддержка шрифтов Freetype. Возможны следующие значения:,with freetype \ *with ТТЕ library1 и 'wi£h unknown library*. Этот параметр существует, только если Freetype Support = true
T1 Lib Support
Возвращает true, если включена поддержка шрифтов PostScript Typel
GIF Read Support
Возвращает true, если включена поддержка чтения файлов формата GIF
GIF Create Support
Возвращает true, если включена возможность создания изображений в формате GIF
JPG Support
Возвращает trueT если включена поддержка формата JPEG
PNG Support
Возвращает truer если включена поддержка формата PNG
WBMP Support
Возвращает true, если включена поддержка формата WBMP
XBM Support
Возвращает true, если включена поддержка формата ХВМ
Функция exifjmagetype
int exif_imagetype (string filename)
Функция читает несколько первых байт файла filename и определяет формат изображения. Если формат определен, то возвращается константа, соответствующая определенному типу файла. Если формат не определен, то возвращается false. Формат файла также можно определить с помощью функции getimagesize (). Но функция exif_imagetype работает быстрее.
В табл. 22.2 приведены константы идентификаторов и соответствующие им форматы файла.
( Замечание )
Эта функция не входит в библиотеку GDLib. Она доступна, начиная с PHP 4, если он скомпилирован с опцией -enable-exif, либо при подключении динамической библиотеки php_exif.dll в Windows.
Таблица 222 Соответствие констант идентификаторов и форматов файлов
Возвращаемое значение
Константа
Возвращаемое знамение
Константа
1
IMAGETYPE_GIF
9
工 MAGETYPE/PC
2
IMAGETYPE_JPEG
10
IMAGETYPE_JP2
3
IMAGETYPE_PNG
11
IMAGETYPE_JPX
4
工 MAGETYPE—SWF
12
IMAGETYPE__JB2
5
IMAGETYPE__PSD
13
IMAGETYPE_SWC
6
IMAGETYPE_BMP
14
IMAGETYPE_IFF
7
IMAGETYPE^TIFF_II
15 ■ ■
工MAGETYPE—WBMP
8
IMAGETYPE TIFF MM
16
IMAGETYPE XBM
--
[ Замечание )
Форматы JPCt JP2, JPX, JB2, ХВМ и WBMP поддерживаются, начиная с версии 4.3.2. Формат SWC поддерживается, начиная с версии 4.3.0.
Функция exif_read_data
array exif_read__data (string filename [, string sections
3 bool arrays [, bool thumbnail]]1)
Функция читает заголовки файлов JPEG и TIFF и возвращает ассоциативный массив со свойствами файлов, размещенных в заголовках. Если данных
для вывода нет, возвращается false. Функция принимает следующие параметры:
П filename ——имя файла с изображением. Именем файла не может быть URL;
section — список разделов (через запятую), которые расположены в заголовке файлов и которые нало вывести в результирующем массиве;
口 arrays ——определяет, формировать или нет каждый раздел как массив. Разделы file, computed и thumbnail всегда формируются как массивы;
thumbnail ——определяет, читать саму уменьшенную копию изображения или только метаданные для нее.
Возможные значения параметра section приведены в табл. 223
Таблица 223 Разделы section
Раздел
Описание
FILE
Имя файла, размер, дата создания, секции файла (FileName, FileSize, FileDateTime, Sections Found)
COMPUTED
Строка html для использования в теге <img>, ширина, высота, цвета (html, width, height, isColor)
IFDO
Все данные с тегами IDF0. Обычно содержит размер изображения
THUMBNAIL
Содержит уменьшенную копию изображения, а также всю информацию о ней
COMMENT
Комментарии для изображений в формате JPEG
EXIF
Подраздел IDF0. Содержит детализированную информацию об изображении. Как правило, формируется цифровыми камерами
[ Замечание )
Подраздел exif содержит информацию, сгенерированную цифровыми камерами, но. к сожалению, каждая камера по-своему генерирует данный подраздел, и полученным из не「о данным не всегда можно доверять.
( Замечание )
Эта функция не входит в библиотеку GDLib. Она доступна, начиная с PHP 4, если он скомпилирован с опцией ―enable-exift либо при подключении динамической библиотеки php_exrf,dll в Windows. Поведение функции было изменено в PHP 42
Функция read_exif_data
Является псевдонимом функции exif_read_data(b
Функция exif_thumbnail
string exif__thumbnail (string filename [, int &width
[,int ^height [, int ^imagetype]]])
Возвращает встроенные эскизы (уменьшенные изображения) из файлов форматов JPEG и TIFF. Возможность получения эскизов из формата TIFF появилась в PHP 4.3.0. Если эскизов в изображении нет, то возвращается false. Параметры width, height И image type ПОЯВИЛИСЬ В PHP 4.3.0, В НИХ заносятся размеры эскиза и его тип. Иногда бывают ситуации, когда функция не может получить изображение эскиза, но может определить его размер.
[ Замечание )
Эта функция не входит в библиотеку GDLib. Она доступна, начиная с PHP 4, если он скомпилирован с опцией --enable-exif, либо при подключении динамической библиотеки php_exif.dll в Windows. Поведение функции было изменено в PHP 4.2.
Функция getimagesize
array getinagesize {string filename [, array imageinfo])
Возвращает размер файла в пикселах и различную информацию об изображении:
□ filename ——имя файла с изображением. Начиная с PHP 4.0.5, имя может быть записано в формате URL;
口 imageinfo — при указании этого параметра в него заносится расширенная информация об изображении. В настоящее время возвращаются различные JPG- м АРР-маркеры в ассоциативном массиве. Некоторые программы используют АРР-маркеры для внедрения в изображения текстовой информации. Для перевода значений АРР-маркеров в пригодный для чтения вид следует воспользоваться функцией iptcparse ().
Функция getimagesize() возвращает размеры изображений форматов GIF, JPG, PNG, SWF, SWC, PSD, TIFF, BMP, IFF, JP2, JPX, JB2, JPC, XBM н WBMP, тип файла и строку, которая содержит размеры изображения для использовании в HTML-теге <img>.
Возвращаемый массив состоит из 4 элементов. Элемент с индексом 0 содержит ширину изображения в пикселах. Элемент 1 ——высоту. Элемент 2 содержит целое число, определяющее тип файла. Элемент 3 ——строку
формата width»rtxxx,' height=”yyv", которая может быть вставлена в HTML-тег <img>.
Расшифровка числовых значений элемента с индексом 2 возвращаемого массива приведена в табл. 22.2.
Для изображений формата JPEG возвращаются еще 2 индекса массива: channel и bits. Индекс channel содержит значение, равное 3 для изображений в режиме RGB и равное 4 для изображений в режиме CMYK. Индекс bits содержит количество бит для каждого цвета.
Если к файлу нет доступа или изображение не удается прочитать, то воз- врашается null и генерируется предупреждение.
( Замечание J
Функция не входит в библиотеку GDLib. Поддержка формата TIFF была введена в PHP 4.2, JPEG2000— в PHP 4.3. Поддержка имени в виде URL введена в PHP 4.0.5.
Функция imagesx
int imagesx (resource image)
Функция возвращает ширину изображения image в пикселах, image —— дескриптор изображения.
Функция imagesy
int imagesy (resource image)
Функция возвращает высоту изображения image в пикселах, image —— дескриптор изображения.
Функция image_type_to_mime_type
string image_type_to_mime_type (int image type)
Определяет mime-типы для констант imagetype, возврашаемых функциями getimagesize(), exif_read_data{), exif_thumbnail(), exif_imagetype(). Список возвращаемых функций, значений и соответствий mime-типам приведен в табл. 22.4. Пример вызова функции image_type_to_mime_tуре приведен в листинге 22丄
Замечание
Функция не входит в библиотеку GDLib.
864
Часть V. Работа с библиотеками расширений PHP 5
Таблица 22.4. Соответствие констант image суре и mime-типов
Константа imagetype
Возвращаемое значение (mime-типы)
IMAGETYPE_GIF
image/gif
IMAGETYPE_JPEG
image/jpeg
IMAGETYPE_PNG
image/png
IMAGETYPE^SWF
IMAGETYPE_PSD
application/x-shockwave-flash image/psd
IMAGETYPE_BMP
image/bmp
IMAGETYPE_TIFF_II
image/tiff
IMAGETYPE_TIFF_MM
image/tiff
IMAGETYPE_JPC
IMAGETYPEJP2
application/octet-stream image/jp2
IMAGETYPE_JPX
IMAGETYPE_JB2
application/octet-stream
application/octet-stream
IMAGETYPE_SWC
application/x-shockwave-flash
IMAGETYPE_IFF
image/iff
IMAGETYPE_WBMP
image/vnd.wap.wbmp
IMAGETYPE_XBM
imageZxbm
:Листинг 22.1. Вызов функции image type to__mime_type
<?php
header ("Content-type: ” . iniage_type_to_niime_type (IMAGETYPE_PNG)); ?>
Функция imagetypes
int image types (void)
Возвращает битовое поле, соответствующее форматам изображений, которые поддерживаются текущей реализацией PHP и библиотеки GD. Возвращаемое битовое поле может содержать следующие биты: img_gif | img_jpg I img_png| img_wbmp. Пример вызова функции приведен в листинге 22.2.
:Листинг 22.2. Вызов функции imagetypes
<?php
if (imagetypes () & IMG_PNG)
echo ^Поддержка формата PNG включена'1;
} ?>
Функции создания изображений
Функция imagecreate
resource imagecreate (int x_sizef int. y_size)
Создает пустое изображение размерами x_size и y_size. Возвращает дескриптор созданного изображения. Функция imagecreate()создает изображения с цветовой палитрой, ограниченной 256 цветами. Для создания полноцветных изображений следует пользоваться функцией imagecreatetruecolor ().
Функция imagecreatetruecolor
resource imagecreatetruecolor (int x_sizer int y_size)
Создает пустое полноцветное изображение размерами x_size и y_size. Возвращает дескриптор созданного изображения. Созданное изображение имеет черный фон.
Замечание )
Функция была введена в PHP 4.0.6 и требует для работы библиотеку GD версии 2.0.1 или выше.
Функция imagecreatefromjpeg
resource imagecreatefromjpeg (string filename)
Создает изображение из файла filename формата JPEG. В качестве имени файла может использоваться URL. Возвращает дескриптор созданного изображения. При ошибке создания возвращается пустая строка и выводится соответствующее сообщение.
( 「аме«ние )
Использование URL в качестве имени файла возможно при включенной опции fopen wrappers в файле php.ini В версии PHP для Windows поддержка использования URL в качестве имени файла появилась в версии 4.3.0.
В листинге 22.3 приведен пример создания изображения из JPEG-файла и его вывод непосредственно в браузер.
I »*.«Wj>wj«*mm j(i11 ¥*!吧:曲"SM^^?*£V0.♦审斗 松***-樓/巧11ylyЦ^_¥ «щ
Листинг 22・3・ Функция imagecreatef romjpeg
litniptWt'^in^fvKfl/Kirtftiinli^ .W7^f,i^M*i*“ *J5;#uA$.?g**“*^7rr.+N*^7TiT”F *WKT2i ?,・ ”m,
<?php
$ jpeg_file = 11 test .jpg1*;
// Создаем изображение из JPEG-®afina
$img = imagecreate from jpeg ($jpeg_f ile);
if ($img)
// Выводим изображение в браузер с качеством, равным 50
header (11 Con tent-type: ° . image_type_to_mime_type (IMAGETYPE__JPEG)); imagejpeg($img);
Функция imageinterlace
int imageinterlace (resource image [, int interlace])
Используется при создании изображений формата JPEG. Включает (interlace = 1) н выключает (interlace = 0) чересстрочное формирование изображения. При interlace = 1 изображение создается в формате progressive JPEG. Функция возвращает interlace-бит изображения.
( Замечание )
Изображение в формате progressive JPEG начинает отображаться в браузере до окончания полной загрузки с постепенным увеличением качества.
Функция imagecreatefromgif
resource imagecreatefromgif (string filename)
Создает изображение из файла filename формата GIF. В качестве имени файла может использоваться URL. Возвращает дескриптор созданного изображения. При ошибке создания возвращается пустая строка н выводится соответствующее сообщение.
( Замечание )
Использование URL в качестве имени файла возможно при включенной опции fopen wrappers в файле php.ini. В версии PHP для Windows поддержка использования URL в качестве имени файла появилась в версии 4.3.0.
:Замечание )
Создание изображений в формате GIF удалено из библиотеки GD. Данная функция недоступна начиная с версии 1.6 библиотеки GD.
Функция imagecreatefrompng
resource imagecreatefrompng (string filename)
Создает изображение из файла filename формата PNG. В качестве имени файла может использоваться URL Возвращает дескриптор созданного изображения. При ошибке создания возвращается пустая строка и выводится соответствующее сообшение.
( Замечание
Использование URL в качестве имени файла возможно при включенной опции fopen wrappers в файле phpJni. В версии PHP для Windows поддержка использования URL в качестве имени файла появилась в версии 430.
Функция imagesavealpha
bool imageaavealpha (resource image, bool saveflag)
Применяется для установки прозрачности изображений в формате PNG. При значении saveflag = true создается полноценный альфа-канал. А в противном случае (saveflag = false) в качестве прозрачного может быть установлен только один из цветов изображения.
〔За менаде
Формат РNG-изображений с альфа-каналом прозрачности поддерживается не всеми браузерами.
Функция imagecreatefromgd
resource imagecreatefromgd (string filename)
Создает новое изображение из файла filename формата GD и возвращает дескриптор созданного изображения. В качестве имени файла может использоваться URL
( Замечанме )
Использование URL в качестве имени файла возможно при включенной опции fopen wrappers в файле php.ini. В версии PHP для Windows поддержка использования URL в качестве имени файла появилась в версии 4.3,0.
Функция imagecreatefromgd2
resource imagecreatefromgd2 (string filename)
Создает новое изображение из файла filename формата GD2 и возвращает дескриптор созданного изображения. В качестве имени файла может использоваться URL.
(Замечание )
Использование URL в качестве имени файла возможно при включенной опции fopen wrappers в php.ini. В версии PHP для Windows поддержка использования URL в качестве имени файла появилась в версии 430.
Функция imagecreatefromgd2part
resource imagecreatefromgd2part (string filename, int srcX# int srcY# int width, int height)
Создает новое изображение из прямоугольной области файла filename и возвращает дескриптор созданного изображения. Изображение в файле- источнике должно быть записано в формате GD2. В качестве имени файла может использоваться URL. Функция принимает следующие параметры:
口 srcx» srcY — координаты левой верхней точки прямоугольной области;
□ width, height — ширина и высота прямоугольной области.
〔Замечание )
Использование URL в качестве имени файла возможно при включенной опции fopen wrappers в php.inL В версии PHP для Windows поддержка использования URL в качестве имени файла появилась в версии 4.3.0.
Функция imagecreatefromstring
resource imagecreatefromstring (string image)
Создает новое изображение из потока, передаваемого в строке image. Возвращает дескриптор созданного изображения. При ошибке создания возвращается пустая строка и выводится соответствующее сообщение.
2. Функция imagecreatefromwbmp
resource imagecreatefromwbmp (string filename)
Создает изображение из файла filename формата WBMP. В качестве имени файла может использоваться URL, Возвращает дескриптор созданного изображения. При ошибке создания возвращается пустая строка и выводится соответствующее сообщение.
( Замечание )
Использование URL в качестве имени файла возможно при включенной опции fopen wrappers в phpjni. В версии PHP для Windows поддержка использования URL в качестве имени файла появилась в версии 4*3.0.
I Замечание )
Для работы с изображениями формата BMP необходима библиотека GD версии 1.8 и выше.
Функция imagecreatefromxbm
resource imagecreatefromxbm (string filename)
Создает изображение из файла filename формата ХВМ. В качестве имени файла может использоваться URL Возвращает дескриптор созданного изображения. При ошибке создания возвращается пустая строка и выводится соответствующее сообщение.
( Замечание )
Использование URL в качестве имени файла возможно при включенной опции fopen wrappers в php.ini. В версии PHP для Windows поддержка использования URL в качестве имени файла появилась в версии 4.3.0.
Функция imagecreatefromxpm
resource imagecreatefromxpm (string filename)
Создает изображение из файла filename формата ХРМ. В качестве имени файла может использоваться URL. Возвращает дескриптор созданного изображения. При ошибке создания возвращается пустая строка и выводится соответствующее сообщение,
С Замечание )
Использование URL в качестве имени файла возможно при включенной опции fopen wrappers в php.ini. В версии PHP для Windows поддержка использования URL в качестве имени файла появилась в версии 4.3.0.
Замечание
Функция доступна только в том случае, если PHP скомпилирован с использованием статической версии библиотеки GD.
Функция imagedestroy
int imagedestroy (resource image)
Функьшя освобождает память, занятую изображением image, image — дескриптор изображения.
Функции работы с изображениями
Функция imagecopy
int imagecopy (resource dsjim, resource src_im,
int dst_x, int dst_y, int src_x, int src_v, int srcw, int src_h)
Функция копирует область изображения src lm на изображение dst_im. Верхний левый угол копируемой области находится в точке с координатами 5гс_к, src_y. Параметры src_w и src_h определяют ширину и высоту копируемой области соответственно. Скопированная область вставляется на изображение dst_im в точку с координатами d§t_x, dst_y.
Функция imagecopymerge
int imagecopymerge (resource dst_im, resource src_im, int dstx, int dst_y, int src_x, int src___yf int src_w, int src_h, int pct)
Функция копирует область изображения src__im н объединяет ее с изображением dst_im. Характер объединения передается аргументом pct, который определяет прозрачность накладываемого изображения. Значения pct лежат в диапазоне от 0 до 100. При значении 0 — копирование не будет выполнено (накладываемое изображение полностью прозрачно). При pct = юо функция работает аналогично функции imagecopy(). Остальные параметры функции идентичны параметрам функции imagecopy《).
[ Замечание )
Функция была введена в PHP 4.06
Функция imagecopymergegray
int imagecopymergegray (resource■dst_im, resource src_im, int dsjx, int dst_y, int src к, int src
int src_w, int src_hf int pct)
Функция аналогична функции imagecopymerge(} s но при совмещении областей сохраняются оттенки изображения-источника sru im. Сохранение оттенков происходит вследствие удаления цветовой информации из совмещаемых пикселов изображения-назначения dst_im до операции совмещения.
Замечание )
Функция была введена в PHP 4.06
Функция imagecopyresampled
bool imagecopyresampled (resource dst_im* resource src_im.
int
dstx.
int
dstY,
int
srcX,
int
srcY,
int
dstW,
int
dstH,
int
srcW,
int
srcH)
Функция копирует прямоугольные области с одного изображения на другое. Если значение dst_im равно src_im, то области копируются в пределах одного изображения, но при перекрытии областей копирования и вставки —— результаты непредсказуемы. При копировании пикселы интерполируются таким образом, чтобы сохранить максимальную четкость и яркость. Если размеры исходной области и области назначения различны, то происходит соответствующее сжатие или растяжение копируемой области. Функция принимает следующие параметры:
О dst_im — изображение назначения;
口 src_im — изображение-источник;
dstx, dstY — точка на изображении назначения, определяющая левый верхний угол прямоугольника, в который будет вставляться копируемая область;
dstw, dstH — ширина и высота прямоугольника, в который будет вписана копируемая область;
口 srcx, srcY —— точка на и зображе н и и - и сточ нике, которая определяет левый верхний угол прямоугольника, содержащего копируемую точку;
srcw, srcH —— ширина и высота копируемой области на изображении- источнике.
( Замечание )
Функция неустойчиво работает с неполноцветными изображениями, цветовая палитра которых ограничена 256 цветами.
I)
Функция была введена в PHP 4.0.6 и требует для работы библиотеку GD версии 2.0.1 или выше.
Функция imagecopyresized
int imagecopyresized (resource dst_im, resourceint imagecopyresized (resource dst_im, resourceint dstX, int srcX, int dstW, int srcW,int dstX, int srcX, int dstW, int srcW,int dstY, int srcY, int dstH, int srcH)int dstY, int srcY, int dstH, int srcH)srjini.srjini.
int imagecopyresized (resource dst_im, resource
int imagecopyresized (resource dst_im, resource
int dstX, int srcX, int dstW, int srcW,
int dstX, int srcX, int dstW, int srcW,
int dstY, int srcY, int dstH, int srcH)
int dstY, int srcY, int dstH, int srcH)
srjini.
srjini.
Эта функция аналогична функции image copyre sampled {), но при копировании не происходит интерполяции копируемых пикселов для сохранения четкости и яркости.
:Замечание )
Функция неустойчиво работает с неполноцветными изображениями, цветовая палитра которых ограничена 256 цветами.
Функция imagerotate
resource imagerotate (resource src_im, float angle, int bgdcolor)
Осуществляет поворот изображения src_im на угол angle. В аргументе bgd_coior передается идентификатор фонового цвета, используемый для заливки пустых областей, которые будут получены в процессе поворота изображения.
Функция imagefilter
bool imagefilter (resource src_im, int filtertype [, int args]) Применяет фильтр к изображению src_inL
Функции сохранения и вывода изображений
Функция imagejpeg
int imagejpeg (resource image [, string filename [, int quality]]) Записывает изображение image на диск под именем filename в формате JPEG. Качество сжатия указывается параметром quality, который может
принимать значения от 0 до 100. 0 соответствует максимальному сжатию, но минимальному качеству изображения. По умолчанию параметр quality = 75. Если параметр filename не указан, то изображение выводится в выходной поток браузера. При выводе изображения непосредственно в браузер необходимо передать браузеру mime-тап выводи^fыx данных. Это следует сделать С ПОМОЩЬЮ функции header О :
header (**Content-type: image/jpeg1');
( Замечание )
Для создания файлов в формате progressive JPEG следует включить чересстрочное создание изображений функцией imageinterlace ().
Функция imagegif
int imagegif (resource image [, string filename])
Записывает изображение image на диск под именем filename в формате GIF. Если в изображении отсутствуют прозрачные области, то оно будет записано в формате GlF87a. В противном случае изображение будет записано в формате GIF89a. Если параметр filename не указан, то изображение будет выведено в выходной поток браузера. При выводе изображения непосредственно в браузер необходимо передать браузеру mime-тип выводимых данных. Это следует сделать с помощью функции header ().
header (иContent^type: image/gif”);
〔 Замечание )
Поддержка создания изображений формата GIF была удалена из библиотеки GD, начиная с версии 1.6, В этой и более поздних версиях библиотеки данная функция недоступна.
Функция imagepng
int imagepng (resource image [, string filename])
Записывает изображение image на диск под именем filename в формате PNG. Если параметр filename не указан, то изображение выводится в выходной поток браузера.
При выводе изображения непосредственно в браузер необходимо передать браузеру mime-тип выводимых данных. Это следует сделать с помощью функции header О * header (мContent-type: image/png");
Функция imagegd
int imagegd (resource image [, string filename])
Записывает СВ2-изображение image на диск под именем filename. Если параметр filename не указан, то изображение выводится в выходной поток браузера.
Функция imagegd2
int imagegd2 (resource image [, string filename [,int chunk_5ize [, int type]]])
Записывает G02-изображение image на диск под именем filename. Если параметр filename не указан, то изображение выводится в выходной поток браузера. Необязательный параметр type может принимать значения IMG_GD2_RAW И IMG_GD2__COMPRESSED.
( Замечание
Аргументы chunk_size и type появились в PHP 432.
Функция imagexbm
int imagexbm (resource image, string filename [, int foreground])
Записывает изображение image на диск под именем filename в формате ХВМ. Аргумент foreground содержит идентификатор цвета переднего плана. По умолчанию передний план имеет черный цвет.
Функция imagewbmp
int imagewbmp (resource image [, string filename [, int foreground]])
Записывает изображение image на диск под именем filename в формате WBMP. Если параметр filename не указан, то изображение выводится в выходной поток браузера. При выводе изображения непосредственно в браузер необходимо передать браузеру mime-тип выводимых данных. Это следует сделать С ПОМОЩЬЮ ФУНКЦИИ header о .
header (^Content-type: image/ vnd.wap.wbmp “);
Аргумент foreground содержит идентификатор цвета переднего плана. По умолчанию передний план имеет черный цвет.
[ Замечание )
Работа с изображениями в формате WBMP доступна, если PHP был скомпилирован с поддержкой библиотеки GD версии 1.8 или выше.
Функция image2wbmp
int image2wbmp (resource image [, string filename (, int threshold]])
Сохраняет WBMP-изображение в файл (filename) на диске или выводит его в выходной поток браузера, image ——дескриптор файла.
Необязательный параметр filename представляет собой имя файла, в который будет сохранено изображение. Если этот параметр опущен, то функция выводит файл непосредственно в браузер. При выводе файла в браузер следует передать браузеру тип выводимого контента с помощью функции header().
header(* Content-type: image/vnd.wap.wbmp);
( Замечание )
Поддержка файлов формата WBMP появилась в библиотеке GD версии 1.8.
Функция jpeg2wbmp
int jpeg2wbmp (string jpegname, string wbmpname, int d_height, int d_width, int threshold)
Конвертирует изображение в формате JPEG в изображение формата WBMP. Функция имеет следующие параметры:
□ jpegname — имя файла с изображением формата JPEG;
口 wbmpname —— имя файла с изображением формата WBMP;
口 d_height, d width — высота и ширина создаваемого изображения формата WBMP. —
[ Замечание )
Работа с изображениями в формате WBMP доступна, если PHP был скомпилирован с поддержкой библиотеки GD версии 1.8 или выше.
Функция png2wbmp
int png2wbmp (string pngname, string wbmpname, int d_height, int d_width# int threshold)
Конвертирует изображение в формате PNG в изображение формата WBMP.
Функция имеет следующие параметры:
门 pngname ——имя файла с изображением формата PNG;
口 wbmpname — имя файла с изображением формата WBMP;
□ d_height, d_width ——высота и ширина создаваемого изображения формата WBMP. 一
Замечание
Работа с изображениями в формате WBMP доступна, если PHP был скомпилирован с поддержкой библиотеки GD версии 1.8 или выше.
Функции работы с текстом
Функция imagefontheight
int imagefontheight (int font)
Возвращает высоту символов шрифта font. Высота вычисляется в пикселах, font — идентификатор шрифта.
Функция imagefontwidth
int imagefontwidth (int font)
Возвращает ширину символов шрифта font. Ширина вычисляется в пикселах. font — идентификатор шрифта.
Функция imageftbbox
array imageftbbox (int size, int angle, string font_filef string text (, array extrainfo])
Вычисляет размеры прямоугольника, ограничивающего строку text, написанную шрифтом Free Туре 2. Параметры функции:
size — размер текста в пикселах;
口 angle —— угол поворота текста. Поворот осуществляется против часовой стрелки. Угол 0 градусов соответствует направлению стрелки, указывающей на 3 часа;
fontfile ——имя файла, содержащего шрифт Free Туре 2.
Функция возвращает массив координат левого нижнего н правого верхнего углов прямоугольника. Описание массива приведено в табл. 22.5.
Таблица 225 Описание массива координат вершин прямоугольника
Индекс массива
Описание
0
х-координата левого нижнего угла
1
^координата левого нижнего угла
/2
х^координата правого верхнего угла
3
у-координата правого верхнего угла
工 Примечание ;
Для работы функция требует наличия библиотеки Free Туре 2. Проверить ее наличие можно, выполнив функцию phpinfo(). Сведения о ней будут находиться в разделе gd.
Функция imagettfbbox
array imagettfbbox (int size, int angle, string fontfile, string text)
Вычисляет размеры прямоугольника, в который вписана строка text, напи* санная ТгиеТуре-шрифтом. Параметры функции:
size — размер текста в пикселах;
angle —— угол поворота текста. Поворот осуществляется против часовой стрелки. Угол 0 градусов соответствует направлению стрелки, указывающей на 3 часа;
fontfile ——имя файла, содержащего шрифт TrueType.
Функция возвращает массив координат левого нижнего и правого верхнего углов прямоугольника. Описание индексов массива приведено в табл. 22.5.
Функция imagepsbbox
array imagepsbbox (string text, int font, int size {, int space
【/ int tightness [r float angle]]])
Вычисляет размеры прямоугольника, в который вписана строка text, написанная шрифтом PostScript Typel. Параметры функции:
口 font — идентификатор шрифта PostScript Typel;
口 size — размер шрифта в пикселах;
口 space ——размер пробела, может быть отрш丄ательнс访 величиной. Измеряется в единицах расстояния между символами (1/1000 ет);
□ tightness —— определяет межсимвольное расстояние. Прибавляется к нормальному значению. Измеряется в единицах расстояния между символами (1/1000 ет);
口 angle ——угол поворота текста. Поворот осуществляется против часовой стрелки. Угол 0 градусов соответствует направлению стрелки, указывающей на 3 часа.
Функция возвращает массив координат левого нижнего и правого верхнего углов прямоугольника. Описание индексов массива приведено в табл* 22.5.
Замечание
Функция доступна только в том случае, если PHP был скомпилирован с опцией
——enable-tllib.
Функция imagefttext
array imagefttext (resource image, int size, int angle, int x, int y, int col, string font_file, string text [, array extrainfo])
Записывает строку text на изображение image с помощью шрифта Free
Type 2. Параметры функции:
口 size — размер шрифта в пикселах;
口 angle — угол поворота текста. Поворот осуществляется против часовой стрелки. Угол 0 градусов соответствует направлению стрелки, указывающей на 3 часа;
х, у — координаты точки, с которой начинается запись текста. Приблизительно соответствует левому верхнему углу первого символа;
col — идентификатор цвета текста;
fonjfiie — имя файла со шрифтом.
工 Примечание-^
Для работы функция требует библиотеку Free Туре 2. Проверить ее наличие можно, выполнив функцию phpinfo(). Сведения о ней будут находиться в разделе gd.
Функция imagettftext
array imagettftext (resource image, int size, int angle, int x, int v, int color, string fontfile, string text)
Записывает строку text на изображении image ТшеТуре-шрифтом. Параметры функции:
size — размер шрифта в пикселах;
angle ——угол поворота текста. Поворот осуществляется против часовой стрелки. Угол 0 градусов соответствует направлению стрелки, указывающей на 3 часа;
х, у — координаты точки, с которой начинается запись текста. Приблизительно соответствует левому верхнему углу первого символа;
口 color — идентификатор цвета текста;
口 font_fiie — имя файла со шрифтом.
Функция возвращает массмв координат четырех вершин прямоугольника> в который будет вписан текст. Вершины перечисляются в следующем порядке: нижняя левая, нижняя правая, верхняя правая, верхняя левая.
工 Примечание ;
Для работы функция требует библиотеку Free Туре. Проверить ее наличие можно, выполнив функцию phpinfo (). Сведения о ней будут находиться в разделе gd.
Функция imagepstext
array imagepstext (resource image, string text, int font, int size, int foreground, int background, int x, int у [,int space [, int tightness [, float angle I, int antialias_steps]]]])
Записывает строку text на изображении image PostScript Type 1-шрифтом. Параметры функции:
О Font — идентификатор шрифта;
size — размер шрифта в пикселах;
foreground — идентификатор цвета текста;
口 background — идентификатор цвета, который будет использоваться в качестве фона при сглаживании границ символов;
к, у —— координаты точки, с которой начинается запись текста. Приблизительно соответствует левому верхнему углу первого символа;
口 space — размер пробела, может быть отрицательной величиной. Нзмеря- ется в единицах расстояния между символами (1/1000 ет);
口 tightness ——определяет межсимвольное расстояние. Прибавляется к нормальному значению. Измеряется в единицах расстояния между символами (1/1000 ет);
口 angle — угол поворота текста. Поворот осуществляется против часовой стрелки. Угол 0 градусов соответствует направлению стрелки, указывающей на 3 часа;
antialias steps — определяет количество цветов, которые используются для сглаживания границ символов. Допустимы значения 4 и 16. Значение 16 имеет смысл использовать только для шрифтов большого размера (более 20 пикселов), так как это увеличивает нагрузку на компьютер.
Функция возвращает массив вершин прямоугольника, в который будет вписан текст. Описание элементов массива приведено в табл. 225
:Замечание )
Функция доступна только в том случае, если PHP был скомпилирован с опцией —enable-tllib*
Функция imagepsloadfont
int imagepsloadfont (string filename)
Загружает шрифт в формате PostScript Typel из файла filename. При успешной загрузке возвращается идентификатор шрифта. В противном случае возвращается false.
〔 Замечание )
Функция доступна только в том случае, если PHP был скомпилирован с опцией --enable-t11ib.
Функция imageloadfont
int iiaageloadfont (string file)
Загружает пользовательский растровый шрифт из файла file. Возвращает идентификатор шрифта. Он всегда больше 5 и поэтому не конфликтует со встроенными шрифтами, идентификаторы которых лежат в диапазоне о О до 5.
Формат шрифта двоичный и поэтому должен быть сгенерирован на том типе процессора, на котором выполняется PHP.
Формат файла file, в котором пользователь может определить свой растровый шрифт, разобран в табл. 22.6,
Таблица 22.6. Формат файла шрифта
Позиция
Тип
Описание
Байты 0—3
Int
количество символов в шрифте
Байты 4—7
Int
значение первого символа в шрифте (часто это 32 для пробела)
Байты 8—11
Int
ширина символов в пикселах
Байты 12—15
Int
высота символов в пикселах
Байты с 16
char
массив символьных данных, один байт на пиксел каждого символа общим количеством (количество символовхшмринахвысота) байт
Функция imagepscopyfont
int imagepscopyfont (int fontindex)
Создает копию шрифта PostScript Typel, загруженного ранее функцией imagepsloadfont(}. При удачном копировании будет возвращен идентификатор скопированного шрифта. В противном случае будет возвращено false. Функция копирования шрифтов используется для модификации шрифта без изменения оригинала. Модификация шрифтов осуществляется с помощью операций растяжения, сжатия, поворота, изменения кодировки и т. п.
{ Замечание )
При использовании этой функции полученные шрифты должны быть освобождены в обратном порядке с помощью функции imagepsf reefont (). Иначе скрипт "зависнет" при выполнении.
; Замечание )
Функция доступна только в том случае, если PHP был скомпилирован с опцией —enable-tllib<
Функция imagepsfreefont
void imagepsfreefont (int fontindex)
Освобождает память, занятую шрифтом font index, fontindex — идентификатор шрифта в формате PostScript TypeL
( Замечание )
Функция доступна только в том случае, если PHP был скомпилирован с опцией —enable-tllib.
Функция imagepsencodefont
int imagepsencodefont (int font_index, string encodingfile)
Загружает вектор кодировки из файла, указанного в параметре encodingfile, и изменяет вектор кодировки PostScript TypeI шрифта font_index. Изменение вектора кодировки необходимо при использовании любого неанглийского шрифта. Формат файла encodingfile описан в документации к библиотеке Tllib.
〔 Замечание )
При постоянном использовании данной функции более правильным рашени- ем является установка параметра ps. default_encoding а конфигурацион-
ном файле. В этом случае все создаваемые шрифты автоматически получат правильную кодировку.
[ Замечание )
Функция доступна только в том случае, если PHP был скомпилирован с опцией
——enable-tllib.
Функция imagepsextendfont
bool imagepsextendfont (int font_indexr float extend)
Сжимает или растягивает PostScript Type 1-шрифт font_index на величину extend.
:Замечание )
Функция доступна только в том случае, если PHP был скомпилирован с опцией
——enable-tllib.
Функция imagepsslantfont
bool imagepsslantfont (int font_index, float slant) Осуществляет наклон PostScript TypeI-шрифта font_index на величину slant.
[ Замечание )
Функция доступна только в том случае, если PHP был скомпилирован с опцией ——enable-tllib.
Функция imagechar
int imagechar (resource image, int font, int x, int y, string c, int color)
Горизонтальное рисование символа. Рисует первый символ из строки с на изображении image. Параметры функции:
font — идентификатор шрифта. Если font равен I, 2, 3, 4 или 5, то используется встроенный шрифт (большее число соответствует шрифту большего размера);
х, у — координаты левого верхнего угла рисуемого символа;
color — идентификатор цвета символа.
Функция imagecharup
int imagecharup (resource image, int font, int x, int y, string c, int color)
Вертикальное рисование символа. В остальном параметры и поведение функции соответствуют функции imagechar О .
Функция imagestring
int imagestring (resource image, int font, int x, int y, string s, int color)
Горизонтальное рисование строки s на изображении image. Функция принимает следующие параметры:
口 font — идентификатор шрифта. Если font равен 1, 2, 3, 4 или 5, то используется встроенный шрифт (большее число соответствует шрифту большего размера);
口 % у — координаты левого верхнего угла рисуемого символа;
口 color — идентификатор цвета символа.
Функция imagestringup
int imagestringup (resource image, int font, int x, int y, string s, int col)
Вертикальное рисование строки. В остальном параметры и поведение функции соответствуют функции imagestring ()・
Рассмотрим пример работы с текстом (листинг 22.4).
<?php
// Создание полноцветного изображения размером 350 на 200 пикселов $img = imagecreatetruecolor{350, 200);
if ($img)
{
Установка 2 цветов, которые будут использоваться для рисования $green = imagecolorallocate($intg, 0* 255, 0};
$yellow = imagecolorallocate($img, 255, 250, 0);
// Рисование текста TrueType-шрифтом arbat.ttf
// Файл co шрифтом находится в той же директории, что и сам скрипт $box = imagettftext($img, 20, 10, 10, 150, $yellow, "arbat.ttf' "叩мфт True Type");
// Для дальнейшего рисования будем использовать
// встроенный шрифт с идентификатором 4
$font = 4;
// Вторая текстовая строка должна быть нарисована непосредственно
/ / над первой строкой *
// Для этого из у-координаты правого верхнего
// угла прямоугольника, ограничивающего только что написанную
// строку, вычитаем высоту встроенного шрифта font, которым будет
// нарисована вторая строка.
$height = $box[5] - imagefontheight($font);
// Рисуем вторую строку
imagestring($img, $font, 40, ^height, f,PHP and GDLib", $green);
// Выводим изображение в браузер
header ("Content-type:"・ image_type_to_mime_type (IMAGETYPE_PNG)); imagepng($img);
} ?>
Функции настройки рисования
Функция imagealphablending
bool imagealphablending (resource image# bool blendmode)
Определяет 2 режима прорисовки полноцветных изображений. При включенном режиме смешивания (blendmode = true) при рисовании поверх существующего изображения учитывается прозрачность (альфа-канал). В данном режиме цвет результирующего пиксела получается совмещением цветов нижележащего и накладываемого поверх пикселов. Параметры смешивания определяются прозрачностью накладываемого пиксела (прозрачность устанавливается в параметрах функций рисования). При отключенном режиме смешивания накладываемый пнксел полностью заменяет исходный. По умолчанию полноцветные изображения создаются с опцией blendmode=t rue,
[Замечание)
Режим смешивания недоступен для неполноцветных изображений.
[За меч5 HHe)
Функция была добавлена s PHP 406 и требует для работы библиотеку GD версии 2.0.1 или выше.
Функция imagesetthickness
bool imagesetthickness (resource image, int thickness}
Устанавливает тол ши ну линий при рисовании, thickness — значение толщины в пикселах. Возвращает true при успешном выполнении и false при ошибке.
( Заменанне )
Функция появилась в PHP 4.0.6 и требует библиотеки GD версии 2.0.1 или выше.
Функция imagesetstyle
bool imagesetstyle (resource image, array style)
Устанавливает стиль, используемый всеми функциями рисования линий и контуров (например, imageiine{)и imagepolygon о) при рисовании специальными цветами img_color_styled и 工MG_C0L0R_6TYLEDBRUSHED・ style — массив пикселов, определяющий стиль линии.
(Замечание )
Функция появилась в PHP 4.06
Функция imagesetbrush
int imagesetbrush (resource image, resource brush)
Устанавливает кисть, используемую при рисовании линий и контуров, например, с ПОМОЩЬЮ функций image line О И imagepolygon {), Когда используются специальные цвета — img_color_brushed и 工mg_color_styledbrushed.
( Замечанме )
Функция появилась в PHP 4,0.6,
Функция imagesettile
int imagesettile (resource image, resource tile)
Устанавливает изображение title в качестве фоновой заливки на изображении image. Фоновое изображение заливки применяется всеми функциями заполнения областей (такими как: imagefill () Н imagef i lledpolygon ()) при использовании специального цвета img_color_tiled. Фоновое изображение тиражируется на всю заливаемую область. Если присвоить фоновому изображению прозрачный цвет функцией imagecoiortransparent (), в про* зрачных областях будет цвет нижележащего пиксела.
29 Зак 118J
[ Замен5HMe )
Функция появилась в PHP 4.06
Функция imagelayereffect
bool imagelayereffect (resource image, int effect)
Устанавливает флаг смешивания для использования в эффектах наложения слоев.
Функция imageantialias
bool imageantialias (resource im, bool on)
Включает и отключает опцию сглаживания при рисовании на изображении i®
Функции рисования
Функция imagesetpixel
int imagesetpixel (resource image, int x, int y, int color)
Рисует пиксел цветом color в точке с координатами х, у на изображении image.
Функция imageline
int imageline (resource image, int xl, int yl, int x2/ int y2# int color)
Рисует линию цветом color из точки с координатами xl, у1 в точку с координатами х2, у2.
Функция imagedashedline
int imagedashedline (resource image, int xl. int yl, int x2, int y2, int color)
Функция рисует штрихпунктирную линию нз точки с координатами 其工,yi в точку с координатами х2, у2* Цвет линии определяется идентификатором цвета color.
ЗамХчанве
Использовать эту функцию не рекомендуется. Вместо нее следует использовать комбинацию функций imageset style () И image line () *
Функция imagerectangle
int imagerectangle (resource image, int xl, int yl, int x2, int y2, int col)
Рисует на изображении image прямоугольник с левым верхним углом в точке xis у1 и правым нижним углом в точке х2, у2. col — идентификатор цвета прямоугольника.
Функция imagepolygon
int imagepolygon (resource image, array points, int nuin^points, int color)
Рисует многоугольник на изображении image. Массив points содержит координаты х, у вершин многоугольника. Параметр nuxn_points определяет общее количество вершин. Цвет многоугольника определяется идентификатором цвета color.
Функция imagearc
int imagearc (resource image, int ex, int cy, int w, int h, int s, int e, int color)
Функция рисует дугу эллипса с центром в точке сх и су шириной w и высотой h. Параметры s и е определяют сектор дуги, s — угол начала сектора, е ——угол конца сектора. Угол отсчитывается против часовой стрелки. Угол О градусов соответствует положению стрелки, указывающей на 3 часа.
Функция imageellipse
int imageellipse (resource image, int ex, int cy, int w, int h, int color)
Функция рисует эллипс на изображении image. Центр эллипса находится в точке с координатами сх, су. Ширина и высота эллипса определяются координатами w, h. Цвет контура определяется идентификатором цвета color.
С Замечание )
Функция была введена в PHP 4.0.6 и требует для работы библиотеку GD версии 2.0.2 или выше
Функция imagefilledrectangle
int imagefilledrectangle (resource image, int xl, int yl, int x2, int y2, int color)
Рисует закрашенный прямоугольник на изображении image. Прямоугольник определяется координатами левого верхнего и правого нижнего углов — xl.
yi и х2, у2 соответственно. Цвет прямоугольника определяется идентификатором цвета color.
Функция imagefilledpolygon
int imagefilledpolygon (resource image, array points, int num_points, int color)
Рисует закрашенный многоугольник на изображении image. Массив points содержит координаты вершин многоугольника. Параметр num_points определяет общее количество вершин. Цвет многоугольника определяется идентификатором цвета color,
Функция imagefilledarc
bool imagefilledarc (resource image, int ex, int cy, int w, int h, int s, int e, int color, int style)
Рисует сектор эллипса н осуществляет его заливку. Функция принимает следующие параметры:
口 сх, су — координаты центра эллипса;
w, h — ширина и высота эллипса;
口 color — идентификатор цвета;
з, е — начальный и конечный углы сектора. Углы задаются в градусах. Отсчет углов производится против часовой стрелки. Позиция, равная О градусов, соответствует положению стрелки, указывающей на 3 часа;
口 style — определяет стиль рисования и может принимать следующие значения, которые перечисляются через побитовое ИЛИ:
工MG—ARjp工е — соединяет начальную и конечную точки на окружности кривой линией, повторяющей окружность;
img_arc_chord — соединяет начальную и конечную точки на окружности прямой линией;
img_arc_nofill — рисуемый сектор не закрашивается;
img_arc_edged ——начальная и конечная точки соединяются с центром окружности.
Значения img__arc_pie и 工mg_arc_chord взаимоисключающие и не могут применяться вместе.
(З.ме^ние )
Функция была введена в PHP 4.0.6 и требует для работы библиотеку GD версии 2.0.1 или выше.
Функция imagefilledellipse
bool imagefilledellipse (resource image, int ex, int cy, int w, int h, int color)
Рисует закрашенный эллипс с центром в точке с координатами сх, су. Шириной w и высотой h. Цвет контура и заливки определяется идентификатором цвета color.
:Замечание )
Функция была введена в PHP 4,0.6 и требует для работы библиотеку GD версии 2.0.1 или выше.
Функция imagefill
int imagefill (resource image, int x, int y, int color)
Функция производит заливку области внутри контура, на изображении image. Контур определяется пикселами любого цвета, отличного от цвета пикселов, находящихся внутри контура. Цвет пикселов внутри контура берется из точки с координатами х, у. color ——идентификатор цвета.
Функция imagefilltoborder
int imagefilltoborder (resource image, int x, int y, int border, int color)
Выполняет запивку области внутри контура, определяемого пикселами цветом border, border — идентификатор цвета контура. Цвет заливки определяется идентификатором цвета color* к, у — координаты точки внутри заливаемой области.
В листинге 22.5 показаны примеры работы с функциями рисования. После выполнения данного скрипта в браузере должно появиться изображение, представленное на рис. 22.1.
i Листинг 22.5. Работа с функциями рисования
書乳.;*乜*,/;%: * *::f;*.而;文3.Y";;(*;Гm:15^^*;;话譚心济.f ?^;指,.日产》、'3 'ii* 洛入上ШХ* ;**■**=*;*;一二.:5": ••• ;/.:丸 “,. …
<?php
// Создаем полноцветное изображение
$img - imagecreatetruecolor(350, 200); if ($img)
// Устанавливаем черный цвет
$black = imagecolorallocate($img, 0, 0, 0);
// Устанавливаем белый цвет $white = imagecolorallocate($img, 255, 255, 255); // Устанавливаем цвет, которым будет нарисован дом Shouse = iniagecoloral locate ($img, 96, 57, 19); // Устанавливаем цвет, которым будет нарисована крыша $roof = imagecolorallocate($img, 154, 87, 54); // Устанавливаем цвет, которым будет нарисовано солнце $yellow = imagecolorallocate($img# 255, 250, 0); // Заливаем белым цветом фон изображения imagefill($imgf 。, 0, $white);
// Рисуем контур дома imagerectangle ($img, 70, 150, 120, 110, $black); // Рисуем контур окна imagerectangle($img, 85, 135, 105, 125, $black); // Закрашиваем дом imagefilltoborder($img, 71,工49, $black, $house); // Создаем массив точек, которые образуют крышу $points - array(
110, 95, 60, 120, 110 ); // Рисуем крышу image filledpolygon($ img # $points, 3, $roof); // Рисуем солнце imagefilledellipse($img, 150, 40, 20, 20, $yellow); // Создаем стиль линии Sstyle = array($house# $house, $house, $white, $white, $white); imagesetstyle ($img, $style);
// Устанавливаем толщину линии в 4 пиксела imagesetthickness ($img, 4);
// Рисуем линию созданным стилем imageline ($img, 0,】55, 200, 155, IMG_COLOR_STYLED ); 7/ Устанавливаем толщину линии в 1 пиксел imagesetthickness ($img, 1);
// Загружаем картинку, которая будет кистью, // используемой при рисовании $brush = imagecreatefromgif (nbrush.gif*');
// Устанавливаем кисть imagesetbrush ($img, $brush);
// Рисуем линию созданной кистью
iinageline ($img, 0, 163, 200, 163, IMG_COLOR_STYLEDBRUSHED);
// Выводам изображение в браузер
header ("Content-type: " . iroage_type_to_mime_type (工MAGETYPE_PNG)); imagepng ($img);
Рис. 22.1. Изображение, полученное в результате выполнения скрипта из листинга 22.5
Функции для работы с цветовой информацией
Функция imageistruecolor
bool imageistruecolor (resource image)
Определяет, является ли изображение image полноцветным.
Функция imagetruecolortopalette
void imagetruecolortopalette (resource image, bool dither, int ncolors)
Конвертирует полноцветное изображение в изображение с цветовой палитрой с ограниченным числом цветов:
口 dither — управляет параметром dithering (размытие) создаваемого изображения;
口 ncoiors — максимальное количество цветов, которое будет содержаться палитре.
Замечание )
Параметр dither используется а изображениях с цветовой палитрой для увеличения количества оттенков. При включении этой опции происходит •'перемешивание'1 двух цветов для получения третьего цвета. Включение этой опции может привести к появлению точек на изображении.
( Замечание )
Функция появилась в PHP 4.0.6 и требует библиотеки GD версии 2.0.1 или выше.
Функция imagecolorallocate
int imagecolorallocate (resource image, int rad, int greenf int blue)
Функция возвращает идентификатор цвета для изображения image, представленный RGB-компонентами. Полученный идентификатор цвета далее может использоваться для функций рисования. Функция imagecolorallocate () должна вызываться для каждого цвета, применяемого при рисовании изображения image.
Параметры red, green, blue являются RGB-компонентамн, и их значения лежат в диапазоне от 0 до 255 или от 0x00 до OxFF в шестнадцатеричном формате.
( Замечание )
Первый вызов функции imagecolorallocate () устанавливает цвет фона.
Функция imagecolorallocatealpha
int imagecolorallocatealpha (resource image, int red, int green, int blue, int alpha)
Поведение функции н ее параметры аналогичны поведению и параметрам функции imagecolorallocate (), за исключением параметра alpha, который определяет прозрачность.
Значения параметра alpha лежат в диапазоне от 0 до 127. Значение alpha = =0 определяет полную непрозрачность. Значение alpha = 127 соответствует полностью прозрачному цвету.
Функция imagecolordeallocate
int imagecolordeallocate (resource image# int color)
Функция уничтожает объект цвета, выделенный ранее функцией imagecoio- rallocate() ИЛИ imagecolorallocatealpha().
Функция imagecolorat
int imagecolorat (resource image, int к, int y)
Функция возвращает индекс цвета пиксела в цветовой палитре, который находится на изображении image в точке с координатами х, у.
Функция imagecolorsforindex
array imagecolorsforindex (resource image, int index)
Функция возвращает цвет, содержащийся в индексе index цветовой палитры изображения image. Цвет возвращается в виде ассоциативного массива с ключами red, green, blue.
Функция imagecolorresolve
int imagecolorresolve (resource image, int red, int green, int blue} Функция ищет на изображении запрошенный цвет и возвращает его идентификатор. Если запрошенный цвет не найден, то возвращается идентификатор ближайшего цвета. Запрашиваемый цвет определяется RGB-компонентами red, green, blue>
Функция imagecolorresolvealpha
int imagecolorresolvealpha (resource image,
int red, int green, int blue, int alpha) Поведение функции аналогично функции imagecolorresolve (у, но в запрашиваемом цвете учитывается параметр прозрачности alpha.
[ Замечание )
Функция imagecolorclosestalpha () была добавлена в PHP 4.0.6 и требует для работы библиотеку GD версии 2.0.1 и выше.
Функция imagecolorexact
int imagecolorexact (resource image, int redf int green, int blue) Возвращает идентификатор цвета из цветовой палитры изображения image. Искомый цвет задается RGB-компонентами red, green» blue. Если искомый цвет не найден в цветовой палитре изображения, то возвращается —1.
Функция imagecolorexactalpha
int imagecolorexactalpha (resource image, int red, int green, int blue, int alpha)
Функция аналогична функции imagecolorexact(), но в компоненты искомого цвета добавляется параметр прозрачности alpha.
Функция imagecolorclosest
int imagecolorclosest (resource image, int redf int greenr int blue)
Функция возвращает из цветовой палитры изображения image идентификатор цвета, наиболее похожего на цвет RGB, компоненты которого передаются параметрами red, green, blue.
Функция imagecolorclosestalpha
int imagecolorclosestalpha (resource image,
int red, int green, int blue, int alpha)
Функция аналогична функции imagecolorclosest (), но при нахождении ближайшего цвета учитывается параметр прозрачности alpha.
〔 Замечание )
Функция imagecolorclosestalpha () была добавлена в PHP 4.0*6 и требует для работы библиотеку GD версии 2.0.1 или выше.
Функция imagecolorclosesthwb
int imagecolorclosesthwb (resource image, int red, int green, int blue)
Возвращает индекс градации серого для цвета, передаваемого RGB-компонентами.
: Замечание )
Функция доступна только в том случае, если PHP скомпилирован с использованием статической версии библиотеки GD.
Функция imagecolorset
bool imagecolorset (resource image, int index, int red, int green, int blue)
Функция производит модификацию цветовой палитры изображения image. В конкретный индекс (параметр index) палитры устанавливается цвет, передаваемый RGB-компонентами red, green, blue.
Код, представленный в листинге 22.6, загружает изображение в формате GIF и меняет цвет одного из индексов цветовой палитры. Для того чтобы пример был нагляднее, загружаемое изображение должно иметь большие области, закрашенные однородным цветом, например, как на рис. 222 В левой части представлено исходное изображение из файла img.gif, а в правой части ——модифицированное.
龔电用弊лм*мшмшнйныдд#ц*шмддммадшмцзстдкА*'ммйцмдды^&ыдАЬ81АЦмцмымдяшймш*и修ft^^^tfi 融抬 ¥Я,绊电'修01УДИ^吗史*举読船也НМШШДМИШ^М£ ;Листинг 22.6. Модификация палитры цветов„ 干&±
<?php
// Загрузка изображения, имеющего палитру цветов
$im = imagecreatefromgif (,fimg.gifм};
// Получение индекса цвета из палитры цветов
// для пиксела в точке с координатами 150, 100
$index = imagecolorat($imf 150, 100};
// Замена цвета в палитре на красный цвет imagecolorset($im, $index, 255, 0, 0);
// Вывод изображения в браузер
header (MContent-type: ” . image_type_to_mime_type (IMAGETYPE_PNG));
imagepng ($im);
Рис. 22.2. Пример работы скрипта, изменяющего палитру цветов
Функция imagecolortransparent
int imagecolortransparent (resource image [, int color])
Устанавливает прозрачный цвет на изображении image. Области изображения, прорисованные передаваемым цветом, после применения этой функции станут прозрачными. Цвет передается параметром color в виде идентификатора, возвращаемого функцией imagecolorallocate().
Функция imagecolorstotal
int imagecolorstotal (resource image)
Возвращает количество цветов в цветовой палитре изображения image. Изображение image ДОЛЖНО быть СОЗДЯНО функций imagecreate()> ЕСЛИ изображение image является полноцветным, то возвращается 0.
Функция imagecolormatch
bool imagecolormatch (resource image1, resource image2)
Проверяет на сходство 2 изображения, одно из которых является полноцветным, а цветовой диапазон второго ограничен палитрой цветов. В случае, если изображения признаются похожими — возвращается true, в противном случае — false:
口 image 1 — полноцветное изображение;
□ images — изображение с палитрой цветов.
Оба изображения должны иметь одинаковый размер по длине и 山нрнне.
Функция imagepalettecopy
int imagepalettecopy (resource destination, resource source) Копирует цветовую палитру из изображения-источника source в изображение-приемник destination.
Функция imagegammacorrect
int imagegammacorrect (resource image, float inputgamma, float outputgamma)
Функция, осуществляющая гамма-коррекцию изображения image. Параметры inputganuna И outputgamma СООТВеТСВуЮТ ВХОДНОЙ И ВЫХОДНОЙ гаммам.
〔 Заменанме )
Гамма — нелинейность яркостной характеристики изображения. Гамма-коррек* ция ——процесс устранения данного вида нелинейности.
В листинге 22*7 приведен пример работы с расемотреиными функциями.
i Листинг 22.7. Работа с цветом
<?php
// Создаем полноцветное изображение
$img = imageereatetruecolor(350, 200);
if ($img)
// Устанавливаем цвета
$white = imagecolorallocate($imgг 2 55, 255, 2 55);
$blue = imagecolorallocate($img, 100, 100, 255);
/ / Устанавливаем желый цвет с 50% прозрачностью
$yellow = imagecolorallocatealpha ($img, 255, 250, 0, 63);
/ / Заливаем белым цветом фон изображения
imagefill($img, 0, 0, $white);
iinagefilledrectangle ($img, 50, 50, 150, 150, $bJLue);
imagefilledrectangle ($img, 100, 70, 200, 180, $yellow);
// Определяем цвет пиксела в точке 51, 100
//и рисуем этим цветам прямоу голь кик
$color 二 imagecolorat($img, 51, 100);
imagefilledrectangle ($img, 200, 10, 22。,20, $color);
// Все что нарисовано белым цветом делаем прозрачным
// В данном случае прозрачным будет фон imagecolortransparent ($img, $white);
// Сохраняем изображение б файл
imagepng($imgf "1 .png");
}
?>
Другие функции
Ниже перечислены функции, не вошедшие ни в одну из перечисленных категорий.
Функция iptcembed
array iptcembed (string iptedata, string jpeg_file__name [,int spool])
Внедряет двоичные данные iptedata в заголовок файла в формате JPEG. Значением параметра jpeg_fiie_name является имя JPEG-файла.
Функция ipteparse
array ipteparse (string iptcblock)
Функция осуществляет разбор и выдачу информации, представленной в двоичном виде в IPTC-блоках. Данные возвращаются в виде массива. Если IPTC-блок не найден, то возвращается false. Для примера получения данных В виде [РТС-блоКОВ СМ. функцию getimagesizeO .
Глава 23
Работа с Flash.
Библиотека ming
Библиотека ming предназначена для создания flash-фильмов. Она поддерживает почти все основные объекты и свойства Flash 4, например такие, как: формы, градиенты, изображения в форматах JPEG и PNG, трансформацию форм из одной в другую, текст, кнопки, механизм событий, movie-фильмы, загрузку потокового звука в формате MP3, трансформацию цветов. Единственная неподдерживаемая опция ——это звуковые события.
Библиотека ming — это разработка с открытым кодом по соглашению LGPL Новости об этой библиотеке, примеры кода, новые версии доступны на сайте, посвященном библиотеке ming, по адресу http://ming.soiircefbrge.net.
На момент написания книги библиотека находилась в стадии разработки, поэтому к тому времени, когда вы будете держать книгу в руках, в библиотеке возможны изменения. Кроме того, с выходом более новых версий могут измениться объекты, их методы, а также их поведение. Данная глава основана на версии 0*2а, работающей с PHP 5.
Библиотека ming была введена в PHP с версии 4.05
Установка
Библиотека ming является кроссплатформенной, поэтому в данной главе будет рассмотрена ее установка для систем UNIX и Windows.
Установка под Windows
Проверьте наличие у вас библиотеки MING (php_ming.dll) в каталоге с установленным PHP. Она должна находиться в подкаталоге ext или extension.
Если у вас нет этой библиотеки ming, то вы можете найти ее по адресу http://ming.sourceforge.net. Будьте внимательны — вам необходима библиотека, скомпилированная под Win32.
( Заменанме )
Версии библиотеки различаются поддержкой языка PHP. На момент написания книги с PHP 5 работала библиотека php_mi门g.dll версии 0.2а размером 192 512 байт. Более новая версия 0.3 работала только с PHP 4.
После того как архив загружен, библиотеку (php_ming.dll) следует распаковать из архива в директорию, где находятся расширения PHP. Например, если корневой путь к PHP совпадает с C:\php\, то расширения должны находиться в директории C:\php\ext\.
〔 Замечание )
Если установка PHP 5 производится из полного дистрибутива, распространяемого как ZIP-архив (zip)r а не из усеченной версии, распространяемой как инсталлятор (ехе), в директории exh динамическая библиотека php_ming.dll уже присутствует.
Далее необходимо подключить библиотеку- Для этого следует перейти в каталог Windows и отредактировать файл php.ini. Для работы с библиотекой в нем следует рас ком ментировать строку (убрать точку с запятой в начале строки):
extens ion=php_ming.dll
После чего необходимо проверить, настроены ли в php.ini пути к библиотекам расширения. Значение директивы extens 1оп_ d±r должно указывать на ту директорию, где лежат библиотеки расширений. Для вышеприведенного примера» когда библиотеки находятся в папке C:\php\ext\, директива extension_diг (в php.ini) должна принимать значение: extension__dir =**с: /php5/extм
Для вступления изменений в силу следует перегрузить сервер Apache. При успешной установке библиотеки, в таблице, генерируемой функцией phpinfo(), должен появиться раздел ming, В нем вы можете посмотреть версию установленной библиотеки.
Установка под UNIX-системы
Для установки библиотеки ming в UNIX-подобной операционной системе архив с библиотекой, полученный по адресам, приведенным в предыдушем разделе, следует распаковать в какую-либо директорию.
Следует отредактировать файл Makefile, исправив в нем значение директивы prefix, указав путь к файлам PHP.
PREFIX="/path/to/phpH
После этого в директории, в которой был распакован архив, следует выполнить две следующие команды
make
make install
В результате чего будет скомпилирована динамическая библиотека Hbming.so, которую следует поместить в директорию /usr/lib/. Кроме этого, заголовочный файл ming.h следует поместить в /usr/include/.
После чего следует изменить конфигурационный файл php.ini, как это показано в предыдущем разделе, посвященном установке библиотеки под Windows, и перезагрузить сервер Apache.
Классы объектов библиотеки ming
В библиотеку ming входят 13 классов объектов:
swFMovie ——flash-фильм. Является контейнером для всех объектов и имеет временную шкалу;
口 swFShape — формы. Методами этого объекта можно рисовать прямые и кривые линии, осуществлять заливку форм;
口 swFDispiayitem — ссылки на объекты всех типов, добавленные во flash- фильм. Методами объектов swFDispiayitem осуществляются трансформации объектов — перемещение, поворот, масштабирование, наклон, установка мменн для работы со скриптами, трансформации цветов и удаления объектов из flash-фильма;
swFGradient —— градиент. Данный объект позволяет осуществлять градиентную заливку форм, как линейную, так и радиальную;
口 swFBitmap — изображения. Объект поддерживает загрузку изображений из форматов JPEG и PNG;
口 swFFiii — заливка. Объект создается при добавлении заливки методами объекта swFShape. С помощью методов объекта swFFiii осуществляются трансформации над заливкой — перемещение, масштабирование, пово рот, наклон;
口 swFMorph ——объект предназначен для создания трансформации одной формы в другую;
swFText ——объект предназначен для работы с текстом и его свойствами (шрифт, высота, цвет и т. п);
swFFont — объект предназначен для загрузки шрифта из внешнего файла;
口 swFTextFieici ——объект предназначен для работы с полями для ввода текста и установки их свойств;
口 swFSprite ——movie-фильм. Этот объект входит в состав flash-фильма, но имеет собственные объекты, анимацию и временную шкалу, независимые от главного flash-фильма;
口 swFButton — кнопка. С ее помощью можно обрабатывать события (перемещение указателя мыши, щелчок мышью на кнопке, щелчок мышью за пределами кнопки и др.) с помощью внутреннего языка flash — action script;
swFAction —— объект для работы с внутренним языком flash — action script.
Далее будут подробно рассмотрены все объекты и методы работы с ними. А в конце главы будет приведено несколько примеров кода.
Объекты SWFMovie
Рассмотрим объекты класса SWFMovie и методы работы с ними.
Конструктор SWFMovie
SWFMovie new SWFMovie(void)
Создание нового объекта flash-фильма.
Метод output
output(void)
Осуществляет вывод объекта SWFMovie (flash-фильма) в выходной поток браузера. Предварительно следует определить mime-тип выводимых данных. Это можно сделать следующей строкой:
header('Content-type: application/x-shockwave-flash*);
( Замечание )
При выводе flash-фильма непосредственно в браузер размеры единиц измерения flash-фильма, а следовательно, и размеры всех его элементов, масштабируются в зависимости от размера окна браузера и размера flash-фильма, установленного методом set Dimens ion (). Если вы хотите получить в браузере те размеры, что вы задавали при рисовании, то вам следует сохранять flash- фильм на диск и затем подключать на страницу с помощью HTML-кода.
Метод save
save(string filename)
Записывает объект SWFMovie (flash-фильм) на диск под именем filename.
Метод add
SWFDisplayltem add(resource instance)
Добавляет объект любого типа в flash-фильм. Возвращает указатель на добавленный объект в виде объекта swroisplayitem. Можно добавить один и тот же объект несколько раз и получить указатели на отдельные добавленные экземпляры. Объект instance может быть объектом любого типа: формы, текст, шрифты и т. п.
Метод remove
remove(SWFDisplayitem i)
Удаляет объект, указатель которого передан методу из flash-фильма*
Метод nextFrame
nextFrame ()
Перемещение к следующему кадру анимации.
Метод setBackground
setBackground(int rf int g, int b)
Установка цвета фона flash-фильма. В качестве параметров передаются значения компонент цветовой модели RGB. Их значения лежат в диапазоне от 0 до 255.
Метод setRate
setRate(int frameRate}
Установка частоты кадров фильма. В качестве параметра передается числовое значение, равное количеству кадров в секунду.
Метод setDimension
setDimension(integer width, integer height)
Устанавливает размеры клипа по ширине (width) и высоте (height). Значения размеров передаются а пикселах.
Метод setFrames
setFrames(integer numberOfFrames)
Установка общего числа кадров в фильме.
Метод streamMp3
streamMp3(file fp)
Загружает музыкальный файл в формате MP3 в фильм в виде потока. При потоковом методе загрузки воспроизведение начинается сразу же после
начала загрузки. Принимает в качестве параметра дескриптор открытого файла:
streamMp3 (fopen (Hf Непате-трЗ'*, 'Т'))
Объекты SWFShape
swFShape — объект-форма. С помощью методов этого объекта можно рисовать произвольную форму из прямых и кривых линий. Рассмотрим методы объекта SWFShape.
Конструктор SWFShape
SWFShape new SWFShape(void)
Создание нового объекта SWFShape.
Перед тем как приступить непосредственно к рисованию, необходимо настроить стиль линии и позицию рисования.
Стиль линии определяет внешний вид линий. Если стиль линии не установлен, то нарисованные линии не будут видньь Иногда это бывает нужно, например, для заливки формы, у которой не видно контуров.
Позиция рисования — это точка, с которой начнется рисование линий. По умолчанию позиция рисования устанавливается в точке с координатами 0; 0. Изменить позицию рисования можно с помощью методов movePenToO и moveРеп ().
Метод setLIne
setLine(int width, int r, int g, int b [, int a])
Установка стиля линии объекта swrshape. Передаваемые параметры:
口 width — толщина линии в пикселах;
г, д, ь — значения цвета в формате RGB. Значение каждого параметра лежит в диапазоне от 0 до 255;
口 а — прозрачность линии. Значения лежат в диапазоне от 0 до 255. При 255 — линия полностью не прозрачна. По умолчанию значение равно 255.
Для удаления стиля линии следует передать в качестве параметра значение 0: setLine(0)
Метод movePenTo
movePenTo(int х, int у)
Перемещает позицию рисования в точку с глобальными координатами х; у.
Метод movePen
movePen(int dx, int dy)
Перемешает позицию рисования на расстояние dx и dy относительно текущей позиции.
Метод drawLineTo
drawLineTo(int х, int у)
Рисует линию из текущей точки в точку с глобальными координатами х; у.
Метод drawLine
drawLine(int dx, int dy)
Рисует линию из текущей точки в точку со смешением dx; dy относительно текущей позиции.
Метод drawCurveTo
drawCurveTo(int controlx, int controly, int anchorx. int anchory)
Рисует кривую из текущей точки в конечную точку с координатами anchorx, anchory. Управляющая точка задается координатами controix, controly (рис, 23』).
.Управляющая точка
/ I\
/■
Текущая точкаКонечная точка
Рис. 23Л. Рисование кривой
Метод drawCurve
drawCurve(int controldx, int controldy, int anchotdx, int anchordy)
Аналогичен функции drawCurveTo(), но координаты конечной и управ- ляюшей точек задаются смещением от текущий точки.
Метод addFill
SWFFill addFill(int r, int g, int b [, int a]}
Добавление одноцветной заливки. Возвращает объект sWFEill. В качестве параметров передается цвет заливки (г, д, ь) и необязательный параметр — значение прозрачности а. Для окончательного добавления заливки следует указать направление заливки с помощью методов setLeftFillo и setRightFill (). Значения г, д, ь, а лежат в диапазоне от 0 до 255.
Методы setLeftFill и setRightFill
setLeftFill(SWFFill fill) setRightFill(SWFFill fill)
Указывают направление заливки. В качестве параметра передается объект SWFFill, для которого указывается направление заливки.
( ЗамеПние )
Кроме заливки однородным цветом, можно присваивать форме градиентную заливку или устанавливать фоновые изображения.
Заливка градиентом
SWFFill addFill(SWFGradient gradient. [, int flags])
В качестве параметров передается объект swFGradient, в соответствии со свойствами которого будет осуществляться заливка.
Параметр flags управляет типом градиента и может принимать значения:
О SWFFILL_LINEAR__GRADIENT ——ЛИНеЙНЫЙ ГрЭДИСНТ;
SW FFI LL_RADI AL_GRAD I ENT ——ралИЭЛЬНЫЙ ГраДИСНТ.
Заливка изображением
SWFFill addFill(SWFBitmap [,int flags])
В качестве параметра передается объект SWFBitmap, который определяет изображение. Параметр flags может иметь значения:
口 swffill_clipped__bitmap —— на форму вставляется только один экземпляр изображения. Изображение не тиражируется;
swffill_tiled_bitmap —— изображение тиражируется для полной заливки площади.
Объекты S WFDisplayltem
Объекты swFDispiayitem —— это указатели на объекты flash-фильма. Они добавляются с помощью метода add о объекта swFMovie. Этими объектами
могут быть формы (swFShape), текстовые объекты (swFText и swFTextrieid), movie-фильмы (SWFSprite) И КНОПКИ (SWFEutton).
Метод move То
moveTo(int х, int у)
Перемешает объект в точку с глобальными координатами х; у.
Метод move
move(int dx, int dy)
Перемещает объект на расстояние dx; dy относительно текущего положения.
Метод scaleTo
scaleTo(float х, float у)
Масштабирует исходный объект в масштабе х по оси X н у по оси 匕 Величина масштабирования отсчитывается от исходного размера объекта. То есть выполнение последовательно двух методов масштабирования с одним и тем же коэффициентом, равным 2, увеличит объект только в 2 раза, а не в 4.
Пример:
$i->scaleTo(2, 2);
$i->scaleTo(2, 2);
Метод scale
scale(float xf float y)
Масштабирует объект относительно его текущего размера. Выполнение последовательно двух методов масштабирования с одним и тем же коэффициентом, равным 2, увеличит объект в 4 раза.
$i->scale(2, 2);
$i->scale(2, 2);
Метод rotateTo
rotateTo(float degrees)
Осуществляет поворот объекта на degrees градусов относительно исходного положения. Как и в методе scaleToо, угол поворота при двух последовательно вызванных методах rotateTo не складывается — поворот осуществляется на больший из углов.
Метод rotate
rotate(float degrees)
Осуществляет поворот объекта на degrees градусов относительно текущего положения. При выполнении последовательно двух методов rotate объект будет повернут на сумму двух углов.
Метод skewXTo
skewXTo(float x)
Осуществляет наклон объекта по оси X на величину х. При значение $х = 1 наклон объекта равен 45 градусам. Величина наклона отсчитывается от исходного положения (рис. 23.2).
Метод skewX
skewX(float х)
Осуществляет наклон объекта по оси X на величину х. При значение $х = I наклон равен 45 градусам» Величина наклона отсчитывается от текущего положения. При выполнении последовательно двух наклонов угол наклона суммируется.
Методы skewYTo и skewY
Осуществляют наклон объекта по оси К
Метод setDepth
setDepth(float depth)
Устанавливает положение объектов по оси Z Если объекты перекрывают друг друга на плоскости XY (один объект частично или полностью закрывает
другой), то выше будет находиться тот объект (будет виден), у которого больше значение свойства depth (рис. 23.3).
Рис. 23.3. Положение объектов по оси N
Метод remove
remove {void}
Удаляет объект из flash-фильма.
Метод setName
setName(string name)
Устанавливает имя объекта, которое будет использоваться для обращения к нему с помощью внутренних команд action script. Используется только для movie-фильмов (swFSprite).
Метод setRatio
setRatio(float ratio)
Устанавливает коэффициент выполнения трансформации объектов для текущего кадра. Используется только для операций трансформации объектов SWFMorph.
Метод addColor
^pddColor(int г, int g, int b 3 int a])
r, g и ь — передаваемые параметры цветовой модели RGB. а — значение прозрачности. Метод осуществляет трансформацию цвета объекта. При-
бавляет цвет, переданный в параметрах функции к существующему цвету объекта.
Значения г, д, ь, а — лежат в диапазоне от 0 до 255.
Метод multColor
multColor(int г, int g, int b [, int a])
Трансформация цвета объекта. Изменяет баланс цветов. Значения г, д, ь, равные 1、оставляют цвета исходного объекта без изменения. Значения, равные 0, полностью убирают из цветовой гаммы объекта соответствующие цветовые компоненты.
2324. Объект SWFGradient
Объект SWFGradient используется для градиентной заливки объектов формы (sWFShape).
Конструктор SWFGradient
SWFGradient new SWFGradient(void)
Создает новый объект градиент (SWFGradient).
Метод addEntry
addEntry(float ratio, int r# int g, int b 匕 int a])
Установка цветов, между которыми будет подстроен градиент. Необходима установка как минимум двух цветов.
Параметр ratio представляет собой длину градиента, его значения лежат в диапазоне от 0 до 1. Этот параметр определяет точку, с которой начинается устанавливаемый цвет. После того как мы добавим градиент в форму, цвет, установленный со значением ratio = 0, будет расположен в левой крайней точке формы, а цвет со значением ratio = I ——в правой крайней точке. Если мы установим цвет со значением ratio = 0,5, то данный цвет будет находиться посередине формы.
Параметры г, g, ь ——компоненты цветовой модели RGB, Их значения, а также значение прозрачности а лежат в диапазон от 0 до 255.
Пример:
$g->addEntry(0.0, 255, 0, 0);
$g->addEntry(1•0, Oxff, 0к££, Oxff};
Объект SWFBitmap
Объекты SWFBitmap — это изображения, которые загружаются из внешних файлов форматов JPEG и DBL*
Конструктор SWFBitmap
SWFBitmap new SWFBitmap(fopen(nfilenamenr "r")
[f fopen (** alpha filename °, ',")]);
Создание изображений из файлов форматов JPEG или DBL Формат JPEG не должен быть записан с опциями Optimized или Progressive. Второй необязательный параметр представляет собой изображение, которое будет применяться к первому в качестве маски. Файлы маскм, имеющие расширение msk, могут быть сгенерированы с помощью утилиты gif2mask.
Метод getWidth
Integer getWidth(void)
Возвращает ширину изображения в пикселах.
Метод getHeight
Integer getHeight(void)
Возвращает высоту изображения в пикселах.
Объект SWFFUI
Объекты SWFF111 определяют заливку. Они позволяют управлять заливкой форм (swFShape) с помощью изображений (SWFBitmap) и градиентов (swFGradient). Для добавления заливки к форме у нее следует вызвать метод addFilK).
Метод токе То
moveTo(int х, int у)
Перемещает объект swFFill в точку с глобальными координатами $х и $у. Перемещение происходит относительно точки середины объекта swFShape, которому назначена заливка.
Метод scaleTo
scaleTo(float х, float у)
Масштабирует заливку на значения $х и $у по соответствую щи м осям.
Метод rotateTo
rotateTo(float degrees)
Поворот объекта заливки на угол, равный degrees градусов.
Метод skewXTo
skewXTo{float х)
Наклон заливки вдоль оси X, Значение х, равное I, соответствует наклону 45 градусов. Увеличение этого значения увеличивает угол наклона. Уменьшение —соответственно уменьшает.
Метод skewYTo
skewYTo(float у)
Наклон заливки по оси К
Объект SWFMorph
Объект swFMorph предназначен для операций трансформации одной формы (SWFShape) В другую.
Конструктор SWFMorph
SWFMorph = new SWFMorph(void)
Создает новый объект swFMorph.
Метод getShapel
SWFShape getShapel(void)
Возвращает указатель на начальную форму трансформации.
Метод getShape2
SWFShape getShape2(void)
Возвращает указатель на конечную форму трансформации.
Объект SWFText
Этот объект представляет собой текст.
Конструктор SWFText
SWFText new SWFText(void)
Создает новый объект — текст.
Метод setFont
setFont(SWFFont f)
Устанавливает шрифт для объекта SWFText. В параметрах метода передается указатель на объект шрифта.
Метод setHeight
setHeight(int height)
Установка высоты шрифта. По умолчанию значение высоты равно 240.
Метод setSpacing
setSpacing(int spacing)
Установка межбуквенного интервала. По умолчанию интервал равен 】.
Метод setColor
setColor(int г, int gf int b (, int a])
Установка цвета шрифта.
Метод moveTo
moveTo($x, $y)
Перемещает курсор в координаты $х, $у объекта swrrext.
Метод addString
addstring(string str)
Добавляет строку $str в точку положения курсора.
Метод getWidth
integer getWidth(string str)
Возвращает длину, которую займет строка $str при текущих значениях шрифта, масштаба и межбуквенного интервала. Длина возвращается в пикселах.
2329. Объект SWFFont
Объект SWFFont представляет собой шрифт.
Конструктор SWFFont
SWFFont new SWFFont(string name)
Создание нового объекта шрифта. Если параметр name соответствует имени FDB-файла, то загружается шрифт из этого файла. В противном случае загружается шрифт, определяемый браузером. Всегда доступны шрифты с именами _s&rif, _sans и _typewriter. Например, $f = newSWFFont (H__sans11);
Созданный объект шрифта используется при установке шрифтов объектов SWFText И SWFTextField методом setFont.
FDB-файлы (font definition block)——это оболочка для блока SWF DefineFont2, который содержит полное описание шрифта.
Объект SWFTextField
Текстовое поле, предназначенное для ввода текста (если это не запрешено свойствами создаваемого объекта).
Конструктор SWFTextField
SWFTextField new SWFTextField([int flags J)
Создает новый объект SWFTextField. Параметр flags определяет свойства текстового поля и может принимать следующие значения: 口 swftextfield_noedit — нередактируемое поле;
口 swftextf工ELD_PASSWORD — при вводе текста символы заменяются звездочкой;
口 SWFTEXTFIELD_DRAWBOX — ТеКСТОВОС ПОЛС С борДЮрОМ;
口 swftextfield_multiline ——текстовое поле для ввода нескольких строк текста;
口 swftextfield_wordwrap —— позволяет делать перенос текста в поле;
swetextf工ELD NCSELECT — невыделяемое текстовое поле.
Параметр flags может быть комбинацией перечисленных значений. Для комбинации используется оператор or, например:
$t = newSWFTextField(SWFTEXTFIELD_PASSWORD I SWFTEXTFIELD_NOEDIT};
Здесь создается нередактируемое текстовое поле, символы в котором отображаются в виде звездочек.
Метод setFont
setFont(SWFFont font)
Установка шрифта font.
Метод align
align(int alignment)
Управляет выравниванием текста. Параметр alignment может принимать значения констант:
口 swftextfield_align_left — выравнивание по левому краю;
swftextfield_align_right — выравнивание по правому краю;
口 swftextfield_align__center — выравнивание по центру;
口 SWFTEXTFIELD_ALIGN_JUSTIFY — выравнивание ПО ширине.
Метод setHeight
setHeight(int height)
Установка высоты текстового поля. По умолчанию равна 240.
Метод setLeftMargin
setLeftMargin(int width)
Установка отступа слева для текста в поле. По умолчанию равен 0.
Метод setRightMargin
setRightMargin(int width)
Установка отступа справа для текста в поле. По умолчанию равен 0.
Метод setMargins
setMargins(int left, int right)
Установка отступов слева и справа от текста в поле. По умолчанию равны 0.
Метод setindentation
setindentation(int width)
Установка отступа первой строки.
Метод setLineSpacing
setLineSpacing(int height)
Установка межстрочного интервала. По умолчанию межстрочный интервал равен 40.
Метод setColor
setColor(int г, int g, int b [, int a])
Установка цвета текста в поле. Цвет по умолчанию —— черный.
Метод setName
setName(string name)
Устанавливает связь между текстовым полем и переменной с именем name.
Метод addString
addString(string str)
Добавляет строку str к тексту в текстовом поле.
Объект SWFSprite
Объекты swFSprite представляют собой так называемые объекты niovie- фильмов (movie clip). Они имеют собственную анимацию и временную шкалу, независимую от временной шкалы главного flash-фильма. Поэтому методы объекта swFSprite похожи на методы объектов SWFMovie.
Конструктор SWFSprite
SWFSprite new SWFSprite(void)
Создает новый movie-фильм.
Метод add
SWFDisplayltem add(resource object)
Добавляет объект любого типа в movie-фильм. Возвращает указатель на добавленный объект В виде объекта SWFDisplayltem.
Метод remove
remove(SWFDisplayltem i)
Удаляет объект, указатель которого передан методу (i) из flash-фильма.
Метод nextFrame
nextFrame(void)
Переход к следующему кадру movie-фильма.
Метод setFrames
setFrames( integer numberOfFrames)
Установка обшего количества кадров movie-фильма.
Объект SWFButton
Объект swFButton представляет собой кнопку, события которой можно обрабатывать с помощью скриптового языка action script.
Констуктор SWFButton
SWFButton new SWFButton()
Создание нового объекта swFButton.
Метод addShape
addShape(SWFShape shape, int flags)
Устанавливает формы shape, реагирующие на события кнопки.
События определяются параметром flags, который может принимать значения одной из следующих констант:
swfbuttonjjp — кнопка отпущена;
О swfbutton_over — указатель мы нт и находится над кнопкой swFButton
口 swf8UTTQN_dcwn — кнопка нажата;
swfbuttoN—H工т — не имеет отображения. Определяет пересечение кнопки swFButton с областью.
Метод setup
setup(SWFShape shape)
Псевдоним ДЛЯ метода addshape (shape, SWFBUTTON—UP).
Метод setOver
setOver(SWFShape shape)
Псевдоним ДЛЯ метода addShape (shape, SWFBUTTON_OVER).
Метод setDown
setDown(SWFShape shape)
Псевдоним ДЛЯ метода addShape (shape, SWFBUTTON_DOWN).
Метод setHit
setHit(SWFShape shape)
Псевдоним для метода addshape (shape, SWFBUTTON_H工T).
Метод add Act ion
addAction(SWFAction action, int flags)
Добавляет на кнопку команды action, которые обрабатывают события, определяемые параметром flags. Параметр flags может быть одной из следующих констант:
口 swfbutton_mouseover — указатель мыши над кнопкой;
口 swfbuttoN—MOUSEout ——указатель мыши вышел за пределы кнопки;
口 swfbuttoN—MOUSEUp — кнопка мыши нажата над областью кнопки SWFButton;
swfbutton_mouseupouts I de — кнопка мыши нажата вне области кнопки SWFButton;
swfbuttoN—MOUSEDOWn — кнопка мыши нажата над областью кнопки SWFButton;
口 swfbutton_dragout — кнопка мыши нажата и покидает область кнопки SWFButton;
口 swfbuttqN—DRAGOVER — кнопка мыши нажата н входит в область кнопки SWFButton.
Метод set Action
setAction(SWFAction action)
Устанавливает команды, обрабатывающие нажатие кнопки SWFButton. Является псевдонимом ДЛЯ метода addActlon(shape, SWFBUTTON MOUSEUP).
Объект SWF Action
Объекты для внутреннего скриптового языка flash — action script.
Конструктор SWFAction
SWFAction new SWFAction(string script)
Создает объекты SWFAction. Код action script компилируется из команд, переданных в параметре script.
23.3. Примеры кода
Градиентная заливка
Рассмотрим пример создания градиентной заливки. В этом примере будут использоваться объекты SWFMovie, SWFShape И SWFGradient (листинг 23.1).
'"^Ё^Ж1y 弋立纬扌号^^^j^:黄 ^^8^'*1"管""^^1喚射'5啥卑',"第".'"峯""11^>11;*'学傑1;^^1**‘¥:冬:"岂萌学'4^^^1".通,,圍^^^'城'窮
;Листинг 23.1. Создание градиентной заливкиi
<?php
// Создание объекта flash-фильма
$m = new SWFMovie {);
$m->setDimension(400, 400);
// Создание объекта формы
$s = new SWFShape();
// Создание объекта градиент
$g = new SWFGradient();
$g->addEntry(0.0, 255, 0, 0);
$g->add£ntry(0.5, 0, 255, 0);
$g->addEntry{1.0, 0, 255, 255);
30 Зак. 1181
// Присоединение градиента к форме и создание объекта линейной заливки
$f = $s->addFill($g, SWFF1LL__LINEAR__GRADIENT);
// Трансформации объекта заливки SWFFill
$f->scaleTo(0.05, 0.05);
$f->moveTo(70, 50);
$f->rotateTo(25);
$s->setRightFill ($f);
// Рисование формы
// Установка стиля линий
$s->setLine(1, 0, 0, 0);
// Перемещение позиции рисования
$s->movePenTo(20, 10);
// Рисование линий
$s->drawLine(0, 100);
$s*->drawLine (100г 0);
$s->drawLine(0, -100);
$s->drawLine(-100, 0);
// Добавление формы к объекту SWFMovie
Sm->add ($s);
// Вывод созданного flash-фильма в браузер
header(* Content-type: application/x-shockwave^flash1);
$m-> output《);
?>
В листинге 23.1 первоначально создается объект flash-фильма swFMovie и устанавливаются его размеры. Затем создаются объекты формы SWFShape и градиента swrGradient. С помощью методов addEntry добавляются 3 цвета градиента. Методом addFiii ()градиент добавляется к форме, и тем самнм создается объект заливки — swffiii, над которым затем производятся операции трансформации. Окончательная установка заливки осуществляется методом setRightrill(). После всех необходимых действий по настройке объектов градиента и заливки рисуются линии формы методами drawLine ()- Последним этапом объект формы добавляется к объекту flash-фильма (add($s)) и выводится в браузер методом output ().
Работа с изображениями.
Объект SWFBitmap
В этом примере будет создан объект формы, и ему в качестве фона будет установлено изображение из файла img.jpg (листинг 23.2).
峭寧囲戳堆驍科■"蹩符皆产甥■望瞿第…[岑军^翼岡暗亀了 m:謡芯^^牙”七"”:声=臂:*睪:*"二*:・汐:彳:3皆曜1笹?三盜飞;;:
i Листйнг 23.2. Работа с изображениями
<?php
// Создание объекта SWFBitmap из изображения img.jpg
$b = new SWFBitmap (fopen (1'img. jpg,f, "rb"));
// Создание объекта flash-фильма
$m ^ new SWFMovie ();
$width =300;
$height = 300;
// Создание объекта формы
$s = new SWFShape();
// Добавление в форму заливки методом addFill ()
// В качестве параметров заливки передается объект изображения $f ='$s->addF土工l($b, SWFFILL_TILED_BITMAP); // Операции трансформации над объектом заливки $f->scaleTo<l, 1);
// Окончательная установка заливки в форму $s->setLeftFill($f);
// Рисование линий в форме $s->drawLine($width, 0); $s->drawLine(О, $height/2); §s->drawLine(-$width, 0); $s->drawLine(0f -$height/2);
// Добавление объекта формы в flash-фильм $i = $m->add($3);
header(1Content-type: application/x-shockwave-flash1);
// Швод flash-фильма в браузер $m->output О ;
Первоначально создается объект swFBitmap, в который загружается изображение из файла img.jpg. Файл img.jpg должен лежать в той же директории, что и выполняемый скрипт php.
( Замечание )
Будьте внимательны, файл JPEG должен быть создан без использования опций optimized или progressive.
Затем создается форма, и к ней методом addFill о добавляется заливка. В качестве параметров метода в него передается объект swFBitmap и кон-
стаита swffill_tiled_bitmap, которая определяет, как именно будет создано фоновое изображение. В данном случае изображение будет размножено ло всей площади формы. Методом scaleTo () выполняется масштабирование объекта заливки. Метод setLeftriil О окончательно добавляет заливку в форму* Далее методами drawLine() на объекте форма рисуется квадрат. После чего происходит добавление объекта формы во flash-фильм н вывод flash-фильма в браузер.
Работа с объектом SWFMorph
В данном примере будет рассмотрена работа с объектом SWFMorph, с помощью которого одна форма будет трансформирована в другую. Процесс трансформации будет анимирован в объекте swFMovie (листинг 23.3).
М.……”「?锌若空吃¥М咗事…..:,塞聲 ■資■电;;俨"塗当匕U 学勺4^^^豈:嘲詁它限吋+塁扌近*二……[… 声,.,訓口.《VM ::識:f:.:号.;" +宀二;
;Листинг 233 Трансформация одной формы в другую
<?php
// Создание flash-фильма
$ш = new SWFMovie ();
// Установка размеров
$m-*>set Dimens ion (320, 240);
// Создание объекта трансформации
$р = new SWFMorph();
// Получение указателя в первую форму трансформации
$s = $p^>getShapel();
// Рисование формы
$s->setLine(0,0,0,0);
$f = $s->addFill(255, 0, 0);
$s->setLeftFill($f);
$s->movePenTo(0,30);
$s->drawLine{200.0);
$s->drawLine(0, 30);
$s->drawLine{-200,0);
$s->drawLine(0,-30);
// Получение указателя во вторую форму трансформации
$s = $p->getShape2(j;
// Рисование формы
$s->setLine(1,0,0,0);
$£ = $s->addFill(0, 0, 255);
$s->setLeftFill($f);
$s->TnovePenTo (100, 50);
$s->drawLine (50,-50);
$s->drawLine (50, 50);
$s->drawLine (*-50, 50);
$s->drawLine(-50,-50);
// Добавление объекта трансформации во flash-фильм
$i = $m->add($р);
// Установка коэффициента выполнения трансформации
// одной формы в другую для последовательности кадров.
for($г=0.0; $г<=1* 0; $r+=0.1)
{
// Установка коэффициента выполнения трансформации
// для текущего кадра
$i->setRatio($r);
/ / Переход к следующему кадру
$m->nextFrame ();
// Вывод flash-фильма в браузер
header(1Content-type: application/x-shockwave-flash*);
$m->output();
?>
В листинге 23.3 первоначально создаются объекты flash-фильма swFMovie и трансформации swraorph. Затем с помощью метода getshapei о возвращается указатель на объект первой формы, над которой будет произведена операция трансформации. На объект формы добавляются заливка и линии методами drawLine о. Аналогичные действия произведены со второй формой, которая возвращается методом getshape2(). Затем методом add() объект трансформации swFMorph добавляется во flash-фильм.
Для создания анимированной трансформации необходимо не только создать объект swFMorph, но и установить коэффициент выполнения трансформации для каждого кадра анимации. Это достигается вызовом метода setRatio()» который выполняется в цикле for. Переход к следующему кадру осуществляется методом nextFrame(). По окончании настройки анимации объект swFMovie выводится в браузер.
Работа с командами action script SWFAction и объектом SWFButton
В данном примере создается flash-фильм с объектом, который можно перемещать по экрану с помощью указателя мыши. Перемещение реализуется с
помощью команд языка action script. События, генерируемые мышью, перехватываются с помощью объекта swFButtcn*
-"4 ^--.,:;•…,■:-蟹,••…::
истинг 234 Работа с командами action script
<?
Ming_u s e SWEVe rs i on(4);
// Создание объекта форма
$s = new SWFShape();
$f = $s->addFill(O, 155, 0); $s->setRightFill($f);
// Рисование квадрата $s->drawLine(50, 0); $s->drawLine(0, 50); $s->drawLine(-50, 0); $s->drawLine{0, -50);
// Создание объекта кнопка
$b = new SWFButton();
// Добавление формы к кнопке и
// установка для этой формы реакций на события $b->addShape($s, SWFBUTTON_HIT |
SWFBUTTON_UP I SWFBUTTONJX)WN I SWFBUTTON_ OVER);
// Создание объекта команд скриптового языка $action == new SWFAction(HstartDrag(1/box', 0);"); // Добавление на кнопку команды объекта SWFAction, // которая будет обрабатывать событие
// нажатия кнопки мыши (SWFBUTTON_MOUSEDOWN).
$b->addAction faction, SWFBUTTON_MOUSEDOWN);
// Создание и добавление команды stopDrag(),
// которая будет выполняться при наступлении событий:
// "кнопка мыши нажата1' над кнопкой SWFButton и
// "указатель мыши вышел за пределы кнопки SWFButton" $action = new SWFAction ("stopDrag ();**);
$b->addAction faction, SWFBUTTON_MOUSEUP I SWFBUTTON_MOUSEUPOUTSIDE); // Создание movie-фильма
$p = new SWFSpriteQ ;
// Добавление на него кнопки $p->add($b);
// Переход к следующему кадру
$p->next Frame ();
// Создание flash-фильма
$m = new SWFMovie();
$m->setDimension(320r 240);
/ / Добавление movie-фильма во flash-^фильм
$i = $m->add($p);
// Установка названия movie-фильма во flash-фильме.
// По этому имени к нему будут обращаться команды action script $i->setName('box');
〃 Вывод flash-фильма в браузера
header(1Content-type: application/x-shockwave-flash *); $m->output ();
?>
В листинге 23.4 первоначально рисуется форма swrshape, которая будет назначена кнопке swFButton. Затем создается объект кнопки swFButton, на которую методом addshape()добавляется созданная форма и определяются события кнопки.
После этого создается объект скриптового языка swFAction. В качестве параметра конструктора передаются команды action script. Созданный объект swFAction добавляется на кнопку swFButton методом addActionO, и определяется событие (swfbuttoN—MOUsedown), которое будет обрабатываться командами action script — nstartDrag( Vbox', 0);”・ Здесь box — это имя flash-объекта, оно будет присвоено movie-фильму после того, как он будет добавлен во flash-фильм. Аналогичным образом добавляется команда action script — "stopDragQ ;"・ Созданная и настроенная кнопка добавляется к объекту swFSprite, который в свою очередь добавляется к объекту SWFMovie. Для того чтобы команды action script могли обращаться к объекту swFSprite, ему с помощью метода setName о присваивается имя box.
Глава 24
Работа с PDF-документами. Библиотека ClibPDF
В этой главе рассмотрены функции PHP 5, позволяющие работать с PDF- документами. Этот формат был создан фирмой Adobe и предназначен для создания документов с не зависящим от операционной системы представлением. То есть PDF-документ, созданный в одной среде, будет выглядеть точно так же в другой.
( Замечание )
PDF-формат (Portable Document Format — портативный документный формат) стал стандартом де-факто в Интернете для храненма, обмена и распространения документов, которые должны выглядеть одинаково на всех платформах. Такой формат получил широкое распространение, так как программа просмотра PDF-документов — Acrobat Reader, распространяется свободно и ее можно загрузить с сайта Adobe. Следует отметить, что практически вся научная и техническая информация в мире распространяется в данном формате. В настоящий момент PDF серьезно рассматривается в качестве замены HTML.
{ Замечание )
Для успешной работы с PDF-форматом на клиентской машине должен быть установлен Acrobat Reader, последнюю версию которого можно найти по адресу http://www.adobe.com/products/acrobat/readstep2.html.
Для работы с PDF-документами PHP 5 предоставляет две библиотеки: ClibPDF и PDFLib. В принципе обе библиотеки очень похожи друг на друга, как с точки зрения функциональности» так и с точки зрения работы с ними средствами PHP. В этой главе мы рассмотрим библиотеку ClibPDF, которая входит в стандартную поставку дистрибутива PHP 5.
КПримечание ;
Пожалуй, одним из немногих отличий библиотеки ClibPDF от PDFLib является то, что первая не позволяет работать одновременно с несколькими PDF-документами.
Обе библиотеки способны создавать PDF-документ в памяти компьютера без использования временных файлов.
Установка
Библиотека ClibPDF является кроссплатформенной, поэтому в данной главе будет рассмотрена ее установка для систем UNIX и Windows.
Установка под Windows
Для установки библиотеки под Windows необходимо загрузить библиотеку ClibPDF, скомпилированную под Win32. После того как архив загружен, библиотеку (php_cpdf.dll) следует распаковать из архива в директорию, где находятся расширения PHP, Например, если корневой путь к PHP совпадает с C:\php\, то расширения должны находиться в директории C:\php\ext\.
; Замечанье )
Если установка PHP 5 производится из полного дистрибутива, распространяе- мо「о как ZIP-архив (zip), а не из усеченной версии, распространяемой как инсталлятор (ехе), в директории ext\ динамическая библиотека php_cpdf.dll уже присутствует.
Далее необходимо подключить библиотеку. Для этого следует перейти в каталог Windows и отредактировать файл php.ini. Для работы с библиотекой в нем следует рас ко м м е нти ровать строку: extension= php_cpdf.dll
После чего необходимо проверить, настроены ли в файле php.ini пути к библиотекам расширения. Значение директивы extension^dir должно указывать на ту директорию, где лежат библиотеки расширений. Для вышеприведенного примера, когда библиотеки находятся в папке C:\php\ext\, в файле php.ini директива extension_dir должна принимать значение: extension_dir =**с: /php5/ext°
Для вступления изменений в силу следует перегрузить сервер Apache. При успешной установке библиотеки в таблице, генерируемой функцией phpinfo(J, должен появиться раздел cpdf.
Установка под UNIX-системы
Для установки библиотеки ClibPDF в UNIX-подобной операционной системе необходимо перекомпилировать PHP с ключом —with-cpdfiib, после ключа через знак равенства (=) можно указать путь, по которому будет помещена библиотека.
Создание простого PDF-документа
В этом разделе будут рассмотрены основные функции библиотеки ClibPDF, необходимые для создания простого PDF-документа м вывода в него текста. После чего будет рассмотрено несколько примеров создания PDF-документов.
Функция cpdf^open
int cpdf_open (int congression (, string filename])
Функция cpdf_open()открывает новый PDF-документ. Первый параметр compression определяет сжатие документа. Значение параметра, равное О, говорит о том, что документ открывается в обычном (не сжатом) виде* Необязательный параметр filename определяет имя файла, в который записывается документ. При отсутствии этого параметра документ создается в памяти и в последующем может быть либо сохранен в файл с помощью функции cpdf_3&ve_to_file(), либо передан в стандартный вывод функцией cpdf_оutput_buffег(),
Функция возвращает дескриптор PDF-документа, который используется в качестве первого параметра во всех остальных функциях библиотеки.
Функция cpdf_save_fо_file
void cpdf__save_to_file (int cpdf, string filename)
Функция cpdf_save_to_file () сохраняет созданный в памяти PDF- документ cpdf в файл с именем filename.
工 Пр-мечанме、
Все функции этой библиотеки, за исключением cpdf_open (), принимают в качестве первого параметра дескриптор открытого PDF-документа cpdf, возвращаемый функцией cpdf_open {), поэтому в дальнейшем при описании остальных функций на этом параметре мы останавливаться не будем.
Если при создании PDF-документа функции cpdf_open()был передан вто* рой необязательный параметр filename, то в использовании функции cpdf__save_to_fiie () уже не возникает необходимости.
Функция cpdf_page_init
bool cpdf_page_init (int cpdf, int page_number, int orientation, float height, float width [, float unit))
Функция cpdf_page_init () создает новую страницу с высотой height, шириной width, номером page__number и ориентацией orientation. Параметр orientation может принимать значения 0 (для вертикального расположения страницы) и 1 (для горизонтального расположения страницы). В том случае, когда необходим вывод текста, значение этого параметра устанавливается равным 0.
:Замечание )
Точка отсчета координат в PDF-документе начинается не с левого верхнего угла, как в большинстве библиотек, а с левого нижнего. Изменить начало координат можно при помощи функции cpdf__translate ()» описываемой ниже*
Необязательный параметр unit устанавливает значение единицы измерения для системы координат> Это значение должно являться числом postscript- точек на единицу расстояния (в данном случае —— на дюйм). По умолчанию это значение равно 72, что означает, что в данном случае в одном дюйме содержатся 72 точки.
Функция cpdf_add_outline
int cpdf_add_outline (int cpdf, int lastoutline, int sublevel, int open, int pagenr, string text)
Функция cpdf_add_outiine () добавляет закладку (bookmark) с текстом text, указывающую на текущую страницу.
Пример использования функции приведен в листинге 24.1.
:Листинг 24.1. Использование функции cpdf_add_outline;
<?php
$cpdf 二 cpdf^open(0);
cpdf_page_init($cpdf, lr 0, 595, 842};
cpdf_adci_outline ($cpdf, 0, 0, 0, 1, "Page Iй);
/*
{блок вывода нужного текста или рисунка}
{блок сохранения документа и его закрытия}
*/
?>
Закладка позволяет осуществлять быстрый переход к указываемому ей месту в PDF-документе и служит для создания оглавления документа.
Функция cpdf_begin_text
bool cpdf_begin_text {int cpdf)
Функция cpdf_begin_text()начинает текстовый раздел. Единственным параметром функции является дескриптор cpdf открытого PDF-документа.
Эта функция является парной — текстовый раздел обязательно должен явно завершаться вызовом функции cpdf_end_text ().
Функция cpdf_end_text
bool cpdf_end_text (int cpdf)
Функция cpdf_end_text() завершает текстовый раздел, начатый функцией cpdf_begin_text().
Между этими двумя разделами обычно располагаются функция вывода текста, функция установки шрифтов выводимого текста и функция выбора способа воспроизведения выводимого текста. Пример показан в листинге 24.2.
曙宏期欣1,飞侬^强將吋雪斯+怫д4喏驴*.w%*:•薄X-綻止,,對載j萤经陋齡繼典整缈附彳線""св畤^】!渺叫1堂*■::饪气产 ктн’с 炉附■券W'*"**w"r
:Листинг 24.2. Использование функции cpdf jand^text;
<?php
cpdf_begin_text($pdf); // начало текстового раздела
cpdf_set_f ont ($ pdf, 16, "Helvetica", "WinAnsiEncoding11);
cpdf_set_text_rendering ($cpdf, 1);
cpdf_text ($pdf, 100, 100, "Выводимый текст");
cpdf_end__text($pdf); // конец текстового раздела ?>
Функция cpdf_set_font
bool cpdf_set_font (int cpdf, string font_naine, float font_size, string encoding)
Функция cpdf_set_font о устанавливает вид font_nanie и размер font_size шрифта, которым выводится текст.
工 Примечание〉
В текущей версии библиотеки поддерживаются только стандартные PostScript- шрифты.
Параметр encoding определяет кодировку н может принимать следующие значения: MacRomanEncoding, MacExpertEncoding, WinAnsiEncoding И NULL.
Значение параметра, равное null, означает встроенную кодировку шрифта.
Функция cpdf_text
bool cpdf_text (int cpdf# string text, float x, float у [, int
mode L float orientation [, int alignmode]]]J
Функция cpdf_text о предназначена для вывода текста, передаваемого в параметре text в точке документа с координатами х и у.
Необязательный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию исполдуются единицы измерения, установленные для страницы. В ином случае координаты измеряются в PostScript-пунктах без учета текущих единиц измерения. Необязательные параметры orientation и alignmode определяют поворот текста в градусах и способ выравнивания соответственно.
Функция cpdf_set_texf_rendering
bool cpdf_set_text_rendering (int cpdf, int rendermode)
Функция cpdf_set_text_rendering () определяет способ вое пром зведен ия текста. Возможными значениями параметра rendermode являются следующие:
0 ——заполненный текст;
口 1 — перечеркнутый текст;
2 ——заполненный и перечеркнутый текст;
3 — невидимый текст;
4 ——заполненный текст, добавленный в отсеченную область;
О 5 — перечеркнутый текст, добавленный в отсеченную область;
6 ——заполненный и перечеркнутый текст, добавленный в отсеченную область;
口 7 ——текст, добавленный в отсеченную область.
К Примечание 、
Отсеченной областью (clipping part) называется область, ограничивающая скрытый графический объект, воспроизводимый только при печати.
Функция cpdf^moveto
bool cpdf_moveto (int cpdf, float x, float у [# int mode])
Функция cpdf_moveto() устанавливает текущую точку с координатами х; у. Необязательный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию используются единицы измерения, установленные для страницы. В ином случае координаты измеряются в PostScript-пунктах без учета текущих единиц измерения.
Функция cpdfjineto
bool cpdf_lineto (int cpdf, float x, float у [, int mode])
Функция cpdf__lineto () рисует линию от текущей точки до точки с координатами х и у. Необязательный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию используются единицы измерения, установленные для страницы. В ином случае координаты измеряются в PostScript-пунктах без учета текущих единиц измерения.
Функция cpdf_stroke
bool cpdf_stroke (int cpdf)
Функция cpdf_stroke о рисует линию вдоль текущего пути.
Функция cpdj finalize
bool cpdf_ finalize (int cpdf)
Функция cpdf_finalize() завершает текущий PDF-документ cpdf.
Функция cpdf_output_buffer
cpdf—output_buffer (int cpdf)
Функция cpdf__output_buffer о выводит текущий PDF-документ cpdf в браузер.
Функция cpdf_close
cpdf_close(int cpdf)
Функция cpdf_close()закрывает текущий PDF-документ cpdf.
Практический пример
В данном разделе будут разработаны два простых приложения. Первое выводит в PDF-документ текст "Hello, World! " (листинг 24.3).
i Листинг 24.3. Вывод текста в PDF-документ
<?php
// открываем новый PDF-документ
$cpdf = cpdf_open(0);
// создаем новую страницу
cpdf_page__init ($cpdf, 1, 0, 595, 842, 1.0);
// добавляем закладку
cpdf_add_outline($cpdff 0, 0, 0, 1, "Page 1”); /* начало блока вывода текста */
cpdf_begin_text($cpdf);
// устанавливаем тип, размер шрифта и кодировку текста cpdf_set_font($cpdf, 11 Times-Roman", 30, "WinAnsiEncoding"}; // устанавливаем способ воспроизведения текста cpdf_set_text_rendering($cpdf, 1);
// выводам текст
cpdf_text ($cpdf r nHello world!11, 50, 500);
cpdf_end_text($cpdf);
/* окончание блока вывода текста */
// рисуем линию вдоль текущего пути cpdf__stroke ($cpdf);
// завершаем PDF*-документ cpdf_finalize($cpdf);
// отсыпаем браузеру соответствующий заголовок
Header ("Content-type: application/pdf11);
// выводим документ в браузер
cpdf__output Jouf fer ($cpdf);
// закрываем PDF-документ cpdf_close ($cpdf);
?>
Результат выполнения этого скрипта показан на рис. 24.1.
Файл Правка бид Избранное Сервис Спрэвкз
Q назад ▼ 0'* 国 ® ; / Поиск >избраннсх Адрес; |й http://1ocalhost/test2,php
■鋼 Save а Сору = Print ® Email 輪 Search | © J® S
冊!x—™
Hdlo worid?
Рис. 24.1. Вывод текста в PDF-документ
Все функции, которые были необходимы для вывода текста с помощью скрипта, показанного в листинге 24.1, были рассмотрели ранее. После завершения PDF-документа с помощью функции cpdf^finaiize(), но перед выводом его в браузер функцией cpdf_output_buf fег(), браузеру был послан заголовок
Header ("Content-type: application/pdfн)
Это необходимо для того, чтобы браузер мог определить тип пересылаемого ему документа как PDF. Подобный заголовок необходимо посылать браузеру в любом скрипте, где происходит работа с PDF-документом, который потом необходимо вывести в браузер, иначе документ будет рассматриваться браузером как текстовый и в окно будет выведено его двоичное представление*
Второе приложение создает PDF-документ и рисует в нем прямоугольник с заданными параметрами (листинг 24.4)*
Ш^^др^^
<?php
/ / открываем PDF-документ
$cpdf = cpdf_open(0);
// Создаем новую страницу cpdf_page_init($cpdf, 1, 0, 595, 842, 1.0); 〃 устанавливаем RGB-значения цвета cpdf_setrgbcolor($cpdf, 1, 0, 0);
// рисуем прямоугольник cpdf_rect($cpdfг 50, 400, 180, 100);
// закрашиваем внутреннюю часть прямоугольника цветом заполнения cpdf_fill($cpdf);
/ / завершаем страницу cpdffinalize($cpdf);
// отправляем заголовок браузеру
Header ("Content-type: app 1 i cat i on /pdf11);
// выводим содержимое документа в браузер cpdf_output_buffer ($cpdf);
/ / закрываем документ cpdf_close($cpdf);
?>
Результат выполнения скрипта из листинга 24.4 показан на рис. 24.2.
Файл Правка Вид Избранное Сервис Справка
© Назад ▼ J ▼凶 ② ;】『Поиск ; V Избранное 或 Медиа §
Адрес: |й http://tocalhosUtest9.php
Save a Copy .、Print 0 Email 也 SearchSave a Copy .、Print 0 Email 也 SearchI 4^) IT- Select Text H l<I 4^) IT- Select Text H l<
Save a Copy .、Print 0 Email 也 Search
Save a Copy .、Print 0 Email 也 Search
I 4^) IT- Select Text H l<
I 4^) IT- Select Text H l<
Рис. 24.2. Красный прямоугольник в PDF-документе
Дополнительные функции библиотеки ClibPDF
Функция cpdf_add_annotation
bool cpdf_add__annotation (int cpdf, float llx, float lly, float
urx, float ury, string title, string content L int mode])
Функция cpdf_add_annotation()добавляет аннотацию с названием title и содержанием content с координатами (Их, Ну) нижнего левого угла и с координатами (urx, игу) правого верхнего угла.
Необязательный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию используются единицы измерения, установленные для страницы. В ином случае координаты измеряются в PostScript-пунктах без учета текущих единиц измерения.
Функция cpdf_arc
bool cpdf_arc (int cpdf, float x, float y, float radius, float start, float end int mode])
Функция cpdf_arc() рисует дугу с центром в точке с координатами х; у и радиусом radius, начиная с угла start и заканчивая углом end*
Необязатеяьный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию используются единицы измерения, установленные для страницы. В ином случае координаты измеряются в PostScript-пунктах без учета текущих единиц измерения.
Функция cpdf_circle
bool cpdf_circle (int cpdff float x, float y, float radius, [, int mode])
Функция cpdf_arc () рисует дугу с центром в точке с координатами х; у и радиусом radius.
Необязательный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию используются единицы измерения» установленные для страницы. В ином случае координаты измеряются в PostScript*пунктах без учета текущих единиц измерения.
Функция cpdf_clip
bool cpdf_clip (int cpdf)
Функция cpdf_ciip о усекает всю прорисовку до текущего пути.
Функция cpdf closepath
bool cpdfclosepath (int cpdf)
Функция cpdf_ciosepatho закрывает текущий путь.
Функция cpdf_closepath_fill_stroke
bool cpdf_closepath_fi1l_stroke (int cpdf)
Функция cpdf_closepath fill_stroke () закрывает, заполняет текущий путь текущим цветом заполнения н прорисовывает его.
Функция cpdf_closepath_stroke
bool cpdf__closepath_stroke (int cpdf)
Функция cpdf^fill_stroke() закрывает путь и прорисовывает линию вокруг пути.
工 Примечание ,
Аналогом этой функции является комбинация из функций cpdf_closepath () и cpdf stroke《).
Функция cpdf_continue_text
bool cpdf_continue_text (int cpdf, string text)
Функция cpdf_continue_text() выводит строку text на следующей строчке.
Функция cpdf_curveto
bool cpdf_curveto (int cpdf, float xl, float yl, float x2, float y2, float k3, float y3 [, int mode])
Функция cpd£_curveto() прорисовывает кривую Безье от текущей точки до точки с координатами (хз; уз) с использованием точек с координатами (xi; yi) и (х2; у2)в качестве контрольных точек.
Необязательный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию используются единицы измерения, установленные для страницы. В ином случае координаты измеряются в PostScript-пунктах без учета текущих единиц измерения.
Функция cpdfJiU
bool cpdf_fill {int cpdf)
Функция cpdf_f ill() заполняет внутреннюю часть текущего пути те куши м цветом заполнения и прорисовывает его контур.
Функция cpdf_finalize_page
bool cpdf_finalize__page (int cpdf, int page_number)
Функция cpdf_f ina 1 ize_page () закрывает страницу С номером page nwnber.
工 Примечание :
Эту функцию имеет смысл применять только с целью уменьшения расхода па- мктм」поскольку пфинализированнаям страница занимает меньше памяти, правда после финализации она уже не может быть модифицирована.
Функция
cpdf_global_set_documentjimits
bool cpdf_global_set_document_limits (int maxpages, int maxfonts, int maximages, int maxannotations, int maxobjects)
Функция cpdf__giobai_set__document_iimits о устанавливает границы документа.
工 Примечание ;
Поскольку функция устанавливает границы открываемого документа, ее нужно вызывать до открытия документа функцией updf_open ().
Функция cpdf__importjpeg
int cpdf_import_jpeg (int cpdf, string filename, float x, float y, float angle, float width, float height, float x-*scale, float у-scale, int gsave [,int mcide])
Функция cpdf_import_jpeg()открывает JPEG-изображение, содержащееся в файле filename. Изображение помещается на текущей странице в точке с координатами х и у. Параметр angle определяет» на сколько градусов должно быть повернуто изображение. Параметры width н height определяют ширину н высоту изображения соответственно. Параметры x-scale и y-scaie устанавливают значение коэффициента масштабирования по направлениям х и у соответственно. Для того чтобы функция работала корректно, параметр gsave должен иметь ненулевое значение.
Необязательный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию используются единицы измерения, установленные для страницы. В ином случае координаты измеряются в PostScript-пунктах без учета текущих единиц измерения.
Функция cpdf_place_inlinejmage
int cpdf_place_inline_image (int cpdf, int image, float xf float y, float angle, float width, float height [r int mode])
Функция cpdf_piace_iniine_lmage() помещает на страницу изображение, созданное image-функциями PHP, в точку с координатами х и у. Одновременно это изображение может быть масштабировано.
Параметр angle определяет, на сколько градусов должно быть повернуто изображение* Параметры width и height определяют ширину и высоту изображения соответственно.
Необязательный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию используются единицы измерения, установленные для спраницы. В ином случае координаты измеряются в PostScript-пунктах без учета текущих единиц измерения.
Функция cpdf^newpath
bool cpdf_newpath (int cpdf)
Функция cpdf_newpath()начинает новый путь в документе.
Функция cpdf_rect
bool cpdjrect (int cpdf, float x, float y, float width, float height [, int mode])
Функция epdf^rect()рисует прямоугольник с нижним левым углом в точке с координатами х и у, с шириной и высотой, определяемыми параметрами width и height соответственно.
Необязательный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию используются единицы измерения, установленные для страницы. В ином случае координаты измеряются в PostScript'Пунктах без учета текущих единиц измерения.
Функция cpdf_save
bool updf_save (int cpdf)
Функция cpdf—S&ve()сохраняет текущее окружение м работает аналогично PostScript-команде gsave. Эта функция необходима в тех случаях, когда нужно перевести или повернуть объект без воздействия на другие объекты.
Функция cpdf_restore
bool (int cpdf)
Функция cpdf_restore() восстанавливает окружение, сохраненное функцией cpdf_save(). Функция работает аналогично PostScript-команде grestore. Как правило, функции cpdf_save()и cpdf_restore()применяются совместно, при этом сначала сохраняется окружение, затем производятся необходимые действия с объектом, и после этого окружение восстанавливается (листинг 24.5).
^^3^^^^^^^^^^^^^^'^^l^i'犖禍空^Й^^^^^^^Й^8^^^^^^^^^'^^^^^1 ^^^^*f ^^^^^^岑**^^^^^^ "燃'* ** ; j Листинг 245, Функция apdf resto«;
<?php
cpdf_save ($cpdf);
/* блок кода, в котором проводятся необходимые действия: вращение объекта, его трансформация и т. д. */
cpdf_r estore($cpdf) ?>
Функция cpdf_rotate
bool cpdf_rotate (int cpdf, float angle)
Функция cpdf_rotate()устанавливает поворот в градусах на угол angle.
Функция cpdf_rotate_text
bool cpdf_rotate_text (int cpdf, float angle)
Функция cpdf_rotate_text()устанавливает поворот текста в градусах на угол angle.
Функция cpdf_scale
bool cpdf_scale (int cpdf, float x-scale, float y-scale)
Функция cpdf_scale () устанавливает коэффициент масштабирования в обоих направлениях с помощью параметров x-scale и y-scale.
Функция cpdf_set_action_url
bool cpdf_set_action_url (int cpdf, float xll, float yll, float xur, float yur, string url [f int mode])
Функция cpdf__set__action_url () добавляет гиперссылку с названием url с координатами (11х, 11у) нижнего левого угла и с координатами (urx, игу) правого верхнего угла.
Необязательный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию используются еди« ницы измерения, установленные для страницы. В ином случае координаты измеряются в PostScript-пунктах без учета текущих единиц измерения.
Функция cpdf_set_char_spacing
bool cpdf_set_char_spacing (int cpdf, float space)
Функция cpdf_set_char_spacing()устанавливает межсимвольное расстояние space.
Функция cpdf_set_creator
bool cpdf_set_creator (string creator)
Функция cpdf_set_creator () устанавливает поле creator (автор) в PDF- документе.
Функция cpdf_set_current_page
bool cpdf__set_current_page (int cpdf, int page__nun4)er)
Функция cpdf_set__current_page () устанавливает страницу с номером page_number в качестве текущей страницы, на которой выполняются все последующие операции. Пока страница не будет завершена функцией
cpdf_finaiize^page()» остается возможность переключения между страницами.
Функция cpdf_set_horiz_scaling
bool cpdf__set_horiz_sca1ing (int cpdf, float scale)
Функция cpdf_set_horiz_scaiing о устанавливает масштабирование по горизонтали в процентах, равных значению параметра scale.
Функция cpdf_set_keywords
bool cpdf_set_keywords (string keywords)
Функция cpdf_set_keywords () устанавливает поле keywords PDF-документа.
Функция cpdf^set leading
bool cpdf_set leading (int cpdf, float distance)
Функция cpdf_set_leading () устанавливает межстрочное расстояние distance.
Функция cpdf_set_page_animation
bool cpdf_set_page_animation (int pdf document, int transition, float duration)
Функция cpdf_set__page_animation () устанавливает способ (параметр transition) и время (параметр duration) перехода от страницы к странице.
Возможными значениями параметра transition являются следующие:
口 0 — нет перехода;
1 — страницы чередуются через две линии, проходящих по экрану;
2 — страницы чередуются через две линии, проходящих по экрану;
3 — страницы чередуются через бокс (box);
4 — страницы чередуются через одну линию, проходящую по экрану;
5 — при чередовании страниц старая страница растворяется, и появляется новая;
6 — аналогично значению 5, только эффект растворения переходит от одного края экрана к другому;
7 — старая страница просто замещается новой (это значение параметра выставлено по умолчанию).
Значением параметра duration является количество секунд между сменой страниц.
Функция cpdf_set_subject
bool cpdf_set_subject (string subject)
Функция cpdf_set_subject () устанавливает поле subject (тема) PDF- документа.
Функция cpdf_set_text_matrix
bool cpdf_set_text_matrix (int cpdf, array matrix)
Функция cpdf_set__text_matrix() устанавливает матрицу matrix, которая описывает трансформацию, применяемую к текущему шрифту текста.
Функция cpdf_set_text_pos
bool cpdf_set_text_pos (int cpdf# float x# float у [, int mode])
Функция cpdf_set_text_pos()устанавливает позицию (с координатами х и у) текста для последующего вызова функции cpdf_show.
Необязательный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию используются единицы измерения, установленные для страницы. В ином случае координаты измеряются в PostScript-пунктах без учета текущих единиц измерения*
Функция cpdf_set_text_risе
bool cpdf_set_text_rise (int cpdf, float value)
Функция cpdf set_text_rise() устанавливает возвышение текста на value единиц.
Функция cpdf_set_title
bool cpdf^set^title (string title)
Функция cpdf_set_titie О устанавливает поле title (заголовок) PDF- документа.
Функция cpdf_set_word_spacing
bool cpdf_set_word_spacing (int cpdf, float space)
^Функция cpdf_set_word_spacing()устанавливает расстояние space между словами.
Функция cpdf_setdash
bool cpdf__setdash ( int cpdf, float white, float black)
Функция cpdf_setdash ()устанавливает штрихпунктирную линию из блоков white и блоков Ыаск. Если оба параметра равны 0, устанавливается сплошная линия.
Функция cpdf_setrgbcolor
bool cpdf_setrgbcolor (int cpdf, float red_value, float green_value, float blue_value}
Функция cpdf_setrgbcoior {) устанавливает текущий цвет прорисовки и заполнения путем назначения RGB-значения цвета. Параметры red vaiue, green_vaiue и blue_value устанавливают значения красного, зеленого и синего цветов (наиболее близкие к цвету, определенному RGB-компонентами) в текущей цветовой гамме*
24338. Функция cpdf_setflat
bool cpdf^setflat (int cpdf, float value)
Функция cpdf_setflat() устанавливает плоскость co значением value от О до 100.
Функция cpdf_show_xy
bool cpdf_show_xy (int cpdf, string text, float x, float у [, int mode]) Функция cpdf_show_xy()выводит строку текста text в позиции с координатами х и у.
Необязательный параметр mode определяет размер единиц измерения. Если этот параметр равен 0 или отсутствует, то по умолчанию используются единицы измерения, установленные для страницы. В ином случае координаты измеряются в PostScript-пунктах без учета текущих единиц измерения.
Функция cpdf_stringwidth
float cpdf_stringwidth (int cpdfr string text)
Функция cpdf_stringwidth () возвращает ширину строки text. Необходимо, чтобы текущий шрифт текста был установлен до вызова этой функции.
Функция cpdf^translate
void cpdf_translate < int pdf document, float xr float у [, int mode]) Функция cpdf_t tans late()устанавливает точку отсчета системы координат в значение с координатами х и у.
Заключение
Итак, книга завершена. Мы прошли через 24 главы» в которых подробно рассказывалось о том, как создать многофункциональный Web-сайт, используя в качестве основного инструмента разработки язык программирования PHP 5. На страницах этой книги мы постарались рассмотреть разработку как можно большего количества программных блоков, которые могут понадобиться Web-разработчику при создании Web-сайта.
Что бы не говорили, но чтение книги всегда похоже на слушание авторского монолога. Что, в обтем-то, не очень хорошо, поскольку быстрая обратная связь в равной мере необходима и читателям» и авторам. С этой целью, чтобы авторский монолог превратить в полноценный диалог с читателем, мы создали на своем сайте форум, который посвящен программированию на PHP и расположен по адресу http://www.softtime,ru/foruin/. Мы искренне надеемся, что после того как вы перелистнете эту страницу, мы с вами не расстанемся, а встретимся на нашем форуме.
Успехов вам и всего доброго!
Приложение
Описание компакт-диска
В этом приложении приведено описание содержимого компакт-диска (табл. ГП). На этом диске вы найдете статьи по PHP, не вошедшие в данную книгу, и исходные коды всех расемагриваемых в книге Web-приложений. Заметим, что на диске (папка \П) находится полнофункциональный корпоративный Web-сайт, который можно быстро (и гГрактически ничего не меняя в коде) запустить в реальную эксплуатацию. Для этого нужно лишь соз* дать необходимые базы данных.
Таблица П1. Описание содержимого компакт-диска
Папки
Описание
Главы
\doc
Статьи по PHP
1
\1
Web-приложения к первой части
2
VI
Web-приложения ко второй части
6,7, & 9. 10, 11, 12, 13, 14
МП
Web-приложения к третьей части
15, 16, 18
VV
Web-приложения к четвертой части
19, 20, 21
Предметный указатель
с
Cookies 227
CSS 208
CSV-файл 265
F
Flash в PHP 898
FTP 633
G
GIF 873
H
HTTP-аутентификация в PHP 155
НТТР-заголовки 143
I
IMAP 512, 519
IMAP-сервер 663
IMAP-функции 661
J, L
JavaScript 158
LiteForum 713
M
MIME 532 533
Multipurpose Internet Mail Extensions 533
P
PDF 924
phpMyAdmin 108-115
PNG 867 875
POP 511
POP 3-сервер 663
РОР-сеанс 518
SMTP 511, 514
SMTP-сервер 528
T
Triple-DES. См. алгоритм 3DES
w
WBMP 869
Web-сервер Apache:
установка под Linux 16
установка под Windows 7
WinMySQLAdriin 41
ААгенты почтовые 512Алгоритм:3DES 226MD5 224ААгенты почтовые 512Алгоритм:3DES 226MD5 224
А
Агенты почтовые 512
Алгоритм:
3DES 226
MD5 224
А
Агенты почтовые 512
Алгоритм:
3DES 226
MD5 224
Индексы UNIQUE и PRIMARY KEY.
См. Индекс обьгчный и уникальный
Инсталляция phpMyAdmii】109
Безопасность Web-приложений 215
Библиотека:
ClibPDF 925
GDLib 481 857
ming 898
г
Генератор паролей 170
Голосование 430
Гостевая книга 430, 459
д
Дизайн форума 787
Директива:
< Directory /> 20
allow 2J
AllowOverride 20
deny 21
Options 20
3
Защита информации хранящейся в cookie 227
и
Импорт прайс-листа 377
Индекс:
FULLTEXT 78
обычный 78
полнотекстового поиска 78
уникальный 78
Индексирование 77
Каталог продукции 333
Ключевое слово DISTINCT 71
Команда:
ALTER TABLE 57
CREATE DATABASE 51
CREATE TABLE 52
DELETE 63
DESCRIBE 57
DROP DATABASE 61
DROP TABLE 60
INSERT 61
SELECT 63
SHOW DATABASES 51
UPDATE 67
USE 52
Кэш 147
M
Многотабличные запросы 72
и
Настройка:
Apache 20
виртуальных хостов 22
о
Оператор:
EXPLAIN 80
GROUP BY 67
LIKE 70
LIMIT 65
NOT LIKE 70
ORDER BY 66
SHOW 68
SHOW COLUMN TYPES 68
SHOW PROCESSLIST 69
WHERE 66
Отправка сообщений с сайта 511
Отправка сообщений с вложениями 532
п
Подсветка кода 170
Поиск по каталогу продукции 384
Поиск по сайту. Cw. Система полнотекстового поиска по сайту
Почтовая служба 661
Почтовые агенты 512
Почтовый сервер 511
Проверка корректности данных вводимых посетителем 215
Прокси-сервер 147
Профилирование:
запросов 80
кода 162
Регулярные выражения 168
Роботы поисковых систем 551
с
Сервис whois 165
Система:
администрирования контента 250
администрирования сайта 243
администрирования форума 809
анализа посещаемости сайта 543
голосования 430
мониторинга сайта в поисковых системах 619
полнотекстового поиска
по сайту 394
рассылки писем 678
Скин форума 787
Сообщение электронной почты 519
Счетчик посетителей. Си. Система
анализа посещаемости сайта
т
Таблицы:
BDB 50
HEAP 50
MERGE 50
Му ISAM 49
Транзакция 73
У
Установка:
MySQL 40
PHP 5 23
ф
Файл php.ini:
основные параметры 26
Формат:
GIF 873
PDF 924
PNG 867 875
WBMP 869
Форум 713
Фотогалерея 481
Функции хеширования 224
Функция:
BENCHMARK 80
COUNT 71
cpdfladd_annotation 933
cpdf_add_outline 927
cpdf_arc 934
cpdf_begin_text 928
cpdf_clip 934
cpd匚close 930
cpdf_closepath 934
cpdf_closepalh_fill_stroke 934
cpdf_continue_text 935
cpdf_curveto 935
cpdf_end_text 928
cpdf_fill 935
cpdf_fill_stroke 934
cpd匚finalize 930
cpdf_finalize_page 935
cpdf_globaLset_docunientJimits 935 cpdf_importjpeg 936 cpdMineto 930 cpdf_moveto 929 cpd匸newpath 936 cpdf_open 926
cpdf_output_bufTer 930 cpdf_pagejnit 927 cpdf_place_inline_image 936 cpdf_rec 937 cpdf_restore 937 cpdf_rotate 937 cpdf_rotate_text 938 cpdf_save 937 cpdf_save_to_file 926 cpdf_scale 938 cpdf_set_action_url 938 cpdf_set_char_spacing 938 cpdf_set_creator 938 cpdf_set_fbnt 928 cpdf_set_horiz_scaling 939 cpdCsct_keywords 939 cpdf_sei_page_aiiimation 939 cpdf_set_subject 940 cpdf_set_text_niatrix 940 cpdf_set_text_pos 940 cpdf_set_text_rendering 929 cpdf_set_text_rise 940 cpdf_setdash 941 cpdf_setflat 941 cpdf_setrgbcolor 941 cpdf_show_xy 941 cpdf_stringwidtli 941 cpdf_stroke 930 cpdfjext 929 cpdf_translate 941 exif_i magetype 860 exif_read_data 860 exif_thumbnail 862 fsockopen 166 ftp_chmod 647, 648 Пр_соппес1 647 ftp_delete 659 ftp__nikdir 647 ftp»nb_continue 657 ftp_nb_get 660 ftp_nb_put 656 ftp_n1ist 652 ftp_rename 651 ftp_rmdir 652 gdjnfo 859 getimagesize 862 header 143 headersjist 144cpdf_output_bufTer 930 cpdf_pagejnit 927 cpdf_place_inline_image 936 cpdf_rec 937 cpdf_restore 937 cpdf_rotate 937 cpdf_rotate_text 938 cpdf_save 937 cpdf_save_to_file 926 cpdf_scale 938 cpdf_set_action_url 938 cpdf_set_char_spacing 938 cpdf_set_creator 938 cpdf_set_fbnt 928 cpdf_set_horiz_scaling 939 cpdCsct_keywords 939 cpdf_sei_page_aiiimation 939 cpdf_set_subject 940 cpdf_set_text_niatrix 940 cpdf_set_text_pos 940 cpdf_set_text_rendering 929 cpdf_set_text_rise 940 cpdf_setdash 941 cpdf_setflat 941 cpdf_setrgbcolor 941 cpdf_show_xy 941 cpdf_stringwidtli 941 cpdf_stroke 930 cpdfjext 929 cpdf_translate 941 exif_i magetype 860 exif_read_data 860 exif_thumbnail 862 fsockopen 166 ftp_chmod 647, 648 Пр_соппес1 647 ftp_delete 659 ftp__nikdir 647 ftp»nb_continue 657 ftp_nb_get 660 ftp_nb_put 656 ftp_n1ist 652 ftp_rename 651 ftp_rmdir 652 gdjnfo 859 getimagesize 862 header 143 headersjist 144
cpdf_output_bufTer 930 cpdf_pagejnit 927 cpdf_place_inline_image 936 cpdf_rec 937 cpdf_restore 937 cpdf_rotate 937 cpdf_rotate_text 938 cpdf_save 937 cpdf_save_to_file 926 cpdf_scale 938 cpdf_set_action_url 938 cpdf_set_char_spacing 938 cpdf_set_creator 938 cpdf_set_fbnt 928 cpdf_set_horiz_scaling 939 cpdCsct_keywords 939 cpdf_sei_page_aiiimation 939 cpdf_set_subject 940 cpdf_set_text_niatrix 940 cpdf_set_text_pos 940 cpdf_set_text_rendering 929 cpdf_set_text_rise 940 cpdf_setdash 941 cpdf_setflat 941 cpdf_setrgbcolor 941 cpdf_show_xy 941 cpdf_stringwidtli 941 cpdf_stroke 930 cpdfjext 929 cpdf_translate 941 exif_i magetype 860 exif_read_data 860 exif_thumbnail 862 fsockopen 166 ftp_chmod 647, 648 Пр_соппес1 647 ftp_delete 659 ftp__nikdir 647 ftp»nb_continue 657 ftp_nb_get 660 ftp_nb_put 656 ftp_n1ist 652 ftp_rename 651 ftp_rmdir 652 gdjnfo 859 getimagesize 862 header 143 headersjist 144
cpdf_output_bufTer 930 cpdf_pagejnit 927 cpdf_place_inline_image 936 cpdf_rec 937 cpdf_restore 937 cpdf_rotate 937 cpdf_rotate_text 938 cpdf_save 937 cpdf_save_to_file 926 cpdf_scale 938 cpdf_set_action_url 938 cpdf_set_char_spacing 938 cpdf_set_creator 938 cpdf_set_fbnt 928 cpdf_set_horiz_scaling 939 cpdCsct_keywords 939 cpdf_sei_page_aiiimation 939 cpdf_set_subject 940 cpdf_set_text_niatrix 940 cpdf_set_text_pos 940 cpdf_set_text_rendering 929 cpdf_set_text_rise 940 cpdf_setdash 941 cpdf_setflat 941 cpdf_setrgbcolor 941 cpdf_show_xy 941 cpdf_stringwidtli 941 cpdf_stroke 930 cpdfjext 929 cpdf_translate 941 exif_i magetype 860 exif_read_data 860 exif_thumbnail 862 fsockopen 166 ftp_chmod 647, 648 Пр_соппес1 647 ftp_delete 659 ftp__nikdir 647 ftp»nb_continue 657 ftp_nb_get 660 ftp_nb_put 656 ftp_n1ist 652 ftp_rename 651 ftp_rmdir 652 gdjnfo 859 getimagesize 862 header 143 headersjist 144
headers_sent 144 highlight_file 171 highlight^string 171 hlmlspecialchars 219 image_type_to_minie_type 863 image2wbmp 875 imagealphablending 884 imagearc 887
imagechar 882
imagechanip 883
imagecolorallocate 892 imagecolorallocatealpha 892 imagecolorat 893 iniagecolordeallocate 893 imagecolorresolve 893 imagecolorresolvealpha 893 imagecolorsfbrindex 893 iniagecopy 870
inwgecopymerge 870 imagecopyniergegray 870 imagecopyresanipled 871 imagecopyresized 872 imagecreate 865
imagecreatefronigd 867 imagecreatefromgd2 868 imagecreatefromgd2part 868 imagecreatefromgif 866 iniagecreatefronijpeg 865 imagecreatefrompng 867 i magecreate fromst ri ng 868 imagecreatefromwbmp 868 imagecreatefronixbin 869 iniagecreatefromxpm 869 imagecreatetniecolor 865 imagedashedline 886 imagedestroy 870
imageellipse 887
imagefill 889
imagefiiledarc 888
imageftlledeilipse 889 imagefilledpolygon 888 imagefilledrectangle 887 imagefilltoborder 889 imagefilter 872 imagefbntheight 876 imagefontwidth 876 imageftbbox 876
(окончание рубрики см на с. 948)
Функция (окончание). imagefttext 878 imagegd 874 imagegd2 874 imagegif 873 imageinterlace 866 iniageistniecolor 891 imagejpeg 872 imageline 886 imageloadfont 880 imagepng 873 imagepolygon 887 imagepsbbox 877 imagepscopylbnt 881 imagepsencodefbnt 881 imagepsextendfont 882 imagepsfreefbnt 881 imagepsloadfont 880 imagepsslantfbnt 882 imagepstext 879 imagerectangle 887 image rotate 872 imagesavealpha 867 imagesetbmsh 885 imagesetpixel 886 imagesetstyle 885 imagesetthickness 885 imagesettiie 885 imagestring 883 imagestringup 883 imagesx 863 imagesy 863 imagetniecolortopalette 891 imagettfbbox 877 imagettflext 878 imagetypes 864 imagewbmp 874 imagexbm 874 imap_close 675 imap_delete 677 imap_expunge 677 imapjieader 675
imap_open 674 isset 216 jpeg2wbmp 875 mail 522 mcrypt 226 nid5 224 mysql_close 83 mysql_connect 82 mysql_fetch_array 90 n)ysql_fetch__assoc() 88 mysql_fetch_object 9! mysql_fetch_row 87 mysqLnum_field$ 92 mysql_nиin__rows 92 mysql_qucry 85 mysql_result 86 mysql_select_db 84 png2wbmp 875 preg_match 169 session_cache_expire 151 session_cachejiniiter 150 setcookie 161 stripslashes 219 uniqid 170
X
Хеш-код 226
Ш
Шифрование: cookie 229 однонаправленное 224 симметричное 226 средствами PHP 224
я
Язык запросов SQL 50
В листинге 4.16 приведен скрипт upload.php, который загружает файл на сервер и копирует его в каталог /temp/.
堂亨*虢女坤g竺^沦储用橙締翠*у举号矗寸号:涉у 产fT**f”尹 Г Листинг 4.16. Файл upload.php
".5Ж:二:二候出:交Т龈J京仙;1.茗;..:.;…..,; /;.;八* …八.■一 *..
<html>
<head>
Если файл index.php не получает параметра id_page, значение этого пара
метра устанавливается равным 1, что соответствует первому разделу в табли
це artpage. Если запись с таким первичным ключом отсутствует, в системе
CMS необходимо предусмотреть получение минимального значения первично
го ключа при помощи запроса "SELECT MIN (id_artpage) FROM artpage".
После этого формируется SQL-запрос на выборку статей для выбранного раздела с прямой сортировкой их по полю name, обеспечивающей вывод статей в алфавитном порядке. Если результат запроса содержит хоть одну запись, формируется таблица с метаданными статей и тремя управляющими ссылками: Скрыть/Отобразить, Редактировать и Удалить (см. рис. 74).
В зависимости от значения поля hide таблицы articles напротив статьи выводится ссылка Скрыть, если статья в настоящий момент доступна для
В случае некорректного значения одного из обязательных полей здесь и далее
используется функция links (), которая выводит сообщение об ошибке и останавливает выполнение приложения.
Далее анализируется значение параметра флага hide, который в установленном состоянии принимает значение “ст", а в сброшенном — пустое значение. В первом случае временная переменная $showhide? используемая далее для формирования SQL-оператора insert принимает значение 'show*, во ВТОрОМ — 'hide'.
g謬爲1?^壽?#1&g'7^1入/噓舒沦>禽黯器&*;兼裁鼎$糯^磨竊^^1霄#Я寫1^;",定;,,"禹,飞1:急1客^*&:'"閣說.寸粉}#.掷辭."靠・i;幅騷^義旗;"《*蓊歸柄&二*“&*川*関浄5«"4推理*.占:
<?php
mail(Hkuznetsov@softtime.гии, нПривет! *\ $msg,
В файле menu.php размещаются ссылки на сервисные страницы системы
PowerCounter. Если значение параметра id_paga установлено, информация
В отличие от скрипта добавления новостной позиции (см. листинг 18.5) в листинге 18.7 используется SQL-оператор update, обновляющий уже существующую запись в таблице news. При этом отправки почтового сообщения подписчикам уже не происходит.
Удаление новостной позиции осуществляется при помощи скрипта delnews.php, код которого представлен в листинге 18.8. Скрипт принимает два параметра:
口 id_news — первичный ключ запмсм таблицы news, соответствующей удаляемому новостному блоку;
口 start — позиция, начиная с которой выводятся новостные блоки для корректного возврата на страницу администрирования.
••ХИТРОСТИ** PHP••ХИТРОСТИ** PHPСИСТЕМААДМИНИСТРИРОВАНИЯ КОНТЕНТА САЙТАСИСТЕМААДМИНИСТРИРОВАНИЯ КОНТЕНТА САЙТАРАБОТА С БАЗАМИ ДАННЫХРАБОТА С БАЗАМИ ДАННЫХРАБОТА С ГРАФИКОЙ, FLASH, PDF-ДОКУМЕНТАМИРАБОТА С ГРАФИКОЙ, FLASH, PDF-ДОКУМЕНТАМИ
••ХИТРОСТИ** PHP
••ХИТРОСТИ** PHP
СИСТЕМА
АДМИНИСТРИРОВАНИЯ КОНТЕНТА САЙТА
СИСТЕМА
АДМИНИСТРИРОВАНИЯ КОНТЕНТА САЙТА
РАБОТА С БАЗАМИ ДАННЫХ
РАБОТА С БАЗАМИ ДАННЫХ
РАБОТА С ГРАФИКОЙ, FLASH, PDF-ДОКУМЕНТАМИ
РАБОТА С ГРАФИКОЙ, FLASH, PDF-ДОКУМЕНТАМИ
МАКСИМ КУЗНЕЦОВ ИГОРЬ симдянов СЕРГЕЙ ГОЛЫШЕВ
PHP 5
ПРАКТИКА СОЗДАНИЯWEB-САЙТОВ
РАЗРАБОТКА ДИНАМИЧЕСКИХ WEB-ПРИЛОЖЕНИЙ
Максим Кузнецов Игорь Симдянов Сергей Голышев
ПРАКТИКА СОЗДАНИЯWEB-САЙТОВ
Санкт-Петербург«БХВ-Петербург»2005
УДК 6813068+800.92РНР5
ББК 32.973.26-018.1
К89
Кузнецов М. В., Симдянов И. В., Голышев С. В.
К89 PHP 5. Практика разработки Web-сайтов. — СПб.: БХВ-Петербург, 2005. — 960 с.: ил.
ISBN 5-94157-552-1
С помощью PHP 5 разрабатываются многочисленные компоненты, входящие в состав полнофункционального Web-сайта. Рассматриваются работа с СУБД MySQL, вопросы защиты Web-приложений, работа с графикой, Flash и PDF-документами. В качестве практических примеров разработаны такие приложения, как система администрирования контента сайта, форум, система сбора и анализа статистики посетителей сайта, система рассылки, FTP-менеджер, универсальный каталог продукции и др. Книга ориентирована на читателей, знакомых с языками HTML и PHP, но не имеющих большого опыта разработки динамических Web-приложений. Компакт-диск содержит исходные коды всех Web-приложений, разрабатываемых в книге и в сумме составляющих готовый полнофункциональный корпоративный Web-сайт.
Для lVeb-разработчиков
УДК 68I.3.068+800.92PHP 5
ББК 32.973.26-018Л
Группа подготовки издания:
Главный редактор
Зам. глапного редактора Зав. редакцией Редактор Компьютерная верстка Корректор Дизайн серии Оформление обложки Зав. производством
Екатерина Кондукова Евгений Рыбаков Григорий Добин Алия Амирова Натальи Смирновой Наталия Першакова Инны Таниной Игоря Цырульникова Николай Тверских
Лицензия ИД № 02429 от 24.07.00. Подписано 8 печать 25.07.05.
Формат 70x100V|fl, Печать офсетная. Усл. пвч. л. 77,4.
Доп. тираж4000 экэ. Заказ № нв1"БХВ-Петербург*, 194354, Санкт-Петербург, ул. Есенина. 5&
Санитарно-эпидемиологическое заключение на продукцию № 77.99.02.953,Д.006421Л1.04от 11.11,2004 г. выдано Федеральной службой по надзорув сфере защиты прав потребителей и благополучия человека.
Отпечатано с готовых диапозитивовв「УП ^Типография "Наука"199034, Санкт-Петербург, 9 линия. 12
ISBN 5-94157-552-1ISBN 5-94157-552-1С Кузнецов М. В., Симдянов И. В,, Голышев С В., 2005
ISBN 5-94157-552-1
ISBN 5-94157-552-1
С Оформление, издательство "ЕХВ^Пстербур/, 2005
Оглавление
Введение
Для кого и о чем эта книга——
Как построена книга— 2
Благодарности *3
Часть I. Основы разработки приложений на PHP 5 5
Глава 1・ Работа с Web-сервером Apache 7
Установка Web-сервера Apache 2,0.0 7
L Установка под Windows 7
Управление Apache. 13
Установка под Linux 16
】.2・ Конфигурирование Web-сервера Apache 2.0.0— 19
12L Базовая настройка Apache 19
.2.2. Установка и настройка PHP 5* 23
PHP.INI 26
Настройки язнка*... 26
Ограничение по ресурсам; 28
Обработка ошибок и журнализация28
L34 Обработка данных** 30
Пути и директории 31
Загрузка файлов **32
Работа с сокетами*...»・,・,・* 32
L38 Динамические расширения,. 33
Установки для модулей * 33
Глава 2. Работа с MySQL 39
Установка и настройка MySQL 39
Установка MySQL на платформу Windows 40
Установка MySQL на платформу Linux......* 43
2*2. Оптимизация SQL-запросов 44
Числовые данные 45
Строковые данные 46
Календарные данные48
Выбор типа данных* 48
Таблицы MySQL 49
Структурированный язык запросов (SQL)50
Индексирование77
Общие вопросы оптимизации 80
Оптимизация копа PHP при работе с MySQL 8)
丄 PHP м MySQL81
Приемы работы с MySQL92
Работа с phpMyAdmin 108
Инсталляция phpMyAdmin109
Конфигурирование phpMyAdmin115
Использование phpMyAdmin124
Экспорт баз данных131
Работа с пользователями и привилегиями...135
Работа с таблицами..140
Глава 3. Заголовки HTTP и работа с ними143
Функции PHP для работы с HTTP-заголовками143
Управление кэшированием147
Функции sessionjcachejimiter и session_cache_expire150
Примеры HTTP-заголовков 152
ЗаголовокAccept 152
ЗаголовокAccept-Charset 153
ЗаголовокAccept-Language153
ЗаголовокAllow 154
ЗаголовокConnection 154
Заголовок Date154
Заголовок Expires154
Заголовок From154
Заголовок Last-Modified155
Заголовок Location 155
Заголовок Referer155
Практический пример: HTTP-аутентификация средствами PHP155
Практический пример: получение документа по HTTP-протоколу156
Глава 4. "Хитрости" PHP158
PHP и JavaScript 158
Передача переменных из JavaScript в PHP 158
Как проверить, включены ли cookies?160
О профилировании кода 162
Получение информации об I P-адресах и доменах164
Часто используемые регулярные выражения168
Генератор паролей 170
Подсветка кода с помощью стандартной функции PHP170
Подсветка синтаксиса PHP (собственная функция) 171
Загрузка файлов на сервер 176
Счетчик загрузки файлов с сервера181
Предотвращение загрузкн страниц 202
LL Определение размера загружаемого файла207
1.2. PHP и CSS: динамическое оформление содержания сайта208
Динамическая оптимизация страниц 210
4J 1.4. Дата последнего обновления страницы 212
】5 Автоматическое распознавание ссылок*212
Глава 5. Безопасность создаваемых приложений.„215
5.1. Проверка корректаости данных, вводимых пользователем215
5.L1. Проверка заполнения обязательного поля 216
Проверка допустимости вводимых данных 217
5J.3, Удаление HTML-тегов и обратных слэшей 218
Публикация изображений и файлов 220
Методы шифрования..... 224
Однонаправленное шифрование.....224
532. Симметричное шифрование 226
Проблемы безопасности, связанные с cookie .227
.4.1. Установка области видимости cookie 228
542* Шифрование cookie 229
5・5. Безопасная установка PHP 230
56 Безопасная настройка PHP 233
5・6」.Параметр display_errors ..233
6.2. Параметр errof_reporring 233
Параметр variables_order .233
Параметр open_basedir 234
Параметр disablefunctions 234
Параметр allow_url_fbpen 234
5.6.7. Параметр register^lobals 235
5.7. Безопасная установка MySQL 236
Часть II. Разработка системы администрирования сайта241
Глава 6. Проектирование 243
Глава 7. Система администрирования контента 249
Описание системы CMS 251
Требования к системе администрирования контента (CMS)251
Терминология -251
Структура и элементы статьи 252
72 Работа с системой администрировании CMS 254
Управление разделами статей 254
Управление метаданными статьи.....257
Редактирование статьи259
73 Разработка системы* 266
Глава 8. Универсальный каталог продукции333
Работа с каталогом в режиме пользователя 335
Работа с каталогом в режиме администратора 336
Разработка каталога '*Риэлторские услуги0 341
Импорт прайс-листа377
Совершенствование системы администрирования378
Поиск по каталогу384
Глава 9. Система полнотекстового поиска по сайту394
Полнотекстовый поиск в СУБД MySQL.394
Полнотекстовый поиск в системе CMS396
Глава 10. Блок "Контакты11 403
Система представления информации404
Система администрирования405
Разработка системы администрирования410
Глава И. Блоки "Голосование" и "Гостевая книга"430
Система голосования430
Система администрирования432
Разработка системы голосования437
Система представления 453
Гостевая книга459
Глава 12. Фотогалерея481
Описание фотогалереи481
Система администрирования 483
Разработка фотогалереи486
Глава 13. Система отправки сообщений с сайта511
Введение в работу электронной почты511
Почтовые агенты 512
Протоколы SMTP, POP и IMAP 514
Структура сообщения электронной почты519
Отправка простых сообщений 522
Функция mail для отправки сообщений электронной почты522
Функция для работы с удаленным SMTP-сервером529
Отправка сообщений с вложениями532
Типы данных (поля заголовка Content-Type) 533
Способ представления данных (поля заголовка Content-Transfer^Encoding 535
Заголовок Content-Disposition 536
Класс, реализующий пересылку вложенных файлов536
Глава 14. Система анализа посещаемости сайта543
Описание системы PowerCounter ».*543
142 Разработка системы PowerCounter556
Часть III. Разработка дополнительных Web-служб617
Глава 15. Система мониторинга позиций сайта в поисковых системах619
Описание системы 620
15・2, Разработка системы мониторинга 622
Глава 16. FTP-менеджер633
Глава 17. Почтовая служба 661
Создание базы данных 662
Архитектура приложения.... 664
Создание учетных записей669
2. Просмотр почтового ящика и чтение сообщений672
3. Отправка сообщений677
Глава 18. Система рассылки писем678
18.1. Разработка системы администрирования.678
182 Разработка системы представления 701
Часть IV. Профессиональный форум на PHP 5711
Глава 19. Проектирование 713
Проектирование базы данных..714
Глава 20. Создание базового "движка”723
Описание форума723
292 Разработка форума 730
Дизайн форума 787
Глава 21. Создание системы администрирования809
Описание системы администрирования809
2L1.1. Страница адм ин истрирования форума809
21.12 Страница Разделы форума 811
21.1.3, Страница Модерирование форума812
21J.4. Страница Статистика форума..813
21.15 Страница nocemumejiu форума813
21.1.6. Страница Настройки форума815
Разработка системы администрирования
L Страница администрирования форума
Страница Разделы форума
Страница Модерирование
Страница Статистика форума
Страница Посетипши форума
Страница Настройки форума
Часть V. Работа с библиотеками расширений PHP 5
Глава 22. Работа с графикой. Библиотека GDLib
Установка
22J.I. Установка под Windows
Информационные функции
Функция gdjtifb
Функция exffjmag5ype
Функция exif_read_data:
Функция read_exif_data
Функция exifjhumhnail
Функция getimagesiz^-*
Функция imagesx
Функция imagesy
Функция imageJype_tojnime_type
Функция imagetypes
Функции создания изображений
Функция imagecreate
Функция imagecreatetruecolor
Функция imagecreaiefronypeg
Функция imageinterlace
Функция imagecreatefromgif.
2236. Функция imagecreatefrompng
Функция imagesavealpha
Функция imagecreatefrvmgd
Функция ima^ecreatefromgd2
Функция imagecreatefromgd2part
Функция imagecreatefromstring
Функция imagecreatefromwbnip
Функция imagecreatefromxbm
Функция imagecreatefromxpm
Функция imagedestroy
Функции работы с изображениями
2241. Функция imagecopy
Функция imagecopymerge
Функция imagecopymergegray
816818833838844846850855857.857858859.859.860.860.862.862.862.863.863.863.864.865.865865.865866.866.867867.867868.868.868.868-.870.870.870.870.870.871816818833838844846850855857.857858859.859.860.860.862.862.862.863.863.863.864.865.865865.865866.866.867867.867868.868.868.868-.870.870.870.870.870.8712244. Функция imagecopyresampled
816818833838844846850855857.857858859.859.860.860.862.862.862.863.863.863.864.865.865865.865866.866.867867.867868.868.868.868-.870.870.870.870.870.871
816818833838844846850855857.857858859.859.860.860.862.862.862.863.863.863.864.865.865865.865866.866.867867.867868.868.868.868-.870.870.870.870.870.871
Функция iniagecopyresized
Функция imagerotate
Функция imagefilter.
Функции сохранения и вывода изображений
Функция imagejpeg
Функция 〃打ng喈/
Функция imagepng
Функция imagegd
Функция imagegd2
Функция imagexbm...»
Функция imagewbmp
Функция image2wbmp
Функция Jpeg2whmp
Функция png2wbmp*
Функции работы с текстом
Функция imagefontheight
Функция imagefontwidth
Функция imagefibbox....,
Функция imagettfbbox
Функция imagepsbbox
Функция imagefttext
Функция imagettftext
Функция imagepstext
Функция imagepsloadfbnt
Функция imageloadfont
Функция imagepscopyjbnt
Функция imagepsfreefont
Функция imagepsencodefbnt
Функция imagepsextendjbnt
Функция imagepsslantfbnt
Функция imagechar
Функция imagecharup
Функция imagestring
Функция imagestringlip
Функции настройки рисования
Функция imagealphablending
Функция imagesetthickness
Функция imagesetstyle
Функиия imagesetbrush
Функция imagesetfile..:
Функция imagelayereffect
Функция imageantialias.
Функции рисования
Функция imagesetpixel
Функция imageline
2 27 78 82 27 78 822233444455566667 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7888888888888888822233444455566667 7 7 7 7 7 7 7 7 7 7 7 7 7 7 788888888888888887 7 87 7 78 8 87 7 87 7 78 8 88 9 07 7 88 8 88 9 07 7 88 8 801112 2 23 33448888888 8 888888888888888801112 2 23 33448888888 8 88888888888888885 5 5 5 6 68 8 8 8 8 88 8 8 8 8 85 5 5 5 6 68 8 8 8 8 88 8 8 8 8 86886886 6 68 8 88 8 86 6 68 8 88 8 8Функция imagedashedline
2 2
7 7
8 8
2 2
7 7
8 8
2223344445556666
7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 78888888888888888
2223344445556666
7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 78888888888888888
7 7 8
7 7 7
8 8 8
7 7 8
7 7 7
8 8 8
8 9 0
7 7 8
8 8 8
8 9 0
7 7 8
8 8 8
01112 2 23 3344
8888888 8 8888888888888888
01112 2 23 3344
8888888 8 8888888888888888
5 5 5 5 6 6
8 8 8 8 8 8
8 8 8 8 8 8
5 5 5 5 6 6
8 8 8 8 8 8
8 8 8 8 8 8
6
8
8
6
8
8
6 6 6
8 8 8
8 8 8
6 6 6
8 8 8
8 8 8
Функция imagerectangle
Функция imagepolygon
Функция imagearc
Функция imageellipse
Функция imagefllledrectangle
Функция imagefilledpolygon
Функция imagefilledarc
Функция imagefilledellipse
Функция imagefill.
Функция imagefilltoborder
Функции для работы с цветовой информацией
Функция imageistmecolor
Функция imagetruecolorfopalette
Функция imagecolornlloca抡
Функция imagecolorallocatealpha
Функция imagecolordeallocate
Функция imagecolorat
Функция imagecotorsforindex
Функция imagecolorresolve
Функция imagecolorresolvealpha
Функция imagecolorexact
Функция imagecolorexactalpha
Функция imagecolorclosest
Функция imagecolorclosestalpha:
Функция imagecolorclosesthwb
Функция imagecolorset
Функция imagecolortransparent
Функция imagecolorstotal
Функция imagecolormatch
Функция imagepalettecopy
Функция imagegammacorrect
Другие функции
Функция iptcembed
Функция iptcparse
Глава 23. Работа с Flash. Библиотека ming
Установка
Установка под Windows
Установка под UNIX-системы
Классы объектов библиотеки ming
Объекты SWFMovie
Объекты SWFShape
Объекты SWF Display hem
Объект SWFGradient
Объект SWF Bitmap
777778898888888888888888777778898888888888888888991112233333388999999999998888800888888s991112233333388999999999998888800888888s4 4 4 4 49 9 9 9 98 8 8 8 84 4 4 4 49 9 9 9 98 8 8 8 85666677799999 9 99888888885666677799999 9 99888888888988988 8 99 9 98 8 88 8 99 9 98 8 8о и тэ 5 Q^ 9 оо о о о о о 19 9 9 9 9 9 9о и тэ 5 Q^ 9 оо о о о о о 19 9 9 9 9 9 9Объект SWFFiH
77777889
88888888
88888888
77777889
88888888
88888888
9911122333333
8899999999999
8888800888888s
9911122333333
8899999999999
8888800888888s
4 4 4 4 4
9 9 9 9 9
8 8 8 8 8
4 4 4 4 4
9 9 9 9 9
8 8 8 8 8
56666777
99999 9 9988888888
56666777
99999 9 9988888888
8
9
8
8
9
8
8 8 9
9 9 9
8 8 8
8 8 9
9 9 9
8 8 8
о и тэ 5 Q^ 9 оо о о о о о 1
9 9 9 9 9 9 9
о и тэ 5 Q^ 9 оо о о о о о 1
9 9 9 9 9 9 9
Объект SWFMorph
Объект SWFText
Объект SWFFont
Объект SWFTextField
L Объект SWFSprite
Объект SWF Button
Объект SWFAction
Примеры кода
Градиентная заливка
Работа с изображениями. Объект SWFBitmap....
Работа с объектом SWFMorph
Работа с командами action script SWF Action и объектом SWF Button
Глава 24. Работа с PDF-документами. Библиагека ClibPDF
Установка
Установка под Windows
Установка под UNIX-системы
Создание простого PDF-документа
Функция cpdfjopen.....
Функция cpdf_save_tojWe
Функция cpdfj)agejmt
Функция cpdfjiddjDiitline
Функция cpdf_begin_text……
Функция cpdf__end_text
Функция cpdf_set_Jbnt
Функция cpdfjext
Функция cpdfjsetjextjrendering
Функция cpdfjnoveto
Функция cpdfjineto
Функция cpdf_stroke
Функция cpdf_ finalize
Функция cpdf_putput_buffer
Функция cpdfjclose
Практический пример
Дополнительные функции библиотеки ClibPDF
Функция cpdf_add_annotation
Функция cpdfjarc
Функция cpdf_circle
Функция cpdf_clip
Функция cpdf_closepath
Функция cpdfjclosepath_Jill_stroke
Функция cpdf_closepath_stroke
Функция cpdf_continuejext
Функция cpdf_curveto
2124252525262626262728282829292930)30加欣?30?30?33)33?33«4?34欢334334935935935 99999999999 9 9 9999999 9 99999999 9 99.99SQ9S59.9.9SS2124252525262626262728282829292930)30加欣?30?30?33)33?33«4?34欢334334935935935 99999999999 9 9 9999999 9 99999999 9 99.99SQ9S59.9.9SSФункция cpdf Jill
2124252525262626262728282829292930)30加欣?30?30?33)33?33«4?34欢334334935935935 99999999999 9 9 9999999 9 99999999 9 99.99SQ9S59.9.9SS
2124252525262626262728282829292930)30加欣?30?30?33)33?33«4?34欢334334935935935 99999999999 9 9 9999999 9 99999999 9 99.99SQ9S59.9.9SS
243IL Функция cpdfjlnalize_page
Функция cpdf_global__set_dociimentjimits
Функция cpdfjmportJpeg
Функция cpdf_placeJn!inejHiage
Функция cpdfjiewpaih
Функция cpdf_rect
Функция cpdfjsave
Функция cpdf_restore
Функция cpdfjvtate
Функция cpdf_rotatejext
24321. Функция cpdf_scale
Функция cpdf_set_qction_url
Функция cpdf_set_char_spacing
Функция cpdf_set_creator
Функция cpdf_setjcurrent_page
Функция cpdf^setjwriz^scaling
Функция cpdf_jet__keywords
Функция cpdfft leading
Функция cpdf^set_page_animation
Функция cpdf__set_subject
Функция cpdf_setjext_matrix
Функция cpdf_setjext__pos
Функция cpdf_setjext_rise
Функция cpdf_setjitle
Функция cpdf_set_word_spacing
Функция cpdf_setdash
24 3 37. Функция cpdf_setrgbcolor
Функция cpdf_setflat
Функция cpdf__show_xy
Функция cpdf_stringwidth
Функция cpdfjranslate
55666777788888899990000001" — — 113333333333333 3 33333444444444444999999999999999999999999999999955666777788888899990000001" — — 113333333333333 3 333334444444444449999999999999999999999999999999Приложение. Описание компакт-диска943
55666777788888899990000001" — — 113333333333333 3 333334444444444449999999999999999999999999999999
55666777788888899990000001" — — 113333333333333 3 333334444444444449999999999999999999999999999999
Предметный указатель944
Введение
Для кого и о чем эта книга
Совсем недавно, в мае 2004 г., в издательстве "БХВ-Петербург*' вышла наша первая книга "Самоучитель PHP 5* в которой мы постарались познакомить читателей с основами языка PHP 5. С того момента прошло несколько месяцев, но несмотря на это мы получили достаточно много положительных отзывов, в которых читатели хвалили нашу книгу, главным образом, за понятное изложение м немалый круг рассматриваемых вопросов. Книга, которую вы сейчас держите в руках, яаттяется логическим продолжением первой книги. Это совершенно не означает, что для того, чтобы работать с материалом этой книги, нужно обязательно прочитать предыдущую. Вовсе нет, хотя читателям, только начинающим свое знакомство с PHP, это, безусловно, не помешает. Если первая книга была ориентирована на читателя, не знакомого с PHP, то эта книга предполагает владение основами PHP, поскольку в ней описаны профессиональные приемы программирования на языке PHP 5 и создание Web-приложений различной степени сложности на его основе.
Таким образом, предлагаемая книга ориентирована на читателей» владеющих языком разметки гипертекста HTML, элементарными основами языка PHP и желающих повысить свой професс и онал ьн ы й уровень в разработке динамических Web-приложений с использованием PHP 5. Основное внимание уделяется разработке максимально возможного количества высокоэффективных Web-приложений, в которых может возникнуть потребность у разработчиков крупных сайтов и порталов. Разрабатываемые в книге Web- приложения проектируются м создаются таким образом, чтобы свести к минимуму усилия персонала компаний по их поддержке, для чего асе пршю- жения снабжаются удобной и быстрой системой администрирования. Все приложения, приводимые в книге, взяты из реальной практики Web- программирования и находятся в эксплуатации на различных Web-сайтах и крупных порталах. Максимально серьезно в книге расематриваются проблемы защиты создаваемых приложений, а также вопросы, связанные с профессиональной работой с Web-сервером Apache и СУБД MySQL Отдельная часть книги посвящена работе с расширениями PHP — созданием графики на PHP, работой с Flash п с PDF-докумегггами.
К книге прилагается CD, содержащий исходные коды Web-приложений, дистрибутив Web-интерфейса к MySQL phpMyAdniin, а также дистрибутивы
Web-сервера Apache, СУБД MySQL и PHP 5.0.0. Мы не покривим душой, если скажем, что на этом диске находится полнофункциональный Web-сайт, который можно сразу же запускать в действие.
На Web-сайте 1Т-ступмн SoftTinie, сотрудниками которой являются авторы книги, работает форум (http://ww.softtime.ru/fonim/), посвященный программированию на PHP. На этот форум авторы с удовольствием приглашав ют читателей обсуждать вопросы не только по материалу данной книги, но и любые вопросы в области Web-nротраммирования-
Как построена книга
Книга содержит Введение, 24 главы, Заключение и Приложение, в котором описан компакт-диск. Материал книги разбит на 5 частей.
Первая часть — вводная и содержит пять глав, в которых обсуждаются общие вопросьЕ разработки Web-приложений с использованием PHP 5.
Глава 1 посвящена установке PHP 5, Web-сервера Apache и СУБД MySQL. Здесь же рассматриваются вопросы конфигурирования Apache и PHP (настройка конфигурационного файла php.ini).
В главе 2 рассматриваются вопросы работы с СУБД MySQL: установка и настройка MySQL, оптимизация SQL-запросов, оптимизация кода PHP при работе с MySQL, а также установка, настройка и работа с Web-интерфейсом к MySQL — phpMyAdmin.
Главы 3 посвящена методам работы с НТТР-заголовкамн.
В главе 4 рассматриваются различные "тонкие вопросы" программирования на PHP. По сута, эта глава представляет собой просто небольшой сборник рецептов на тему программирования на PHP.
И завершает первую часть книги глава 5, посвященная вопросам безопасности создаваемых приложений.
Во второй части книги проводится разработка полнофункциональной системы администрирования Web-сайта, включающей как непосредственно систему администрирования контента сайта, так и основные программные блоки: систему голосования, гостевую книгу, систему новостей, систему отправки сообщений с сайта, фотогалерею, каталог продукции, систему полнотекстового поиска и систему сбора и анализа статистики по посетителям сайта (Исчетчик посетителей").
Как и положено, началу разработки системы администрирования предшест* вует ее проектирование, которое проведено в главе 6.
Глава 7 посвящена разработке основного блока системы администрирования: системе администрирования контента сайта.
В главе 8 проводится разработка универсального каталога продукции.
В главе 9 проводится разработка системы полнотекстового поиска по сайту. Разработке динамически изменяемого блока мКонтакты1' посвящена глава 10.
В главе 11 проводится разработка блоков *Толосованием и 'Тостевая книга".
Глава 12 посвящена разработке фотогалереи.
В главе 13 рассматриваются вопросы» связанные с функционированием электронной почты, и создается система отправки сообщений с сайта, поддерживающая отправку как простых сообщений, так н сообщений, содержащих вложения.
В главе 14 разрабатывается полнофункциональная система сбора и анализа статистики по посетителям сайта.
Третья часть книги посвящена разработке дополнительных Web-служб.
Глава 15 посвящена вопросам разработки системы мониторинга позиции сайта в поисковых системах по выбранным поисковым запросам.
В главе 16 проводится разработка FTP-менеджера.
В главе 17 рассматриваются основные вопросы создания почтового приложения на основе Web-интерфейса..
В главе 18 разрабатывается система рассылки.
Четвертая часть этой книги, содержащая главы 19——21, полностью посвящена разработке профессионального форума с развитой системой администрирования.
И, наконец, пятая, заключительная, часть книги посвящена вопросам работы с некоторыми библиотеками расширений PHP.
В главе 22 рассмотрены вопросы работы с графикой на PHP 5 при помощи библиотеки GD.
Работа с Flash (библиотека Ming) рассматривается в главе 23.
Глава 24 посвящена методам работы с PDF-документами с помощью библиотеки ClibPDF.
Благодарности
Авторы выражают признательность сотрудникам издательства "ВХВ-Петер- бург*1, благодаря которым наша рукопись увидела свет.
Авторы также благодарны всем читателям, приславшим отзывы на нашу первую книгу '^Самоучитель PHP 5* вышедшую в издательстве "БХВ-Петер^ бург*' в мае 2004 г. Ваше доброжелательное отношение и полезные замечания существенно помогли нам в работе над этой книгой.
Замечания и пожелания просим отправлять авторам по электронной почте kuznetsov@softtime.ru, а также высказывать в гостевой книге по адресу http://www.softtime.ru/. Если при чтении книги у вас возникнут вопросы по изложенному материалу, вы можете обращаться на наш форум, который располагается по адресу http://ww.softtime.ru/forurn/.