Книга
Книга
СОЗДАЕМ ДИНАМИЧЕСКИЕ
ВЕБ-САЙТЫ
С помощью
PHP, MySQL
JavaScriptЯ
Робин НиксонРобин Никсон^ ПИТЕР^ ПИТЕР
Робин Никсон
Робин Никсон
^ ПИТЕР
^ ПИТЕР
SECOND EDITION
Learning PHP, MySQL, JavaScript,and CSS
Robin Nixon
O’REILLY"
Beijing • Cambridge • Farnham • Koln • Sebastopol • Tokyo
ВТОРОЕ ИЗДАНИЕ
СОЗДАЕМ ДИНАМИЧЕСКИЕВЕБ-САЙТЫ С ПОМОЩЬЮРНР, MySQL
JavaScriptи CSS
Робин Никсон
С^ППТЕР
Москва - Санкт-Петербург - Нижний Новгород - ВоронежРостов-на-Дону •Екатеринбург - Самара - НовосибирскКиев - Харьков - Минск
2013
Никсон Р.
Создаем динамические веб-сайты с помощьюРНР, MySQL, JavaScript и CSS2-е издание
Серия «Бестселлеры O’Reilly»
Перевел с английского Н. Вильчинский
Заведующий редакцией
Д. IВиницкий
Ведущий редактор
LЕ. Каляева
Научный редактор
Н. Вильчинский
Литературный редактор
LЕ. Каляева
Художник
Л. Адуевская
Корректоры
LЕ. Павлович
Верстка
А.IБарцевич
ББК 32.988.02-018 УДК 004.738.5
Никсон Р.
Н64 Создаем динамические веб-сайты с помощью РНР, MySQL, JavaScript и CSS. 2-е изд. — СПб.: Питер, 2013. — 560 с.: ил. — (Серия «Бестселлеры O’Reilly»).
ISBN 978-5-496-00187-8
Научитесь создавать современные динамические веб-сайты, даже если у вас нет опыта в программировании! Если вы умеете писать статические сайты на HTML, то с помощью этого руководства вы освоите динамическое веб-программирование и изучите современные технологии с открытым кодом: РНР, MySQL, JavaScript и CSS.
В данном руководстве каждая технология рассматривается отдельно и показывается, как их объединить в одно целое, дается представление о самых современных концепциях веб-программирования. С помощью подробно разобранных примеров и контрольных вопросов, приводимых в каждой главе, вы сможете закрепить изученный материал на практике.
12+ (В соответствии с Федеральным законом от 29 декабря 2010 г. № 436-ФЭ.)
ISBN 978-1449319267 англ.Copyright © 20122RobinпNixon. AllI rights reserved.
Authorized Russian translation of the English edition of Learning PHP, MySQL, JavaScript, and CSS, Second Edition (ISBN 9781449319267). This translation is published and sold by permission of O’Reilly Media, Inc., which owns or controls all rights to publish and sell the same.
ISBN 978-5-496-00187-8© Перевод на1русский1языккОООDИздательствоз«Питер», 20133
© Издание на русском языке, оформление ООО Издательство «Питер», 2013
Права на издание получены по соглашению с O’Reilly. Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.
Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством как надежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не может гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за возможные ошибки, связанные с использованием книги.
ООО «Питер Пресс», 192102, Санкт-Петербург, ул. Андреевская (д. Волкова), 3, литер А, пом. 7Н.
Налоговая льгота — общероссийский классификатор продукции ОК 005-93, том 2; 95 3005 — литература учебная.
Подписано в печать 04.04.13. Формат 70x100/16. Уел. п. л. 45,150. Тираж 2000. Заказ№ 296.
Отпечатано в полном соответствии с качеством предоставленных издательством материалов
в ГППО «Псковская областная типография». 180004, Псков, ул. Ротная, 34.
Краткое содержание
Предисловие21
От издательства25
Глава 1. Введение в динамическое содержимое веб-страницы... 26
Глава 2. Установка сервера, предназначенного для разработки 39
Глава 3. Введение в РНР62
Глава 4. Выражения и управление процессом выполнения программы в РНР 90
Глава 5. Функции и объекты РНР 1188
Глава 6. Массивы в РНР 1455
Глава 7. Практикум по программированию на РНР 160О
Глава 8. Введение в MySQL1888
Глава 9. Освоение MySQL2303
Глава 10. Доступ к MySQL с использованием РНР 2544
Глава 11. Обработка форм 2811
Глава 12. Cookie, сессии и аутентификация2983
Глава 13. Изучение JavaScript 3188
Глава 14. Выражения и управление процессом выполнения сценариев в JavaScript 3383
Глава 15. Функции, объекты и массивы JavaScript3565
Глава 16. Проверка данных и обработка ошибок в JavaScript и РНР 3744
Глава 17. Использование технологии Ajax3977
Глава 18. Введение в CSS4133
Глава 19. Расширение CSS с помощью CSS34522
Глава 20. Доступ к CSS из JavaScript4777
Глава 21. Объединение технологий4965
Приложение А. Ответы на контрольные вопросы5277
Приложение Б. Интернет-ресурсы 5433
Приложение В. MySQL's FULLTEXT Stopwords5466
Приложение Г. Функции MySQL549
Оглавление
П редисловие21
Для кого предназначена эта книга 21
Предположения, допущенные в даннойкниге 21
Как устроена кн ’ ига22
Дополнительная литература 22
Соглашения, использованные в данной кн-иге 23
Использование примеров кода 24
Благодарности24
От издательства 25
Глава 1. Введение в динамическое содержимое веб-страницы... 26
HTTP и HTML: основы, заложенные Бернерсом-Ли27
Процедура «запрос — ответ» 27
Преимущества использования РНР, MySQL, JavaScript и CSS 29
Использование РНР311
Использование MySQL 322
Использование JavaScript333
Использование CSS344
Веб-сервер Apache 35
Несколько слов о программах с открытым исходным кодом 36
А теперь все это, вместе взятое 36
Проверьте ваши знания 38
Глава 2. Установка сервера, предназначенного для разработки39
Что такое WAMP, MAMP и LAM P 40
Установка WAMP на систему Windows'40О
Тестирование установки 47
Другие системы WAMP 49
Установка МАМР на систему Mac OS X 50
Настройка MySQL 53
Обеспечение запуска MySQL при загрузке системы54
Проверка установки 54
Установка LAMP на Linux 56
Работа в удаленном режиме 57
Вход в систему 57
Использование FTP57
Использование редактора программ 59
Использование IDE60
Проверьте ваши знания 61
Глава 3. Введение в РНF Р 62
Включение РНР в HTML 62
Вызов парсера РНР63
Примеры, приводимые в этой книге 64
Структура РНР65
Использование комментариев 65
Основной синтаксис66
Осмысление переменных67
Операторы 71
Присваивание значений переменным 74
Многострочные команды 77
Типы переменных 79
Константы 80
Различие между командами echo и print81
Функции82
Область видимости переменной83
Проверьте ваши знания 88
Глава 4. Выражения и управление процессом выполнения пр । ограммыi в РНР90
Выражения90
Литералы и переменные 92
Операторы 93
Приоритетность операторов933
Взаимосвязанность операторов 95
Операторы отношения96
Условия1000
Инструкция iIf 100 0
Инструкция eIlse 1022
Инструкция eIlseif 1033
Инструкция switch1044
Оператор ? 107 7
Организация циклов 1088
Циклы while1099
Циклы do...while 110О
Циклы foгrIll11
Прекращение работы цикла 1133
Инструкция continue 1144
Неявное и явное преобразование типов 1144
Динамическое связывание в РНР 1155
Динамическое связывание в действии 1166
Проверьте ваши знания 1177
Глава 5. Функции и объекты РН Р 1188
Функции РНР1199
Определение функции 120О
Возвращение значения 1211
Возвращение массива 1233
Передача по ссылке 1233
Возвращение глобальных переменных 1255
И еще раз об области видимости переменных 1255
Включение и запрос файлов 1266
Инструкция include1266
Использование инструкции indude_once 1266
Использование инструкций require и require_once1277
Совместимость версий РН3Р 127 7
Объекты РНР1288
Терминология 1299
Объявление класса 130О
Создание объекта1311
Доступ к объектам 131 1
Конструкторы 1344
Написание методов135 5
Объявление свойств 1366
Объявление констант 1377
Область видимости свойств и методов в РНР 5 1388
Наследование 140О
Проверьте ваши знания 1433
Глава 6. Массивы в РН Р 1455
Основные подходы к массивам 1455
Массивы с числовой индексацией 1455
Ассоциативные массивы1477
Присваивание, использующее ключевое слово array 1488
Цикл foreach...as 1499
Многомерные массивы15111
Использование функций для работы с массивами 1544
is_array1544
count1544
sort 1544
shuffle155 5
explode155 5
extract1566
compact 1577
reset 158 8
end 158 8
Проверьте ваши знания 15919
Глава 7. Практикум по программированию на РНF Р 160 0
Использование функции printf 1600
Настройка представления данных 1622
Дополнение строк 1633
Использование функции sprintf1655
Функции даты и времени 1655
Константы, связанные с датами 1677
Использование функции checkdate 1677
Работа с файлами 1688
Проверка существования файла 1688
Создание файла 1688
Чтение из файлов170 0
Копирование файлов 171 1
Перемещение файла 171 1
Удаление файла 1722
Обновление файлов 1722
Блокирование файлов при коллективном доступе173 3
Чтение всего файла целиком 1755
Загрузка файлов на веб-сервер175 5
Системные вызовы 18111
XHTML1822
Преимущества XHTML 1833
Версии XHITML 1833
Отличие XHTML от HTML1833
Типы документов HTML 4.01 1855
Типы документов HTML518515
Типы документов XHTML 1.0 1855
Проверка правильности XHTML1866
Проверьте ваши знания 1877
Глава 8. Введение в MySQL 1888
Основные характеристики MySQL 188 8
Сводка понятий, используемых в базах данных 1899
Доступ к MySQL из командной строки 189У
Начало работы с интерфейсом командной строки190О
Использование интерфейса командной строки 1933
Команды MySQL 1955
Типы данных 1999
Индексы2088
Создание индекса2099
Создание запросов к базе данных MySQL 2144
Объединение таблиц 2233
Использование логических операторов 225 5
Функции MySQL 226б
Работа с MySQL через phpMyAdmin 2266
Для пользователей Windows 2266
Для пользователей Mac OS X 2266
Для пользователей Linux2288
Использование phpMyAdmin228
Проверьте ваши знания 2288
Глава 9. Освоение MySQL230О
Проектирование базы данных 230О
Первичные ключи: ключи к реляционным базам данных231 1
Нормализация2322
Первая нормальная форма 2333
Вторая нормальная форма 2355
Третья нормальная форма 2388
Когда не следует проводить нормализацию240О
Отношения 241 1
«Один к одному» 241
«Один ко многим» 241
«Многие ко многим» 242
Базы данных и анонимность 244
Транзакции 244
Ядра (механизмы хранения) транзакций 245
Использование команды BEGIN246
Использование команды COMMIT246
Использование команды ROLLBACK246
Использование команды EXPLAIN247
Резервное копирование и восстановление данных248
Использование команды mysqldump249
Создание файла резервной копии250
Восстановление данных из файла резервной копии252
Выгрузка данных в файлы формата CSV . . . 252
Планирование резервного копирования253
Проверьте ваши знания253
Глава 10. Доступ к MySQL с использованием PHP254
Запросы к базе данных MySQL с помощью PHP254
Процесс254
Создание файла регистрации255
Подключение к MySQL 256
Практический пример261
Массив $_РО5Т264
Удаление записи264
Отображение формы265
Запросы к базе данных266
Запуск программы266
Практическая работа с MySQL267
Создание таблицы268
Описание таблицы268
Удаление таблицы269
Добавление данных270
Извлечение данных270
Обновление данных271
Удаление данных272
Использование свойства AUTO_INCREMENT272
Выполнение дополнительных запросов274
Предотвращение внедрения SQL-кода275
Предотвращение внедрения HTML-кода279
Проверьте ваши знания280
Глава 11. Обработка форм281
Создание форм281
Извлечение отправленных данных282
register_globals: склонность к использованию устаревших решений284
Значения по умолчанию284
Типы элементов ввода данных2866
Обезвреживание введенных данных2933
Пример программы2944
Проверьте ваши знания 2977
Глава 12. Cookie, сессии и аутентиф/и < кация 2988
Использование cookie в РНР2988
Установка cookie 300О
Доступ к cookie 3011
Удаление cookie301
HTTP-аутентификация 301
Сохранение имен пользователей и паролей 304
Добавление произвольных данных 305
Использование сессий 308
Начало сессии 309
Завершение сессии312
Безопасность сессии 313
Проверьте ваши знания317
Глава 13. Изучение JavaScript318
JavaScript и текст HTML 318
Использование сценариев в заголовке документа320
Устаревшие и нестандартные браузеры 320
Включение файлов JavaScript321
Отладка кода JavaScript322
Использование комментариев 324
Точка с запятой 324
Переменные 325
Строковые переменные 325
Числовые переменные 326
Массивы 326
Операторы 327
Арифметические операторы 327
Операторы присваивания 3288
Операторы сравнения 3288
Логические операторы 3299
Инкремент и декремент переменной3299
Объединение строк 329
Управляющие символы329
Типизация переменных 330
Функции 331
Глобальные переменные331
Локальные переменные3322
Объектная модель документа 333 3
Но не все так просто3355
Использование DOM3366
Проверьте ваши знания 3377
Глава 14. Выражения и управление процессом выполнения сценариев в JavaScript 3388
Выражения 3388
Литералы и переменные 3399
Оператор i ы 340 О
Приоритетность операторов 341 1
Взаимосвязанность341 1
Операторы отношения 3422
Инструкция with 3455
Использование события onerror3455
Использование конструкции try...catch3466
Условия3477
Инструкция iff348 8
Инструкция switch 3499
Оператор ? 350 О
Циклы 351 1
Циклы while3511
Циклы do...while 351 1
Циклы foгr 352 2
Прекращение работы цикла 3533
Инструкция continue 3533
Явное преобразование типов. . ; 3544
Проверьте ваши знания 3555
Глава 15. Функции, объекты I и массивыI JavaScript 356б
Функции JavaScript 3566
Определение функции 356
Возвращение значения 3588
Возвращение массива 360О
Объекты JavaScript 361 1
Объявление класса 3611
Создание объекта 3622
Доступ к объектам3633
Ключевое слово prototype3633
Массивы в JavaScript 3655
Числовые массивы3666
Ассоциативные массивы3677
Многомерные массивы 3688
Использование методов массивов 3699
Проверьте ваши знания 3733
Глава 16. Проверка данных и обработка ошибок в JavaScript и РН Р 3744
Проверка данных, введенных пользователем, средствами JavaScript 374
Документ validate.html (часть первая)3755
Документ validate.html (часть вторая)3777
Регулярные выражения 380О
Соответствие, закладываемое в метасимволы 380О
Нестрогое символьное соответствие 3811
Группировка с помощью скобок3822
Символьный класс3833
Более сложные примеры3844
Сводная таблица метасимволов3877
Общие модификаторы 3888
Использование регулярных выражений в JavaScript389 9
Использование регулярных выражений в РНР 3899
Повторное отображение формы после проверки данных РНР-программой 390О
Проверьте ваши знания 3955
Глава 17. Использование технологии Ajax3977
Что такое Ajax? 3988
Использование XMLHttpRequest3988
Реализация Ajax с помощью POST-запросов400О
Свойство readyState4022
Серверная половина Ajax-процессэ 4033
Использование GET вместо POST4055
Отправка XML-запросов 4077
Несколько слов о XML 4099
А зачем вообще использовать XML? 410О
Использование для Ajax специальной среды 411 1
Проверьте ваши знания 4122
Глава 18. Введение в CSS 413
Импортирование таблицы стилей4144
Встроенные настройки стиля 415
Использование идентификаторов (ГО)4155
Использование классов415
Правила CSS416
Использование точек с запятой416
Множественные задания стиля416
Использование комментариев417
Типы стилей418
Исходные стили418
Пользовательские стили418
Внешние таблицы стилей419
Внутренние стили419
Внедренные стили420
Селекторы CSS420
Селектор типа420
Селектор потомков420
Селектор дочерних элементов421
Селектор смежных элементов422
Селектор элементов, имеющих идентификатор423
Селектор класса424
Селектор атрибутов424
Универсальный селектор425
Групповая селекция426
Каскадность CSS426
Создатель таблиц стилей427
Методы создания таблиц стилей427
Селекторы таблиц стилей428
Разница между <div> и <span>430
Измерения431
Шрифты и оформление433
font-family433
font-style434
font-size434
font-weight435
Управление стилями текста435
Оформление436
Разрядка436
Выравнивание437
Преобразование437
Отступы437
Цвета CSS 438
Сокращенные цветовые строки438
Градиенты439
Позиционирование элементов 440о
Абсолютное позиционирование440о
Относительное позиционирование 441 1
Фиксированное позиционирование 441 1
Сравнение типов позиционирования 441 1
Псевдоклассы4433
Псевдоэлементы 4455
Сокращенная запись правил 445
Модель блока и макет страницы446б
Установка полей 4466
Применение границ4488
Настройка отступов4499
Содержимое объекта 4511
Проверьте ваши знания 451 1
Глава 19. Расширение CSS с помощью CSS3 4522
Селекторы атрибутов4522
Соответствующие части строк 4533
Свойство box-sizing4544
Создание фона в CSS34544
Свойство background-clip 4555
Свойство background-origin4555
Свойство background-size4577
Использование нескольких фонов4577
Границы CSS34599
Свойство border-color 4599
Свойство border-radius 460О
Прямоугольные тени4633
Выход элемента за пределы размеров4633
Разметка с использованием нескольких колонок 4644
Цвета и непрозрачность 4655
Цвета HSL 4655
Цвета HSLA 4666
Цвета RGB 4666
Цвета RGBA . . . .. 4677
Свойство opacity 4677
Эффекты, применяемые к тексту4688
Свойство text-shadow 4688
Свойство text-overflow 468
Свойство word-wrap4699
Веб-шрифты......469
Веб-шрифты Google470
Трансформации470
Переходы472
Свойства, применяемые к переходам473
Продолжительность перехода473
Задержка перехода473
Задание скорости перехода473
Сокращенный синтаксис474
Проверьте ваши знания476
Глава 20. Доступ к CSS из JavaScript477
Еще одно обращение к функции getElementByld477
Функция О477
Функция S478
Функция С479
Включение функций480
Обращение к свойствам CSS из JavaScript481
Некоторые общие свойства482
Другие свойства482
Встроенный JavaScript485
Ключевое слово this485
Привязка событий к объектам в сценарии486
Прикрепление к другим событиям486
Добавление новых элементов487
Удаление элементов489
Альтернативы добавлению и удалению элементов489
Использование прерываний490
Использование функции setTimeout490
Отмена тайм-аута491
Использование функции setinterval491
Использование прерываний для анимации493
Проверьте ваши знания495
Глава 21. Объединение технологий496
Проектирование сайта социальной сети496
Информация на веб-сайте497
Файл functions.php497
Функции497
Файл header.php 499
Файл setup.php501
Файл index.php502
Файл signup.php5033
Проверка возможности использования желаемого имени пользователя 5033
Файл checkuser.php506б
Файл login.php5077
Файл profile.php5099
Добавление текста в поле About Me (Обо мне) 510О
Добавление изображения профиля510О
Обработка изображения 511 1
Отображение текущего профиля 511 1
Файл members.php 5133
Просмотр профилей пользователей5133
Добавление и удаление друзей5133
Вывод списка всех участников5144
Файл friends.php5177
Файл messages, php520О
Файл logoutphp5233
Файл styles.css 5244
Приложение А.Ответыhна Кконтрольныевопросы5277
Приложение Б.1Интернет-ресурсы5433
Приложение В.MySQL'sFFULLTEXT5Stopwords 5466
Приложение Г.<3Функции MySQL 54
Юлии
Предисловие
Сочетание РНР и MySQL является одним из самых удобных подходов к динамическому веб-конструированию, основанному на использовании базы данных. Этот подход удерживает свои позиции перед лицом вызовов, брошенных интегрированными средами разработки, такими как Ruby on Rails, освоение работы с которыми дается значительно труднее. Благодаря открытости исходных кодов (в отличие от конкурирующей технологии Microsoft .NET framework) это технологическое сочетание можно использовать совершенно бесплатно, и поэтому оно приобрело очень большую популярность у веб-разработчиков.
Любой претендующий на результативность разработчик, использующий платформу Unix/Linux или даже Windows/Apache, нуждается в серьезном освоении этих технологий. В то же время большую важность приобретает изучение языка JavaScript, поскольку он обеспечивает браузерную динамическую функциональность, а также посредством Ajax скрытую связь с веб-сервером, позволяющую создавать плавно меняющиеся интерфейсы.
Для кого предназначена эта книга
Эта книга предназначена для тех, кто хочет изучить способы создания эффективных и динамичных веб-сайтов. К их числу можно отнести веб-мастеров или специалистов по графическому дизайну, которым уже приходилось создавать статические веб-сайты и у которых есть желание вывести свое мастерство на следующий уровень, а также студентов вузов и колледжей, недавних выпускников этих учебных заведений и просто самоучек.
Фактически любой человек, стремящийся изучить основные принципы, заложенные в основу технологии Web 2.0, известной как Ajax, сможет получить весьма обстоятельные сведения обо всех трех основных технологиях: РНР, MySQL, JavaScript и CSS.
Предположения, допущенные в данной книге
Материал данной книги изложен в предположении, что читатель уже имеет элементарные понятия об HTML и способен как минимум скомпоновать простой
статический веб-сайт. Но при этом не предполагается наличие у читателя каких- либо знаний в области РНР, MySQL, JavaScript и CSS. Тем не менее, если такие знания имеются, изучение книги будет происходить значительно быстрее.
Как устроена книга
Главы книги расположены в определенном порядке. Сначала идет представление всех основных технологий, рассматриваемых в книге, а затем описывается процесс их установки на сервер, предназначенный для разработки веб-приложений, для того чтобы подготовить читателя к практической работе с примерами.
В следующей части книги преподносятся основы языка программирования РНР, охватывающие основы синтаксиса, массивов, функций и объектно-ориентированного программирования.
Затем, после усвоения основ РНР, можно переходить к введению в систему управления базами данных MySQL, рассмотрение которой начинается со структуры базы данных MySQL и заканчивается составлением сложных запросов.
После этого рассказывается о том, как воспользоваться сочетанием РНР и MySQL, чтобы приступить к созданию собственных динамических веб-страниц путем интегрирования в это сочетание форм и других функциональных возможностей HTML.
В следующих трех главах рассматриваются подробности практических аспектов разработки на РНР и MySQL, включая описание различных полезных функций и способов работы с cookie и сессиями, а также способов поддержания высокого уровня безопасности.
В следующих четырех главах излагаются основы JavaScript, начиная с простых функций и обработки событий и заканчивая доступом к объектной модели документа (DOM), проверкой введенных данных и обработкой ошибок в браузере.
После рассмотрения всех трех основных технологий излагаются способы создания фоновых Ajax-вызовов и превращения веб-сайтов в высокодинамичную среду.
И наконец, вам предстоит освоить еще две главы, изучая все. касающееся использования CSS для стилевого оформления и подбора формата ваших веб-страниц, а затем собрать ранее изученное в полноценный набор PHP-программ, в совокупности создающий работоспособный веб-сайт социальной сети.
По мере изложения материала дается большое количество указаний и советов по выработке хорошего стиля программирования, а также подсказок, которые помогут читателям обнаружить и устранить скрытые ошибки программирования. Также делается много ссылок на веб-сайты, содержащие дополнительные материалы, относящиеся к рассматриваемым темам.
Дополнительная литература
Приступив к изучению разработки программных продуктов с помощью РНР, MySQL и JavaScript, вы наверняка будете готовы к переводу своего мастерства на новый уровень, если используете следующие книги:
О «Dynamic HTML: The Definitive Reference» (http://oreil.ly/dynamic_html) Денни Гудмана (Danny Goodman), издательство O’Reilly;
О «РНР in a Nutshell» (http://oreil.ly/PHP_nutshell) Пола Хадсона (Paul Hudson), издательство O’Reilly;
О «MySQL in a Nutshell» (http://oreil.ly/MySQL_nutshell) Рассела Дайера (Russell Dyer), издательство O’Reilly;
О «JavaScript: The Definitive Guide» (http://oreil.ly/JS_Definitive) Дэвида Фланагана (David Flanagan), издательство O’Reilly;
О «CSS: The Definitive Guide» (http://oreil.ly/CSS_Definitive) Эрика А. Майера (Eric A. Myer), издательство O’Reilly
Соглашения, использованные в данной книге
Здесь приводится список соглашений, принятых в данной книге.
Шрифт для названий
Применяется для отображения URL, а также названий папок и выводимой на экран информации.
Шрифт для команд
Используется для имен файлов, названий путей, имен переменных и команд. Для примера, путь будет выглядеть так: / Developer/Appl ications.
Шрифт с постоянной шириной
Применяется для отображения примеров исходного кода и содержимого файлов.
Полужирный шрифт с фиксированной шириной
Обозначает текст, который должен быть введен пользователем дословно. Кроме того, данный шрифт иногда используется в примерах для создания логического ударения, например, чтобы выделить важную строку кода в большом примере.
Курсивный шрифт с фиксированной шириной
Обозначает код, который должен быть заменен подходящим значением (например, имя_пользователя).
Вам следует обращать особое внимание на специальные врезки, выделенные с помощью следующих рисунков.
Это подсказка, пожелание, заметка общего типа. Содержит полезную прикладную информацию по рассматриваемой теме.
Это предостережение или указание, говорящее о том, что вам необходимо быть внимательным.
Использование примеров кода
Эта книга предназначена для оказания помощи в выполнении поставленных перед вами задач. Вы можете использовать код, приведенный в ней, в своих программах и документации. Вам не нужно обращаться к нам за разрешением до тех пор, пока вы не станете копировать значительную часть кода. Например, использование при написании программы нескольких фрагментов кода, взятых из данной книги, не требует специального разрешения. А вот продажа и распространение компактдиска с примерами из книг издательства O’Reilly — требует. Ответы на вопросы, в которых упоминаются материалы этой книги, и цитирование приведенных в ней примеров не требуют разрешения. А включение существенного объема примеров кода, приводимых в данной книге, в документацию по вашему собственному продукту требует получения разрешения.
Ссылки на источник приветствуются, но не обязательны. В такие ссылки обычно включаются название книги, имя ее автора, название издательства и номер ISBN. Например: «Learning РНР, MySQL, JavaScript and CSS», второе издание, автор Робин Никсон (Robin Nixon). Copyright 2012 Robin Nixon, 978-1-4493-1926-7».
При любых сомнениях относительно превышения разрешенного объема использования примеров кода, приведенных в данной книге, можете свободно обращаться к нам по адресу permissions@oreilly.com.
Благодарности
Огромное спасибо моему редактору Энди Ораму, у которого всегда находились дельные мысли о том, как лучше объяснить сложные вопросы, Рэйчел Хэд за отличную работу по приведению в порядок моей рукописи, Айрис Фебрес и Рэйчел Стили, которые тщательно курировали создание книги, Роберту Романо за прекрасные иллюстрации в обоих изданиях, Карен Монтгомери за великолепного летающего сахарного поссума на обложке книги, Дэвида Футато за понятный и легко читаемый внутренний дизайн, и всем другим преданным своему делу работникам издательства O’Reilly, упорно трудившимся над этой книгой. Без них она никогда не была бы написана. Хотелось бы также поблагодарить моих технических рецензентов, работавших над первым изданием, Дерека Дехарта, Кристофера Дорна, Томислава Ду- ганджика, Веска Моргана, Гарри Никсона, Алана Солиса и Демиана Тернера, а также Альберта Виаша за его неоценимый вклад и советы по новому для этого издания разделу, посвященному CSS. И наконец, спасибо читателям первого издания книги, заметившим опечатки, всем, кто помог появиться этому исправленному, обновленному и улучшенному руководству по веб-разработке.
От издательства
Ваши замечания, предложения и вопросы отправляйте по адресу электронной почты vinitski@minsk.piter.com (издательство «Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
На сайте издательства http://www.piter.com вы найдете подробную информацию о наших книгах.
1
Введение
в динамическое содержимое веб-страницы
Всемирная паутина — это непрерывно развивающаяся сеть, ушедшая далеко вперед от своей концепции ранних 1990-х, когда ее создание было обусловлено решением вполне конкретных задач. Высокотехнологичные эксперименты в ЦЕРНе (Европейском центре физики высоких энергий, известном в наши дни в качестве обладателя большого адронного коллайдера) выдавали невероятно большой объем данных, который был слишком велик для распространения среди участвующих в экспериментах ученых, разбросанных по всему миру.
К тому времени Интернет уже существовал и к нему было подключено несколько сотен тысяч компьютеров, поэтому Тим Бернерс-Ли (Tim Berners-Lee) (специалист ЦЕРНа) придумал способ навигации между ними с использованием среды гиперссылок — так называемого протокола передачи гиперссылок (Hyper Text Transfer Protocol (HTTP)). Он также создал специальный язык разметки, названный языком гипертекстовой разметки (Hyper Text Markup Language (HTML)). Для того чтобы собрать все это воедино, он создал первые веб-браузер и веб-сервер.
Теперь мы воспринимаем эти инструменты как должное, но в то время эта концепция носила революционный характер. До этого основной объем соединений приходился на пользователей домашних модемов, дозванивавшихся и подключавшихся к электронным доскам объявлений, которые базировались на отдельном компьютере и позволяли общаться и обмениваться данными только с другими пользователями этой службы. Следовательно, для эффективного электронного общения с коллегами и друзьями нужно было становиться участником многих электронных досок объявлений (bulletin board systems).
Однако Бернерс-Ли изменил все это одним махом, и к середине 1990-х годов уже существовали три основных конкурирующих друг с другом графических веб-браузера, пользовавшихся вниманием 5 млн пользователей. Но вскоре стало очевидно, что кое-что было упущено. Конечно, текстовые и графические страницы, имеющие гиперссылки для перехода на другие страницы, были блестящей концепцией, но результаты не отражали текущий потенциал компьютеров и Интернета по удовлетворению насущных потребностей пользователей в динамическом изменении
контекста. Всемирная паутина оставляла весьма невыразительное впечатление, даже при использовании прокрутки текста и анимированных GIF-картинок.
Корзины покупателей, поисковые машины и социальные сети внесли существенные коррективы в порядок использования Всемирной паутины. В этой главе будет дан краткий обзор различных компонентов, формирующих ее облик, и программного обеспечения, способствующего обогащению и оживлению наших впечатлений от ее использования.
Пришло время использовать ряд акронимов. Прежде чем делать это, я постарался дать им mF а. четкое объяснение. Но, если сразу не удастся понять, какое именно понятие они замещают ——1^. или что означают, переживать не стоит, поскольку все подробности прояснятся по мере чтения материала.
HTTP и HTML: основы, заложенные Бернерсом-Ли
HTTP является стандартом взаимодействия, регулирующим порядок направления запросов и получения ответов — процесса, происходящего между браузером, запущенным на компьютере конечного пользователя, и веб-сервером. Задача сервера состоит в том, чтобы принять запрос от клиента и попытаться дать на него содержательный ответ, обычно передавая ему запрошенную веб-страницу. Именно поэтому и используется термин сервер (обслуживающий). Партнером, взаимодействующим с сервером, является клиент, поэтому данное понятие применяется как к веб-браузеру, так и к компьютеру, на котором он работает.
Между клиентом и сервером может располагаться ряд других устройств, например маршрутизаторы, модули доступа, шлюзы и т. д. Они выполняют различные задачи по обеспечению безошибочного перемещения запросов и ответов между клиентом и сервером. Как правило, для отправки этой информации ими используется Интернет.
Обычно веб-сервер может обрабатывать сразу несколько подключений, а при отсутствии связи с клиентом находится в режиме ожидания входящего запроса на подключение. При поступлении подобного запроса сервер подтверждает его получение отправкой ответа.
Процедура «запрос — ответ»
В наиболее общем виде процесс «запрос — ответ» состоит из просьбы веб-браузера к веб-серверу отправить ему веб-страницу и выполнения веб-браузером этой просьбы. После этого браузер занимается отображением страницы (рис. 1.1).
При этом соблюдается данная пошаговая последовательность.
Вы вводите в адресную строку браузера http://server.com.
Ваш браузер ищет IP-адрес, соответствующий доменному имени server.com.
Браузер посылает запрос на главную страницу server.com.
Запрос проходит по сети Интернет и поступает на веб-сервер server.com.
Веб-сервер, получивший запрос, ищет веб-страницу на своем жестком диске.
Сервер извлекает веб-страницу и отправляет ее по обратному маршруту в адрес браузера.
Браузер отображает веб-страницу.
Веб-серверна server.comВеб-серверна server.comЖесткий диск на server.comЖесткий диск на server.comВеб-браузерВеб-браузерИнтернетИнтернет
Веб-серверна server.com
Веб-серверна server.com
Жесткий диск на server.com
Жесткий диск на server.com
Веб-браузер
Веб-браузер
Интернет
Интернет
Получение запросана страницу index :
-Извлечениеmdex.html ,с жесткого диска
Возвращениебфаницы index
Полученийи отображениестраницы
Рис. 1.1. Основная последовательность процесса «запрос — ответ» между клиентом и сервером
При передаче типовой веб-страницы этот процесс осуществляется для каждого имеющегося на ней объекта: элемента графики, встроенного видео- или Flash-ролика и даже шаблона CSS.
Обратите внимание на то, что на шаге 2 браузер ищет IP-адрес, принадлежащий доменному имени server.com. У каждой машины, подключенной к Интернету, включая и ваш компьютер, есть свой IP-адрес. Но, как правило, доступ к веб-серверам осуществляется по именам, таким как google.com. Вам, должно быть, известно, что браузер обращается к вспомогательной интернет-службе, так называемой службе доменных имен (Domain Name Service (DNS)), для того чтобы найти связанный с сервером IP-адрес, а затем воспользоваться им для связи с компьютером.
При передаче динамических веб-страниц процедура состоит из несколько большего количества действий, поскольку к ней могут привлекаться как РНР, так и MySQL (рис. 1.2).
Вы вводите в адресную строку браузера http://server.com.
Ваш браузер ищет IP-адрес, соответствующий доменному имени server.com.
Браузер посылает запрос на главную страницу server.com.
Запрос проходит по сети Интернет и поступает на веб-сервер server.com.
Веб-сервер, получивший запрос, ищет веб-страницу на своем жестком диске.
Теперь, когда главная страница размещена в его памяти, веб-сервер замечает, что она представлена файлом, включающим в себя PHP-сценарии, и передает страницу интерпретатору РНР.
Интерпретатор РНР выполняет РНР-код.
Некоторые фрагменты кода РНР содержат MySQL-инструкции, которые интерпретатор РНР, в свою очередь, передает процессору базы данных MySQL.
База данных MySQL возвращает результаты выполнения инструкции интерпретатору РНР.
Интерпретатор РНР возвращает веб-серверу результаты выполнения кода РНР, а также результаты, полученные от базы данных MySQL.
Веб-сервер возвращает страницу выдавшему запрос клиенту, который отображает эту страницу на экране.
Конечно, ознакомиться с этим процессом и узнать о совместной работе трех элементов не помешает, но на практике эти подробности не понадобятся, поскольку все происходит в автоматическом режиме.
В каждом из примеров возвращенные браузеру HTML-страницы могут содержать также код JavaScript, интерпретируемый локально на машине клиента. Этот код может инициировать еще один запрос, точно так же запрос может быть инициирован встроенными объектами, например изображениями.
Преимущества использования РНР, MySQL, JavaScript и CSS
В начале этой главы был представлен мир технологии Web 1.0, но рывок к созданию технологии Web 1.1, вместе с которой были разработаны такие браузерные расширения, как Java, JavaScript, JScript (несколько иной вариант JavaScript от корпорации Microsoft) и ActiveX, не заставил себя долго ждать. На серверной стороне прогресс был обеспечен за счет общего шлюзового интерфейса (Common Gateway Interface (CGI)), использования таких языков сценариев, как Perl (альтернатива языку РНР), и выполнения сценариев на стороне сервера (динамическая вставка содержимого одного файла или выходных данных системного вызова в другой файл).
1
2
3
4
5
6
7
8
9
10
11
Веб-бркау1 зерк Инте^рнет Веб-серКверКВеб-бркау1 зерк Инте^рнет Веб-серКверКПроцессор Жесткий База данных рНрдиск MySQLПроцессор Жесткий База данных рНрдиск MySQLРис. 1.2. Динамическая последовательность процесса «запрос — ответ», выполняемого клиентом и серверомРис. 1.2. Динамическая последовательность процесса «запрос — ответ», выполняемого клиентом и серверомКогда ситуация окончательно прояснилась, на передовых позициях остались три основные технологии. Несмотря на то что язык сценариев Perl силами своих стойких приверженцев сохранил популярность, простота РНР и допустимость использования в нем встроенных ссылок на программу базы данных MySQL обеспечили этому языку более чем двойное превосходство по количеству пользователей. A JavaScript, ставший важнейшей составной частью уравнения, используемого для динамического манипулирования каскадными таблицами стилей (Cascading Style Sheets (CSS)), в настоящее время берет на себя наиболее трудоемкие задачи осуществления Ajax-процесса на стороне клиента. Благодаря технологии Ajax (которая рассматривается в разделе «Использование JavaScript») веб-страницы обрабатывают данные и отправляют запросы веб-серверу в фоновом режиме, не оповещая пользователя о происходящем.
Веб-бркау1 зерк Инте^рнет Веб-серКверК
Веб-бркау1 зерк Инте^рнет Веб-серКверК
Процессор Жесткий База данных рНрдиск MySQL
Процессор Жесткий База данных рНрдиск MySQL
Рис. 1.2. Динамическая последовательность процесса «запрос — ответ», выполняемого клиентом и сервером
Рис. 1.2. Динамическая последовательность процесса «запрос — ответ», выполняемого клиентом и сервером
Несомненно, своеобразный симбиоз РНР и MySQL способствует их продвижению, но что привлекает к ним разработчиков в первую очередь? На это следует дать простой
ответ: та легкость, с которой эти технологии можно использовать для быстрого создания на веб-сайтах динамических элементов. MySQL является быстродействующей и мощной, но при этом простой в использовании системой базы данных, предлагающей веб-сайту практически все необходимое для поиска и обработки данных, предназначаемых для браузеров. Когда РНР для хранения и извлечения этих данных выступает в союзе с MySQL, вы получаете основные составляющие, необходимые для разработки сайтов социальных сетей и для перехода к технологии Web 2.0.
И когда вы также соедините вместе JavaScript и CSS, у вас появится рецепт для создания высокодинамичных и интерактивных веб-сайтов.
Использование РНР
Использование РНР существенно упрощает встраивание средств, придающих вебстраницам динамические свойства. Когда страницам присваивается расширение РНР, у них появляется прямой доступ к языку сценариев. Разработчику нужно лишь написать код, похожий на этот:
<?php
echo "Hello World. Today is " .date('T') .". ": ?>
How are you?
Открывающий тег <?php дает веб-серверу разрешение на интерпретацию всего последующего кода вплоть до команды ?>. Все, что находится за пределами этой конструкции, отправляется клиенту в виде простого HTML. Поэтому текст «How are you?» просто выводится в браузер. А внутри PHP-тегов встроенная функция date отображает текущий день недели, соответствующий системному времени сервера.
В итоге на выходе их этих двух частей получается примерно следующее:
Hello World. Today is Wednesday. How are you?
PHP —довольно гибкий язык, и некоторые разработчики предпочитают помещать PHP-конструкцию непосредственно рядом с кодом РНР, как в этом примере:
Hello World. Today is <?php echo dateC'l"): ?>. How are you?
Существуют также другие способы форматирования и вывода информации, которые будут рассмотрены в главе, посвященной РНР. Важно усвоить то, что, используя РНР, веб-разработчики получают язык сценариев, который хотя и не обладает быстротой кода, скомпилированного на С или ему подобных языках, но все же работает невероятно быстро и к тому же очень хорошо вписывается в код HTML.
Если вы собираетесь набирать встречающиеся в этой книге примеры на РНР, чтобы работать параллельно с моим повествованием, не забывайте предварять их тегом <?php, а в конце ить тег ?>, для того чтобы обеспечить их обработку интерпретатором РНР. Для упрощения этой задачи можно заранее подготовить файл example.php, содержащий эти теги.
Используя РНР, вы получаете средство управления своим веб-сервером с неограниченными возможностями. Если понадобится на лету внести изменения в HTML, обработать данные кредитной карты, добавить сведения о пользователе в базу данных или извлечь информацию из стороннего веб-сайта, все это можно будет сделать из тех же самых PHP-файлов, в которых находится и сам код HTML.
Использование MySQL
Разумеется, без средств отслеживания тех изменений, которые пользователь вносит по мере использования вашего веб-сайта, нельзя в полной мере говорить о возможностях динамического изменения выходного кода HTML. На заре создания Всемирной паутины многие сайты использовали для хранения таких данных, как имена пользователей и пароли, неструктурированные текстовые файлы. Но такой подход мог вызвать ряд проблем, если файл не был надежно заблокирован от повреждений, возникающих при одновременном доступе к нему множества пользователей. К тому же неструктурированный файл мог разрастаться до таких размеров, что с ним непросто было работать, не говоря уже о трудностях, связанных с попытками объединения файлов и осуществления в них сложных поисковых операций за какое-нибудь мало-мальски приемлемое время.
Именно в таких случаях приобретает большое значение использование реляционных баз данных со структурированной системой запросов. И MySQL, будучи совершенно бесплатной и установленной на огромном количестве веб-серверов Интернета системой, оказывается как нельзя кстати. Она представляет собой надежную и исключительно быстродействующую систему управления базами данных, использующую команды, похожие на простые английские слова.
Высшим уровнем структуры MySQL является база данных, внутри которой можно иметь одну или несколько таблиц, содержащих ваши данные. Предположим, например, что вы работаете над таблицей под названием users (пользователи), внутри которой были созданы графы для фамилий — surname, имен — firstname и адресов электронной почты — email, и теперь нужно добавить еще одного пользователя. Одна из команд, которую можно использовать для этого, выглядит следующим образом:
INSERT INTO users VALUES ( 'Sm ith', 'John', 'jsmith@niysite.com'):
Разумеется, как упоминалось ранее, для создания базы данных и таблицы и настройки всех нужных полей понадобится выдать и другие команды, но используемая здесь команда INSERT демонстрирует простоту добавления в базу данных новой информации. Команда INSERT является примером структурированного языка запросов (Structured Query Language (SQL)), разработанного в начале 1970-х годов и напоминающего один из старейших языков программирования — COBOL. Тем не менее он хорошо подходит для запросов к базе данных, что и предопределило его использование в течение столь длительного времени.
Так же просто выполняется и поиск данных. Предположим, что имеется адрес электронной почты пользователя и нужно найти имя его владельца. Для этого можно воспользоваться следующим запросом MySQL:
SELECT surname.firstname FROM users WHERE email='jsmith@mysite.com';
После чего MySQL вернет Smith, John и любые другие мары имен, которые могут быть связаны в базе данных с адресом электронном почты.
Нетрудно предположить, что возможности MySQL простираются значительно дальше выполнения простых команд вставки и выбора INSERT и SELECT. Например, можно объединить несколько таблиц в соответствии с множеством различных критериев, запросить результаты, выбрав порядок их выдачи из множества вариантов, найти частичные совпадения, если известна только часть искомой строки, вернуть только конкретно заданное количество результатов и сделать многое другое.
При использовании РНР все эти вызовы можно направлять непосредственно к MySQL, без необходимости запуска самой программы MySQL или использования ее интерфейса командной строки. Это значит, что для того, чтобы докопаться до нужного вам элемента данных, вы можете сохранять результаты в массивах для их обработки и осуществления множества поисковых операций, каждая из которых зависит от результатов, возвращенных предыдущими операциями.
Далее будет показано, что для придания еще большей мощности прямо в MySQL встроен ряд дополнительных функций, которые можно вызвать для наиболее часто встречающихся операций и повышения скорости обработки данных.
Использование JavaScript
Самая старая из основных технологий, рассматриваемых в данной книге, JavaScript, была создана с целью получения доступа из сценариев ко всем элементам HTML- документа. Иными словами, она предоставляет средства для динамического взаимодействия с пользователем, например для проверки приемлемости адресов электронной почты в формах ввода данных, отображения подсказок наподобие «Вы действительно подразумевали именно это?» и т. д. (хотя с точки зрения безопасности, которая всегда должна реализовываться на веб-сервере, на эту технологию положиться нельзя). -
В сочетании с CSS (смотрите следующий раздел) JavaScript закладывает основу мощности динамических веб-страниц, которые изменяются буквально на глазах, в отличие от технологии, при которой сервер возвращает новую страницу.
Тем не менее с использованием JavaScript могут возникнуть осложнения, обусловленные некоторыми существенными различиями в способах реализации этого языка, выбранных разными разработчиками браузеров. В основном эти различия возникают, когда некоторые производители пытаются придать своим браузерам дополнительные функциональные возможности, не обращая внимания на совместимость с продуктами своих конкурентов.
К счастью, разработчики в большинстве своем уже взялись за ум и осознали необходимость полной совместимости своих продуктов, для того чтобы разработчикам веб-продуктов не приходилось создавать код с множеством исключений. Но остаются миллионы экземпляров устаревших браузеров, которыми будут пользоваться на протяжении еще многих лет. Тем не менее и для них существуют решения проблем несовместимости, и позже в этой книге будут рассмотрены технологии, позволяющие без каких-либо опасений проигнорировать существующие различия.
А сейчас давайте взглянем на то, как можно воспользоваться обычным JavaScript, воспринимаемым всеми браузерами:
<script type="text/javascript">
document, write ("Hello World. Today is " + DateO ):
</script>
Этот фрагмент кода предписывает веб-браузеру интерпретировать все, что находится внутри тегов script, в качестве кода JavaScript, который браузер затем интерпретирует путем записи в текущий документ текста «Hello World. Today is», а также даты, полученной за счет использования принадлежащей JavaScript функции Date. В результате получится нечто подобное следующему:
Hello World. Today is Sun Jan 01 2012 14:14:00
■^Г I
m% Стоит взять на заметку: если не требуется указывать конкретную версию JavaScript, то, как м?» д* правило, можно опустить type="text/javascript" и использовать для начала интерпретации — —3?' JavaScript тег < script>.
Ранее было упомянуто, что изначально JavaScript разрабатывался для того, чтобы получить возможность динамического управления различными элементами, находящимися внутри HTML-документа, и это его предназначение по-прежнему является основным. Но все чаще JavaScript используется для реализации технологии Ajax. Это понятие используется для обозначения процессов доступа к веб-серверу в фоновом режиме. (Сначала оно означало «асинхронный JavaScript и XML» — Asynchronous JavaScript and XML, но сейчас это определение несколько устарело.)
Ajax является основным процессом, лежащим в основе технологии, известной как Web 2.0 (этот термин популяризирован Тимом О’Рейли (Tim O ’Reilly), основателем и исполнительным директором издательства, в котором эта книга вышла на английском языке), при использовании которой веб-страницы стали напоминать автономные программы, поскольку их уже не нужно загружать целиком. Вместо этого в быстром вызове Ajax может быть задействован отдельный элемент вебстраницы, например, может быть изменена ваша фотография на сайте социальной сети или заменена кнопка, на которой нужно щелкнуть, отвечая на вопрос. Полностью эта тема будет рассмотрена в главе 17.
Использование CSS
После появления третьего стандарта (CSS3) CSS предлагает уровень динамической интерактивности, которая прежде поддерживалась только с помощью JavaScript. Например, вы можете не только придать стиль любому элементу HTML, чтобы изменить его размеры, цвета, границы, интервалы, но теперь, используя всего лишь несколько строк CSS, вы можете добавить своим веб-страницам анимированные переходы и преобразования.
Применение CSS может просто заключаться во вставке правил между тегами <st.yle> и </style>, расположенными в заголовке веб-страницы:
<style>
P
{
text-align:justify;
font-family:Helvetica:
}
</style>
Эти правила будут изменять выравнивание по умолчанию тега <р>, чтобы содержащиеся в нем абзацы были полностью выровнены и для них использовался шрифт Helvetica.
В главе 18 будет показано, что существует множество различных способов задания правил CSS и их также можно включать непосредственно в теги или сохранять во внешнем файле, предназначенном для отдельной загрузки. Такая гибкость позволяет не только проводить точную настройку стиля HTML. Вы также увидите, как с ее помощью можно, например, предоставить встроенную функцию hover для анимирования объектов при проходе над ними указателя мыши. Кроме того, вы научитесь получать доступ ко всем свойствам CSS-элемента из JavaScript и из HTML.
Веб-сервер Apache
В дополнение к РНР, MySQL, JavaScript и CSS в динамической веб-технологии фигурирует и пятый герой —веб-сервер. В нашей книге предполагается, что это веб-сервер Apache. Мы уже немного касались того, что делает веб-сервер в процессе обмена информацией между клиентом и сервером по протоколу HTTP, но на самом деле негласно он выполняет куда более масштабную работу.
Например, Apache обслуживает не только HTML-файлы —он работает с широким спектром файлов, начиная с файлов изображений и Flash-роликов и заканчивая аудиофайлами формата MP3, файлами RSS-потоков (Really Simple Syndication — простое распространение по подписке) и т. д. Каждый элемент, найденный на HTML- странице веб-клиентом, также запрашивается у сервера, который затем и осуществляет обслуживание.
Но эти объекты не должны быть статическими файлами, такими как изображения GIF-формата. Все они могут быть сгенерированы программами, такими как сценарии РНР. И это действительно возможно: РНР способен даже создавать для вас изображения и другие файлы, либо на лету, либо заранее, в расчете на последующее обслуживание. Для этого обычно имеются модули, либо предварительно скомпилированные в Apache или РНР, либо вызываемые во время выполнения программы. Одним из таких модулей является библиотека GD (сокращение от Graphics Draw - «рисование графики»), которую РНР использует для создания и обработки графических элементов.
Apache поддерживает также обширный арсенал собственных модулей. В дополнение к модулям РНР наиболее важными для вас как для веб-программиста будут модули, занимающиеся обеспечением безопасности. В качестве других примеров могут послужить модуль Rewrite, позволяющий веб-серверу обрабатывать широкий
диапазон типов URL-адресов и перезаписывать их в соответствии с его внутренними требованиями, и модуль Proxy, который можно использовать для обслуживания часто запрашиваемых страниц из кэша, для того чтобы снизить нагрузку на сервер.
Далее в этой книге будет показано практическое применение этих модулей для улучшения свойств, предоставляемых рассматриваемыми нами основными технологиями.
Несколько слов о программах с открытым исходным кодом
Часто спорят, обусловлена или нет популярность этих технологий тем, что они представлены программами с открытым исходным кодом, но РНР, MySQL и Apache действительно являются наиболее востребованными инструментами в своих категориях.
Тем не менее следует отметить, что их принадлежность к продуктам с открытым кодом означает, что они были разработаны в сообществе команд программистов, которые придавали им свойства в соответствии со своими желаниями и потребностями и хранили исходный код доступным для всеобщего просмотра и изменения. Ошибки и бреши в системе безопасности могли предотвращаться еще до их проявления.
Есть и еще одно преимущество: все эти программы могут использоваться бесплатно. Если вы наращиваете пропускную способность своего веб-сайта и привлекаете к его обслуживанию дополнительные серверы, не нужно задумываться о приобретении дополнительных лицензий. Также не нужно пересматривать свой бюджет перед тем, как принять решение об обновлении системы и установке самых последних версий этих продуктов.
А теперь все это, вместе взятое
Истинная красота РНР, MySQL, JavaScript и CSS проявляется в том замечательном способе, благодаря которому они совместно работают над производством динамического веб-контента: РНР занят основной работой на веб-сервере, MySQL управляет данными, а комбинация CSS и JavaScript заботится о представлении веб-страницы. JavaScript может также взаимодействовать с вашим PHP-кодом на веб-сервере, когда ему нужно что-нибудь обновить (как на сервере, так и на вебстранице).
Неплохо бы теперь подвести краткий итог всему, что изложено в данной главе, и, не используя программный код, рассмотреть процесс, сочетающий в себе все три технологии в повседневно использующейся многими веб-сайтами функции Ajax: проверке в процессе регистрации новой учетной записи, не используется ли на сайте выбранное имя кем-нибудь другим. Хорошим примером подобного использования технологий может послужить почтовый сервер Gmail (рис. 1.3).
(55© ahttps^/accounts-go... Р ~Д й X~| | Л Google Accounts
file £dit View Favorites Tools Help
Your Google Account is more than just Gmail.Talk, chat, share, schedule, store, organize, collaborate, discover and create. Use Google products from Gmail to Google+ to YouTube, view your search history, all with one username and password., all backed up all the time and easy to find at (you guessed it) Google.com.Your Google Account is more than just Gmail.Talk, chat, share, schedule, store, organize, collaborate, discover and create. Use Google products from Gmail to Google+ to YouTube, view your search history, all with one username and password., all backed up all the time and easy to find at (you guessed it) Google.com.NameSamuelNameSamuelSmithSmithChoose your usernamesamsmith@gmail.comChoose your usernamesamsmith@gmail.comSomeone already has that username. Try another?Available. smithsamuel169 ss1676073Someone already has that username. Try another?Available. smithsamuel169 ss1676073
Your Google Account is more than just Gmail.
Talk, chat, share, schedule, store, organize, collaborate, discover and create. Use Google products from Gmail to Google+ to YouTube, view your search history, all with one username and password., all backed up all the time and easy to find at (you guessed it) Google.com.
Your Google Account is more than just Gmail.
Talk, chat, share, schedule, store, organize, collaborate, discover and create. Use Google products from Gmail to Google+ to YouTube, view your search history, all with one username and password., all backed up all the time and easy to find at (you guessed it) Google.com.
Name
Samuel
Name
Samuel
Smith
Smith
Choose your username
samsmith@gmail.com
Choose your username
samsmith@gmail.com
Someone already has that username. Try another?
Available. smithsamuel169 ss1676073
Someone already has that username. Try another?
Available. smithsamuel169 ss1676073
Create a password
<100% ’
Рис. 1.3. Gmail использует технологию Ajax для проверки допустимости пользовательских имен
Этот Ajax-процесс состоит примерно из следующих шагов.
Сервер выдает код HTML для создания веб-формы, запрашивающей необходимые данные: имя пользователя, настоящее имя, настоящую фамилию и адрес электронной почты.
Одновременно с этим сервер вкладывает в HTML код JavaScript, позволяющий отслеживать содержимое поля ввода имени пользователя и проверять два обстоятельства: введен ли в это поле какой-нибудь текст и был ли фокус ввода перемещен из этого поля по щелчку пользователя на другом поле ввода.
Как только будет введен текст и фокус ввода перемещен на другой элемент формы, кодJavaScript в фоновом режиме передает введенное имя пользователя PHP-сценарию на веб-сервере и ждет ответной реакции.
Веб-сервер осуществляет поиск имени пользователя и возвращает кодуJavaScript ответ, в котором сообщает, было ли уже задействовано такое же имя.
Затем JavaScript размещает под полем ввода имени пользователя индикатор приемлемости имени пользователя, возможно, в виде зеленой галочки или красного крестика, сопровождая его текстом.
Если пользователь ввел неприемлемое имя, но все же пытается отправить форму, код JavaScript прерывает отправку и повторно обращает внимание пользователя (возможно, выводя более крупный графический индикатор и/или открывая окно предупреждения) на необходимость выбора другого имени.
Усовершенствованная версия этого процесса может даже изучить имя, запрошенное пользователем, и предложить альтернативное доступное на данный момент имя.
Все это для удобства пользователя и целостности восприятия им всего происходящего делается без привлечения его внимания в фоновом режиме. Без использования Ajax на сервер будет отправлена вся форма, затем он вернет код HTML с подсветкой тех полей, в которых были допущены ошибки. Можно, конечно, сделать и так, но обработка поля налету будет выглядеть намного интереснее и приятнее.
Технология Ajax может использоваться для решения куда более широкого круга задач, чем простой контроль и обработка вводимой информации. Далее в этой книге будет рассмотрено много дополнительных приемов, реализуемых с применением Ajax.
В этой главе вашему вниманию было представлено довольно полное введение в основные технологии применения РНР, MySQL, JavaScript и CSS (а также Apache) и рассмотрен порядок их совместной работы. В главе 2 будут рассмотрены способы установки вашего собственного сервера, предназначенного для веб-разработок, на котором можно будет освоить на практике весь изучаемый материал. Но сначала ответьте на следующие вопросы.
Проверьте ваши знания
Какие четыре компонента необходимы для создания полностью динамических веб-сайтов?
Что означает аббревиатура HTML?
Почему в названии MySQL присутствуют буквы SQL?
И РНР и JavaScript являются языками программирования, генерирующими динамическое содержимое веб-страниц. В чем состоит их главное различие и почему вы будете использовать оба этих языка?
Что означает аббревиатура CSS?
Если вам удастся обнаружить ошибку в одном из инструментальных средств с открытым кодом (что случается довольно редко), то как, по-вашему, можно получить исправленную версию?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 1».
2
Установка сервера, предназначенного для разработки
Если у вас есть желание разрабатывать интернет-приложения, но нет собственного сервера для их разработки, то прежде, чем протестировать каждую созданную модификацию приложения, вам придется загружать ее на сервер, находящийся где-нибудь в Интернете.
Даже при наличии высокоскоростного широкополосного подключения это обстоятельство может существенно замедлить разработку. А на локальном компьютере тестирование может быть не сложнее обновления программы (зачастую запускается простым щелчком на значке) с последующим нажатием кнопки Refresh (Обновить) в браузере.
Еще одно преимущество разработочного сервера заключается в том, что при написании и тестировании программ не нужно волноваться о смущающих разработчика ошибках или проблемах безопасности, однако при размещении приложения на публичном веб-сайте следует знать о том, что люди могут увидеть, или о том, что они могут сделать с вашим приложением. Лучше решить все проблемы, пока вы работаете дома или в небольшом офисе, который, вероятнее всего, защищен межсетевыми экранами (брандмауэрами) и другими средствами обеспечения безопасности.
Получив в свое распоряжение разработочный сервер, вы удивитесь, как раньше могли обходиться без него, а также обрадуетесь легкости его настройки. Нужно лишь пройти все шаги, изложенные в следующих разделах, и выполнить соответствующие указания для обычных персональных компьютеров, Мае- или Linux-систем.
В этой главе будет рассмотрена только серверная сторона сетевого взаимодействия, о которой шла речь в главе 1. Но для тестирования результатов вашей работы, особенно потом, когда мы приступим к использованию JavaScript и CSS, понадобится также копия каждого основного веб-браузера, работающего под управлением удобной для вас системы. Список браузеров должен состоять по крайней мере из Internet Explorer, Mozilla Firefox, Opera, Safari и Google Chrome. Если вы хотите убедиться, что ваши приложения также хорошо выглядят на мобильных устройствах, постарайтесь использовать для тестирования телефоны и планшеты под управлением Apple iOS и Google Android.
Что такое WAMP, МАМР и LAMP
WAMP, МАМР и LAMP — это сокращения от «Windows, Apache, MySQL и РНР», «Мае, Apache, MySQL и РНР» и «Linux, Apache, MySQL и РНР» соответственно. Данными сокращениями описываются полноценные функциональные установки, используемые для разработки динамических веб-страниц.
Системы WAMP, МАМР и LAMP поставляются в форме пакетов, связывающих упакованные программы таким образом, чтобы их не нужно было устанавливать и настраивать по отдельности. Это означает, что нужно просто загрузить и установить одну программу и следовать простым подсказкам, чтобы подготовить разработочный сервер и запустить его в кратчайшие сроки и с минимальными усилиями.
В процессе установки будут созданы исходные настройки. Конфигурация безопасности при такой установке не будет столь же строгой, как на технологическом веб-сервере, поскольку она оптимизирован^ для использования на локальной машине. Поэтому не следует пользоваться такими настройками при установке технологического сервера.
Однако для разработки и тестирования веб-сайтов и приложений подобная установка подойдет как нельзя лучше.
Если для создания своей системы разработки вы решили не использовать W/L/MAMP, следует учесть, что загрузка и самостоятельная взаимоувязка составных частей займет очень много времени и может отнять большое количество сил на исследования для создания полноценной конфигурации всей системы. Но если все компоненты у вас уже установлены и согласованы друг с другом, они смогут работать с примерами, приводимыми в этой книге.
Установка WAMP на систему Windows
Существует несколько доступных WAMP-серверов, каждый из которых предлагает свою немного отличающуюся от других конфигурацию. Наверное, самый лучший из них —Zend Server СЕ (где СЕ означает Community Edition — «издание сообщества»), поскольку он распространяется бесплатно самими разработчиками РНР. Его можно загрузить с веб-сайта http://tinyurl.com/zendce (рис. 2.1).
Я рекомендую вам всегда загружать последний стабильный выпуск (в данном примере это 5.6.0 SP1 для Windows), который будет показан на веб-странице в разделе загрузок первым. На странице должен быть изображен установщик, подходящий для вашего компьютера: Linux, Windows или OS X. Перед загрузкой вам будет предложено войти на сайт. Можно щелкнуть на ссылке, чтобы получить файл без входа на сайт или регистрации, однако в таком случае вам не будут приходить по электронной почте сообщения о выходе обновлений и другие новости.
Возможно, когда данное издание выйдет в свет, некоторые виды экранов и настройки, рассмотренные далее, могут измениться. Если это произойдет, руководствуйтесь здравым смыслом, чтобы выполнить приведенную последовательность действий аналогичным образом.
Favorites Tools HelpFavorites Tools HelpComplete PHP stack that includes ZFComplete PHP stack that includes ZFOpcode acceleration {Zend Optimizer*)Opcode acceleration {Zend Optimizer*)PHP application deploymentPHP application deploymentPage caching and job queue supportPage caching and job queue supportPHP monitoring and code tracingPHP monitoring and code tracingZend Server Cluster Manager supportZend Server Cluster Manager supportTechnical support, updates and hotfixesTechnical support, updates and hotfixeswww.zend.com/en/prc & * § С Xwww.zend.com/en/prc & * § С XZend Server CE DownloadsZend Server CE Downloads0 Linux:#/ Windows ti*-. Mac OS X: @ ibli ।0 Linux:#/ Windows ti*-. Mac OS X: @ ibli ।ProductProductVersionVersionZend Server CE (PHP 5.3)Zend Server CE (PHP 5.3)5 6.0 SP115 6.0 SP11иОиОZend Server Community Edition (CE) is the free edition of Zend Server, our production-ready Web Application ServerZend Server Community Edition (CE) is the free edition of Zend Server, our production-ready Web Application ServerServerServerYesYesYesYesYesYesYesYesYesYesYesYesYesYesService Pack/Hotfixes AvailableService Pack/Hotfixes AvailableServer CEServer CEYesYesYesYesIBM only,for 1 yearIBM only,for 1 yearUse the latest patches to ensure a fully secure environment!Use the latest patches to ensure a fully secure environment!Format/SizeFormat/SizeNotesNotesi'EXE) 58 23 MBi'EXE) 58 23 MBRelease NotesRelease Notes^100%^100%
Favorites Tools Help
Favorites Tools Help
Complete PHP stack that includes ZF
Complete PHP stack that includes ZF
Opcode acceleration {Zend Optimizer*)
Opcode acceleration {Zend Optimizer*)
PHP application deployment
PHP application deployment
Page caching and job queue support
Page caching and job queue support
PHP monitoring and code tracing
PHP monitoring and code tracing
Zend Server Cluster Manager support
Zend Server Cluster Manager support
Technical support, updates and hotfixes
Technical support, updates and hotfixes
www.zend.com/en/prc & * § С X
www.zend.com/en/prc & * § С X
Zend Server CE Downloads
Zend Server CE Downloads
0 Linux:#/ Windows ti*-. Mac OS X: @ ibli ।
0 Linux:#/ Windows ti*-. Mac OS X: @ ibli ।
Product
Product
Version
Version
Zend Server CE (PHP 5.3)
Zend Server CE (PHP 5.3)
5 6.0 SP11
5 6.0 SP11
иО
иО
Zend Server Community Edition (CE) is the free edition of Zend Server, our production-ready Web Application Server
Zend Server Community Edition (CE) is the free edition of Zend Server, our production-ready Web Application Server
Server
Server
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Service Pack/Hotfixes Available
Service Pack/Hotfixes Available
Server CE
Server CE
Yes
Yes
Yes
Yes
IBM only,for 1 year
IBM only,for 1 year
Use the latest patches to ensure a fully secure environment!
Use the latest patches to ensure a fully secure environment!
Format/Size
Format/Size
Notes
Notes
i'EXE) 58 23 MB
i'EXE) 58 23 MB
Release Notes
Release Notes
^100%
^100%
Рис. 2.1. Zend Server CE для Windows можно загрузить с веб-сайта Zend
После загрузки запустите установщик, чтобы появилось окно, показанное па рис. 2.2.
Щелкните на кнопке Next (Далее) и примите лицензионное соглашение, чтобы переместиться на экран типа установки (Setup Туре) (рис. 2.3). Установите переключатель в положение Custom (По выбору пользователя), чтобы установить еще и сервер MySQL.
Когда появится окно установки но выбору пользователя (Custom Setup), прокрутите вниз до конца список вариантов установки и убедитесь, что установлены флажки phpMyAdmin и MySQL Server (рис. 2.4). Затем щелкните на кнопке Next (Далее).
На следующем экране (рис. 2.5), даже если у вас уже установлен веб-сервер IIS, я рекомендую выбрать установку веб-сервера Apache, поскольку примеры в этой книге рассчитаны на его применение. После выполнения необходимых настроек щелкните на кнопке Next (Далее).
Согласитесь с предлагаемым по умолчанию значением 80 для порта веб-сервера и со значением 10081 для порта интерфейса сервера Zend (рис. 2.6) и нажмите кнопку Next (Далее).
Zend Server Community Edition Installer
IrctatShieki
server
Welcome to the Zend Servei Community Edition 5.6 Installer
The Installation wizaid will irv-»alH>nd 'ie've i Coriimumiv Edition on youi computet Го continue click Next
Hext
Рис. 2.2. Основное окно установщика Zend Server CE
Zend Server Community Edition Installer
lrr,!s89w*l
server
▼A
Setup Type
Select an installation method
Click the type of setup you prefer then diet Mext
Г” L'pu: al Program will be metalled with the most common option!
Recommended tor most user-:
C Full AH program texture: will be installed
Require^ an Internet connection m order to download 'htidpaiiy component-:.
(♦ ’Custom - ou may select Hie option: mu want to mrtatl Recommended tot " advanced uter:
Require.* an Internet connection in order to download third-party c omponent'
eack
Zend Server Community Edition Instetler
server
n
IrctaSShreid
Custom Setup
Select components to install
Only согп рспеп к with a ma^ ed ■:h c jI box wil I oe it. '.r*llei A(t.:-r mMallahon run the Installer again to add component; not se I lected >hi- tune
:■ LJZend Java Btiage
a^end Deployment
Q 0Zend Framework
?ШВа$е
; □Extras(separatedownload)
; DD[o io (sepaiate download)
□J Informix
0OradeOCI Driver
@ phpMyAdmin I separate download!
1 D lB M DB2 RTCL (separate downloadl
[Д MySQL Server (s^ra'tedw^
□JM S SQ L N alive Client (separate download I
149.40 MB of space required on the С drive
240499 74 MB of space available on the С drive
-Description---
Installs the MySQL
E ssential Database Server
I version 5 1 50|
Рис. 2.4. Установите флажки для phpMyAdmin и MySQL Server
Zend Server Community Edition Installer
server
n
Web Server
Select the type ol Web Server to use and deltne where to install Zend Server
Select a Web Serve»
/• Install an Apache 2.2.21 Web Server
Note Microvolt IIS n currently not installed
U you want to use IIS please install it and restart the Zend Server irr.iallalion
Define where to Install Zend Server
Click Next to approve or Browse to change the localton
•Destination Folder
C \Program Files IxSS^Zend''.
Browse...
Bail.Bail.
Bail.
Bail.
irata^StreM
Zend Server Community Edition Installer
server
Number
Click Next tc>use the default settings or change the port values that your Webserver will listen and that the Administration Interface uses respectively
Web Server Port
80
ОК
OKOK(10081
OK
OK
Вэcl-Вэcl-Ins-fcaRStesJd
Вэcl-
Вэcl-
Рис. 2.6. Согласие с предлагаемыми по умолчанию портами
После назначения портов вы попадете на экран, показанный на рис. 2.7, где для начала установки нужно щелкнуть на кнопке Install (Установить).
В ходе установки могут быть загружены некоторые дополнительные файлы, поэтому на установку программ может уйти несколько минут. Когда все будет готово, появится уведомление о том, что после щелчка на кнопке Finish (Завершить) можно будет приступить к использованию программного обеспечения. Как только это будет сделано, откроется используемый вами по умолчанию браузер с загруженной в него страницей, которая показана на рис. 2.8. На ней необходимо установить флажок, чтобы согласиться с условиями.
Теперь вы готовы к установке пароля (рис. 2.9). Выберите такой пароль, который легко запомнить, и щелкните на кнопке Next (Далее) для перехода к экрану, показанному на рис. 2.10. Нажмите кнопку Finish (Завершить).
ЛГ.»
•К—
Если какой-либо из предлагаемых портов утверждает, что он уже занят (обычно это быва- му 4 * ет при наличии другого запущенного веб-сервера), и вы не можете использовать значения — по умолчанию, попробуйте установить значение 8080 (или 8000) для порта веб-сервера
и значение 10082 для порта интерфейса сервера Zend. Не забудьте воспользоваться этими значениями позже, при доступе как к веб-страницам, так и к серверу Zend. Например, вместо веб-страницы http://localhost/index.htm, нужно воспользоваться адресом http:// localhost:8080/index.htm.
Zend Server Community Edition Installer
server
Installation Settings
Review your settings belote installing Zend Setver Community Edition
Click Next to begin installing Zend Server Community Edition with the following components
РНР 5.3 9
Common Extensions
Additional Extensions
Zend Optjmeef+
Zend Debugger
Zend Data Cache
Zend Page Cache
Zend Monitor
Zend Job Queue
Zend Session Clustering
Zend Code T racing
Zend Deployment
Zend Framework 1.11.11
Zend Framework Base
<r-
|«1ЛМ***|«1ЛМ***Рис. 2.7. Щелкните на кнопке Install (Установить) для начала установкиРис. 2.7. Щелкните на кнопке Install (Установить) для начала установкиStep 1 of 3 : End User License AgreementStep 1 of 3 : End User License AgreementPlease read and accept the following terms before using Zend Server Community EditionPlease read and accept the following terms before using Zend Server Community EditionГ I have read and agree to the end user license agreement termsГ I have read and agree to the end user license agreement termsZend Technologies Ltd.End-User License AgreementThis End-User License Agreement (this "Agreement") is a legal contract between you, as either an individual or a single business entity, and Zend Technologies Ltd. and its affiliates ("Zend”)Zend Technologies Ltd.End-User License AgreementThis End-User License Agreement (this "Agreement") is a legal contract between you, as either an individual or a single business entity, and Zend Technologies Ltd. and its affiliates ("Zend”); File Edit View Favorites Jools grip; File Edit View Favorites Jools gripREAD THE TERMS AND CONDITIONS OF THIS AGREEMENT CAREFULLY BEFORE DOWNLOADING OR INSTALLING ZEND’S PROPRIETARY SOFTWARE (THE 'SOFTWARE") OR OBTAINING A LICENSE KEY TO THE SOFTWARE OR USING THE SOFTWARE. THE SOFTWARE IS FURTHER DEFINED IN AN ORDER DOCUMENT (AN 'ORDER'). ENTERED INTO BETWEEN YOU AND ZEND OR YOU AND A ZEND RESELLER. WHICH SETS FORTH COMMERCIAL TERMS APPLICABLE TO YOUR PURCHASE OF THE CnCQUAQL - ..READ THE TERMS AND CONDITIONS OF THIS AGREEMENT CAREFULLY BEFORE DOWNLOADING OR INSTALLING ZEND’S PROPRIETARY SOFTWARE (THE 'SOFTWARE") OR OBTAINING A LICENSE KEY TO THE SOFTWARE OR USING THE SOFTWARE. THE SOFTWARE IS FURTHER DEFINED IN AN ORDER DOCUMENT (AN 'ORDER'). ENTERED INTO BETWEEN YOU AND ZEND OR YOU AND A ZEND RESELLER. WHICH SETS FORTH COMMERCIAL TERMS APPLICABLE TO YOUR PURCHASE OF THE CnCQUAQL - ..<*U0Q%<*U0Q%
|«1ЛМ***
|«1ЛМ***
Рис. 2.7. Щелкните на кнопке Install (Установить) для начала установки
Рис. 2.7. Щелкните на кнопке Install (Установить) для начала установки
Step 1 of 3 : End User License Agreement
Step 1 of 3 : End User License Agreement
Please read and accept the following terms before using Zend Server Community Edition
Please read and accept the following terms before using Zend Server Community Edition
Г I have read and agree to the end user license agreement terms
Г I have read and agree to the end user license agreement terms
Zend Technologies Ltd.
End-User License Agreement
This End-User License Agreement (this "Agreement") is a legal contract between you, as either an individual or a single business entity, and Zend Technologies Ltd. and its affiliates ("Zend”)
Zend Technologies Ltd.
End-User License Agreement
This End-User License Agreement (this "Agreement") is a legal contract between you, as either an individual or a single business entity, and Zend Technologies Ltd. and its affiliates ("Zend”)
; File Edit View Favorites Jools grip
; File Edit View Favorites Jools grip
READ THE TERMS AND CONDITIONS OF THIS AGREEMENT CAREFULLY BEFORE DOWNLOADING OR INSTALLING ZEND’S PROPRIETARY SOFTWARE (THE 'SOFTWARE") OR OBTAINING A LICENSE KEY TO THE SOFTWARE OR USING THE SOFTWARE. THE SOFTWARE IS FURTHER DEFINED IN AN ORDER DOCUMENT (AN 'ORDER'). ENTERED INTO BETWEEN YOU AND ZEND OR YOU AND A ZEND RESELLER. WHICH SETS FORTH COMMERCIAL TERMS APPLICABLE TO YOUR PURCHASE OF THE CnCQUAQL - ..
READ THE TERMS AND CONDITIONS OF THIS AGREEMENT CAREFULLY BEFORE DOWNLOADING OR INSTALLING ZEND’S PROPRIETARY SOFTWARE (THE 'SOFTWARE") OR OBTAINING A LICENSE KEY TO THE SOFTWARE OR USING THE SOFTWARE. THE SOFTWARE IS FURTHER DEFINED IN AN ORDER DOCUMENT (AN 'ORDER'). ENTERED INTO BETWEEN YOU AND ZEND OR YOU AND A ZEND RESELLER. WHICH SETS FORTH COMMERCIAL TERMS APPLICABLE TO YOUR PURCHASE OF THE CnCQUAQL - ..
<*U0Q%
<*U0Q%
Рис. 2.8. Чтобы воспользоваться Zend Server CE, нужно согласиться с условиями лицензии
Мв^НШ НМв^НШ Н......Http://localhostl00Bl/ZendServer/Loginsl3= Р * С X | | Ц Zend Server Community Ed.-......Http://localhostl00Bl/ZendServer/Loginsl3= Р * С X | | Ц Zend Server Community Ed.-file Edit View Favorites Toots Helpfile Edit View Favorites Toots HelpStep 3 of 3 : Notification RegistrationStep 3 of 3 : Notification RegistrationWelcome toZend Server Community Edition!Thank you for installing Zend Server Community Edition - you've made a great choice!Ifyou ever want to enhance your PHP environment with:Welcome toZend Server Community Edition!Thank you for installing Zend Server Community Edition - you've made a great choice!Ifyou ever want to enhance your PHP environment with:Automated deploymentApplication monitoringClustering and scalabilityAutomated deploymentApplication monitoringClustering and scalabilityPerfoimance optimization*Advancedjdebugging]andjanalysis tools24x7?technical supportPerfoimance optimization*Advancedjdebugging]andjanalysis tools24x7?technical supportYou can try the advanced features of Zend Server free for 30 days at any time!You can try the advanced features of Zend Server free for 30 days at any time!Г" ^Modify me of new ZendSetver Community Edition releases arid other important! jjpdatesEmail Address I1Г" ^Modify me of new ZendSetver Community Edition releases arid other important! jjpdatesEmail Address I1^100% • J^100% • J
Мв^НШ Н
Мв^НШ Н
......
Http://localhostl00Bl/ZendServer/Loginsl3= Р * С X | | Ц Zend Server Community Ed.-
......
Http://localhostl00Bl/ZendServer/Loginsl3= Р * С X | | Ц Zend Server Community Ed.-
file Edit View Favorites Toots Help
file Edit View Favorites Toots Help
Step 3 of 3 : Notification Registration
Step 3 of 3 : Notification Registration
Welcome to
Zend Server Community Edition!
Thank you for installing Zend Server Community Edition - you've made a great choice!
Ifyou ever want to enhance your PHP environment with:
Welcome to
Zend Server Community Edition!
Thank you for installing Zend Server Community Edition - you've made a great choice!
Ifyou ever want to enhance your PHP environment with:
Automated deployment
Application monitoring
Clustering and scalability
Automated deployment
Application monitoring
Clustering and scalability
Perfoimance optimization
*Advancedjdebugging]andjanalysis tools
24x7?technical support
Perfoimance optimization
*Advancedjdebugging]andjanalysis tools
24x7?technical support
You can try the advanced features of Zend Server free for 30 days at any time!
You can try the advanced features of Zend Server free for 30 days at any time!
Г" ^Modify me of new ZendSetver Community Edition releases arid other important! jjpdates
Email Address I1
Г" ^Modify me of new ZendSetver Community Edition releases arid other important! jjpdates
Email Address I1
^100% • J
^100% • J
Рис. 2.10. Для завершения установки щелкните на кнопке Finish (Завершить)
В результате браузер отобразит экран Dashboard (Инструментальная панель), который позволит вам управлять сервером (рис. 2.11).
й ☆ @ Iй ☆ @ I9 Applications9 ApplicationsQ Rule ManagementQ Rule Management•% Server Setup•% Server Setup£ Monitor£ MonitorAdministrationAdministrationDashboardDashboardTasksTasksSystem OverviewSystem OverviewEvents i Joos 1 Code Tracing ; Sarver Into I PHP Info [ LogsEvents i Joos 1 Code Tracing ; Sarver Into I PHP Info [ Logsip— http:7^xalhortd0^^P - d X || В Zend Server Community Ed... X [^JI ; DI* Edit View Favorites Tools Helpip— http:7^xalhortd0^^P - d X || В Zend Server Community Ed... X [^JI ; DI* Edit View Favorites Tools HelpLoad or Unload PHP ExtensionsLoad or Unload PHP ExtensionsConfigure Zend Server ComponentsConfigure Zend Server ComponentsChance PHP directive Values 'Chance PHP directive Values 'ViewPHPmfo oaaeViewPHPmfo oaaePHP VersionPHP Version5 3 9-ZS5 6.05 3 9-ZS5 6.0Zend Framework VersionZend Framework VersionOpen ohDMvAdminOpen ohDMvAdminI earn how to start with Zend Server and PHPI earn how to start with Zend Server and PHPZend Server Community EditionZend Server Community EditionZend Data CacheZend Data CacheElElZend DebuggerZend DebuggerBlBlZend Guard LoaderZend Guard Loader8325183251Zend Java BridgeZend Java BridgeтатаZend Optimizer-Zend Optimizer-в Restart PHP'в Restart PHP'FJFJ«4100%«4100%
й ☆ @ I
й ☆ @ I
9 Applications
9 Applications
Q Rule Management
Q Rule Management
•% Server Setup
•% Server Setup
£ Monitor
£ Monitor
Administration
Administration
Dashboard
Dashboard
Tasks
Tasks
System Overview
System Overview
Events i Joos 1 Code Tracing ; Sarver Into I PHP Info [ Logs
Events i Joos 1 Code Tracing ; Sarver Into I PHP Info [ Logs
ip— http:7^xalhortd0^^P - d X || В Zend Server Community Ed... X [^J
I ; DI* Edit View Favorites Tools Help
ip— http:7^xalhortd0^^P - d X || В Zend Server Community Ed... X [^J
I ; DI* Edit View Favorites Tools Help
Load or Unload PHP Extensions
Load or Unload PHP Extensions
Configure Zend Server Components
Configure Zend Server Components
Chance PHP directive Values '
Chance PHP directive Values '
ViewPHPmfo oaae
ViewPHPmfo oaae
PHP Version
PHP Version
5 3 9-ZS5 6.0
5 3 9-ZS5 6.0
Zend Framework Version
Zend Framework Version
Open ohDMvAdmin
Open ohDMvAdmin
I earn how to start with Zend Server and PHP
I earn how to start with Zend Server and PHP
Zend Server Community Edition
Zend Server Community Edition
Zend Data Cache
Zend Data Cache
El
El
Zend Debugger
Zend Debugger
Bl
Bl
Zend Guard Loader
Zend Guard Loader
83251
83251
Zend Java Bridge
Zend Java Bridge
та
та
Zend Optimizer-
Zend Optimizer-
в Restart PHP'
в Restart PHP'
FJ
FJ
«4100%
«4100%
Рис. 2.11. Экран управления Zend Server СЕ
Вы можете вернуться на этот экран в любое время, для чего нужно ввести в адресную строку вашего браузера адрес http://1оса Ihost: 10081 (или, если для порта интерфейса сервера Zend было указано значение, отличное от 10081, вы можете зайти на этот экран, используя заданное значение после двоеточия).
Тестирование установки
На данном этапе нужно проверить, что все работает должным образом. Для этого следует попытаться отобразить исходную веб-страницу, которая была сохранена в корневой папке сервера (рис. 2.12). Введите любой из следующих URL-адресов в адресную строку браузера:
http://local host
http://127.0.0.1
Слово 1оса 1host используется в URL-адресах для указания локального компьютера, который также будет отвечать на 1Р-адрес 127.0.0.1, поэтому исходный источник документов вашего веб-сервера можно вызывать любым из этих методов.
^Г--1
Если в процессе установки был выбран серверный порт, отличный от 80 (например 8080), u*r J « то после любого из предыдущих URL-адресов нужно поставить двоеточие и значение пор——— та. Например: http://localhost:8080. То же самое придется сделать для всех примеров в данной книге. Например, вместо URL http://localhost/example.php, следует ввести http:// localhost:8080/example.php (или то значение, которое вы выбрали).
Рис. 2.12. Так по умолчанию должна выглядеть главная страницаРис. 2.12. Так по умолчанию должна выглядеть главная страница
Рис. 2.12. Так по умолчанию должна выглядеть главная страница
Рис. 2.12. Так по умолчанию должна выглядеть главная страница
Исходным источником документов является каталог, в котором содержатся главные веб-документы домена. Именно он вводится, когда в браузере набирается базовый URL без пути, например http://yahoo.com или на локальном сервере http:// localhost.
По умолчанию Zend Server СЕ использует для этого каталога одно из следующих мест (первое из них относится к 32-разрядным компьютерам, а второе — к 64-разрядным):
С:/Program F i 1es/Zend/Apache2/htdocs
t^r-*% dl«,——3^t^r-*% dl«,——3^С:/Program Files (x86)/Zend/Apache2/htdocs
t^r-
*% dl«,
——3^
t^r-
*% dl«,
——3^
Если вы не знаете, какой у вас компьютер, 32- или 64-разрядный, попробуйте перейти в первый каталог. Если он существует, значит, у вас 32-разрядная машина. Если нет, откройте второй каталог, поскольку у вас 64-разрядный компьютер.
Теперь, чтобы убедиться в том, что все сконфигурировано должным образом, нужно создать обязательную программу Hello World. Создайте небольшой HTML- файл, содержащий следующие строки, применив Windows-программу Блокнот или любое другое приложение или текстовый редактор (при использовании текстовых процессоров, таких как Microsoft Word, которые создают форматированный текст, файл нужно сохранять в виде простого текста):
<html>
<head>
<title>A quick test</title>
</head>
<body>
Hello World!
</body>
</html>
После набора сохраните файл в ранее упомянутом каталоге, являющемся исходным источником документов, использовав в качестве имени файла te st. htm. Если вы создаете файл в Блокноте, убедитесь в том, что в окне Сохранить как в списке Тип файла выбран вариант Все файлы, а не Текстовые документы (*.txt).
Теперь вы сможете вызвать эту страницу в своем браузере, введя в адресной строке следующий URL-адрес (рис. 2.13):
http ://1 оса!host/test.htm
Рис. 2.13. Ваша первая веб-страница
Теперь вы получили успешно установленный программный продукт с полностью работоспособной системой WAMP. Если у вас возникнут трудности, обратитесь к подробной документации, размещенной по адресу http://tinyurl.com/zendcedocs.
Другие системы WAMP
При обновлении программы иногда работают неожиданным для вас образом и в них даже могут проявиться какие-нибудь ошибки. Поэтому если с использованием Zend Server СЕ возникают неразрешимые трудности, то вместо этого сервера вы можете остановить свой выбор на одном из многих других, к которому есть доступ в Интернете.
При выборе другого сервера вы точно так же сможете воспользоваться всеми примерами, приводимыми в данной книге, но при этом придется пользоваться инструкциями, поставляемыми с каждым WAMP-сервером, что может быть сложнее, чем следовать ранее упомянутому руководству.
Вот наиболее подходящие, на мой взгляд, серверы:
О EasyPHP: http://www.easyphp.org;
О ХАМ РР : http://apachefriends.org/en/xampp.html;
О WAMPServer: http://wampserver.com/en/;
О Glossword WAMP: http://glossword.biz/glosswordwamp/.
Установка МАМР на систему Mac OS X
Zend Server СЕ также доступен и для OS X. Его можно загрузить на странице http:// tinyurl.com/zendce (рис. 2.14).
ft.©,©. .ft.©,©. .. . , Zend Server CEJjojffilo^s.^+ : Ш http://www.zend.com/en/produas/server-ce/downloac tiBi 6w zend server ce. . , Zend Server CEJjojffilo^s.^+ : Ш http://www.zend.com/en/produas/server-ce/downloac tiBi 6w zend server ceComplete PHP stack that includesZFComplete PHP stack that includesZFOpcode acceleration (Zend Optimizer»-)Opcode acceleration (Zend Optimizer»-)PHP application deploymentPHP application deploymentPage caching and job queue supportPage caching and job queue supportYesYesYesYesYesYesYesYes
ft.©,©. .
ft.©,©. .
. . , Zend Server CEJjojffilo^s.^
+ : Ш http://www.zend.com/en/produas/server-ce/downloac tiBi 6w zend server ce
. . , Zend Server CEJjojffilo^s.^
+ : Ш http://www.zend.com/en/produas/server-ce/downloac tiBi 6w zend server ce
Complete PHP stack that includes
ZF
Complete PHP stack that includes
ZF
Opcode acceleration (Zend Optimizer»-)
Opcode acceleration (Zend Optimizer»-)
PHP application deployment
PHP application deployment
Page caching and job queue support
Page caching and job queue support
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
РНР monitoring and code tracing YesZend. Sfirver.Glygtef.ManagerYessupportTechnical support, updates andYeshotfixesРНР monitoring and code tracing YesZend. Sfirver.Glygtef.ManagerYessupportTechnical support, updates andYeshotfixesВЫ orty.for 1yearВЫ orty.for 1year
РНР monitoring and code tracing Yes
Zend. Sfirver.Glygtef.ManagerYes
support
Technical support, updates andYes
hotfixes
РНР monitoring and code tracing Yes
Zend. Sfirver.Glygtef.ManagerYes
support
Technical support, updates andYes
hotfixes
ВЫ orty.for 1year
ВЫ orty.for 1year
'TRY ZEND SERVER !r;ST'TRY ZEND SERVER !r;STUse the latest patches to ensure a fully secure envronmendUse the latest patches to ensure a fully secure envronmend
'TRY ZEND SERVER !r;ST
'TRY ZEND SERVER !r;ST
Use the latest patches to ensure a fully secure envronmend
Use the latest patches to ensure a fully secure envronmend
£ Linux ^ Windows ! Itf* Mac OS X в IBM i
Note: Zend Server CE for the Mac OS X platform is meant for development use only, not production use.
ProductProductVersionVersionFormat/SizeFormat/SizeNotesNotesZend Server CE (PHP 5.3) 5.6.0Zend Server CE (PHP 5.3) 5.6.0(OMG)] 152.902MB5(OMG)] 152.902MB5Release NotesRelease Notes
Product
Product
Version
Version
Format/Size
Format/Size
Notes
Notes
Zend Server CE (PHP 5.3) 5.6.0
Zend Server CE (PHP 5.3) 5.6.0
(OMG)] 152.902MB5
(OMG)] 152.902MB5
Release Notes
Release Notes
Рис. 2.14. Zend Server CE для OS X можно загрузить с веб-сайта Zend
Всегда загружайте самые последние стабильные версии (в данном случае это версия 5.6.0 SP1 для OS X), которые на веб-странице в разделе загрузки указываются первыми в списке. На странице должен быть изображен нужный установщик для вашего компьютера: Linux, Windows или OS X. Перед загрузкой вам будет предложено войти на сайт под своим именем. Можете щелкнуть на ссылке для получения файла без входа и без регистрации, но тогда вам не будут приходить по электронной почте уведомления об обновлении продукта и другие новости.
Когда загрузка завершится, дважды щелкните на файле с расширением DMG. Дождитесь проверки загрузки, после чего должно появиться окно, показанное на рис. 2.15. Если вы хотите ознакомиться с инструкциями, дважды щелкните на ссылке на файл READ ME. Перетащите Zend Server (Сервер Zend) и Zend Controller (Контроллер Zend) в свою папку Applications, которая доступна в меню Go (Перейти) файлового менеджера Finder, чтобы установить сервер и контроллер.
zend'Server 1
Community Edition
Double click to install Zend Server
j RTFREADMEj RTFREADME
j RTF
README
j RTF
README
Рис. 2.15. Перетащите приложения в папку Applications (Приложения)
Чтобы приступить к работе, найдите файл Zend Server в папке Applications. Дважды щелкните на нем, чтобы открыть окно установки (рис. 2.16).
Щелкните на кнопке Continue (Далее), прочитайте показанные после этого инструкции, а затем еще раз нажмите кнопку Continue (Далее), чтобы перейти на экран, показанный на рис. 2.17. Здесь можно принять решение, куда поместить устанавливаемое программное обеспечение. По умолчанию предлагается Macintosh HD. Когда все будет готово, нажмите Install (Установить) и введите при необходимости свой пароль.
В ходе инсталляции может быть задан вопрос, не хотите ли вы установить дополнительное программное обеспечение. Рекомендую принять все предложения, щелкнув на кнопке Install (Установить). Когда установка завершится, для закрытия установщика можно щелкнуть на кнопке Close (Закрыть).
ШШШ
Welcome to the ZendServer Installer
6 Introduction
Read Me
Destination Select
Installation Type
Installation
Summary
The Installation Wizard will install Zend Server on your computer. To start installing, click Continue.
zend server
Co. mmuntty Edition
I Go Back I [ Continue |
•>
Рис. 2.16. Установщик Zend Server CE
Standard Install on MMaclntosh HD"
0 Introduction
© Read Me
6 Destination Select
6 Installation Type
• Installation
# Summary
zend server
Community Edition
This will take 485.5 MB of space on your computer.
Click Install to perform a standard installation of this software on the disk "Macintosh HD".
Change Install Location...
Co Back Install ~)
Рис. 2.17. Выбор места установки
Чтобы завершить настройку, необходимо после установки найти в папке Applications значок программы Zend Server и дважды щелкнуть на нем кнопкой мыши. В браузере, используемом по умолчанию, должна появиться страница, похожая на
показанную на рис. 2.8. Теперь следуйте приглашениям, показанным на рис. 2.8-2.10, согласно которым нужно принять условия лицензионного соглашения, выбрать пароль и завершить установку. После этого вы попадете на основную панель инструментов.
Настройка MySQL
К сожалению, установщик Zend Server СЕ не устанавливает команды, необходимые для того, чтобы запускать, останавливать и перезапускать сервер MySQL, поэтому вам придется сделать это самостоятельно, для чего нужно открыть окно программы Terminal и ввести следующую команду:
sudo nano /usr/1ocal/zend/bin/zendctl.sh
После ввода пароля вы окажетесь в текстовом редакторе Nano, где нужно будет с помощью клавиши стрелки вниз переместить курсор на несколько строк вниз и, когда покажется строка с текстом MySQL_EN=" fal se", изменить слово false па true.
Теперь опустите курсор еще ниже, пока не появятся следующие две строки:
case $1 in
"start")
Ниже этих строк будет строка с отступом, имеющая следующий вид:
SO start-apache
Сразу же после этой строки вставьте новую строку, имеющую следующий вид:
$0 start-MySQL
Это позволит MySQL запуститься. Теперь опустите курсор еще ниже, пока не появится раздел, начинающийся со следующей строки:
"restart")
Ниже ее вы увидите строку с отступом, имеющую следующий вид:
$0 restart-apache
Сразу же после этой строки вставьте новую строку:
$0 restart-MySQL
Теперь создана возможность для перезапуска MySQL, но это еще не все. Чтобы можно было остановить MySQL, опустить курсор еще ниже, пока не появится следующая строка:
"stop")
Ниже этой строки есть следующая строка с отступом:
SO stop-apache %
Сразу же после этой строки вставьте новую строку:
$0 stop-MySQL
Теперь можно нажать сочетание клавиш Ctrl+X для выхода из режима редактирования. Когда поступит запрос на сохранение изменений, нажмите клавишу У, а затем нажмите клавишу Return для сохранения отредактированного файла.
Обеспечение запуска MySQL при загрузке системы
Чтобы программа MySQL запускалась при старте Мас-компьютера, нужно в программе Terminal ввести следующие команды:
cd /Library/StartupItems/ZendServer_init/ sudo rm zendctl.sh
sudo In -s /usr/local/zend/bin/zendctl.sh ./
Теперь Mac настроен, но программа MySQL еще не запущена. Чтобы завершить подготовку, введите следующую команду:
sudo /Library/Startupltems/Zend5erver_init/zendctl.sh restart
Проверка установки
Теперь можно проверить установку путем ввода следующих URL в адресную строку браузера для вызова экрана, показанного на рис. 2.18:
http ://localhost: 10088
http://127.0.0.1:10088
Слово localhost указывает на локальный компьютер, который также будет откликаться на 1Р-адрес 127.0.0.1. Причина, по которой нужно вводить : 10088 заключается в том, что на многих Мае-компьютерах уже есть запущенный веб-сервер: ввод этого адреса порта избавит от конфликтов. По этой же причине не следует забывать набирать : 10088 после каждого http ://localhost для всех примеров, приводимых в данной книге. Например, если используется файл с именем tes:из
браузера его нужно вызвать с помощью URL http://localhost: 10088/test.php.
Если вы уверены в том, что на вашем Mac-компьютере нет никакого другого запущенного веб-сервера, можно отредактировать файл конфигурации (если у вас есть разрешение на это действие), который находится по адресу /usr/local/zend/apache2/conf/httpd.conf. Измените команду Listen 10088 (она находится приблизительно в строке под номером 40) на Listen 80.
Затем перезапустите сервер, открыв утилиту Terminal и запустив следующую команду: sudo /usr/local/zend/bin/zendctl.sh restart.
В результате вам не придется добавлять : 10088 к локальным URL-адресам.
Страница, отображаемая в браузере при переходе по адресу http://localhost или http://localhost: 10088, представляет собой файл index, html, который находится в каталоге, являющемся исходным источником документов и содержащем главные веб-документы домена. Именно он вводится, когда в адресной строке браузера набирается базовый URL-адрес без указания пути, например http://yahoo.com, или в случает использования вашего локального веб-сервера —http://localhost.
® о о® о оZend server is now up and running on this server unt e content is added this message willZend server is now up and running on this server unt e content is added this message willno longer be displayedno longer be displayedIf you are this server s administrator:If you are this server s administrator:you can now add content to your Web server's document root you can configure and controlyou can now add content to your Web server's document root you can configure and controlyour Zend Server installation through the zend Server Administration Interface Please refeiyour Zend Server installation through the zend Server Administration Interface Please refeito the documentation or to vour installation s README file for more informationto the documentation or to vour installation s README file for more informationIf vou need help getting started with zend Server. please visit the on-line resources cageIf vou need help getting started with zend Server. please visit the on-line resources cageFor moie information about Zend Server you are welcome to visit us at www.zend.comFor moie information about Zend Server you are welcome to visit us at www.zend.comeeZend Server Test PageZend Server Test Page] + 0hnp7/locaJhost:lOO88/] + 0hnp7/locaJhost:lOO88/The PHP CompanyThe PHP CompanyzendzendРис. 2.18. Веб-сервер запущен и работаетРис. 2.18. Веб-сервер запущен и работаетZend Server Test PageZend Server Test Page
® о о
® о о
Zend server is now up and running on this server unt e content is added this message will
Zend server is now up and running on this server unt e content is added this message will
no longer be displayed
no longer be displayed
If you are this server s administrator:
If you are this server s administrator:
you can now add content to your Web server's document root you can configure and control
you can now add content to your Web server's document root you can configure and control
your Zend Server installation through the zend Server Administration Interface Please refei
your Zend Server installation through the zend Server Administration Interface Please refei
to the documentation or to vour installation s README file for more information
to the documentation or to vour installation s README file for more information
If vou need help getting started with zend Server. please visit the on-line resources cage
If vou need help getting started with zend Server. please visit the on-line resources cage
For moie information about Zend Server you are welcome to visit us at www.zend.com
For moie information about Zend Server you are welcome to visit us at www.zend.com
e
e
Zend Server Test Page
Zend Server Test Page
] + 0hnp7/locaJhost:lOO88/
] + 0hnp7/locaJhost:lOO88/
The PHP Company
The PHP Company
zend
zend
Рис. 2.18. Веб-сервер запущен и работает
Рис. 2.18. Веб-сервер запущен и работает
Zend Server Test Page
Zend Server Test Page
По умолчанию Zend Server СЕ использует в качестве своей папки исходного источника документов следующий каталог:
/usr/local/zend/apache2/htdocs
Теперь, чтобы убедиться в правильности всех настроек, нужно загрузить тестовый файл. С помощью, например, программы TextEdit (при использовании текстовых процессоров, таких как Microsoft Word, которые создают форматированный текст, файл нужно сохранять в виде простого текста) создайте небольшой HTML- файл, состоящий всего из нескольких строк:
<html>
<head>
<title>A quick test</title>
</head>
<body>
Hello World!
</body>
</html>
После набора этого текста сохраните файл в каталоге исходного источника документов, используя имя файла test.htm. Теперь вы можете вызвать эту
страницу в своем браузере путем ввода в адресную строку следующего URL- адреса (рис. 2.19):
http://localhost:10088/test.htm
Рис. 2.19. Если вы видите эту страницу, значит, все работает правильноРис. 2.19. Если вы видите эту страницу, значит, все работает правильно
Рис. 2.19. Если вы видите эту страницу, значит, все работает правильно
Рис. 2.19. Если вы видите эту страницу, значит, все работает правильно
Вы получили успешно установленный программный продукт с полностью работоспособной системой МАМР. При появлении проблем вы можете обратиться к документации, размещенной по адресу http://tinyurl.com/zendcedocs.
Для удобства я использую в книге для длинных URL-адресов, которые сложно набирать, службу сокращения веб-адресов tinyurl.com. Например, адрес http://tinyurl.com/zendcedocs намного короче, чем тот URL-адрес, к которому он приводит: http://files.zend.com/help/Zend- Server-Community-Edition/zend-server-community-edition.htm.
Установка LAMP на Linux
Инструкции и примеры, приведенные в этой книге, также будут хорошо работать и на Linux-компьютере. Существуют десятки популярных разновидностей Linux, на каждую из которых LAMP может устанавливаться со своими особенностями. Многие версии Linux поступают с предустановленным веб-сервером и MySQL, поэтому есть вероятность, что у вас уже все готово к работе. Чтобы понять, так ли это, попробуйте ввести в браузер следующий адрес и посмотрите, получите ли вы веб-страницу, используемую по умолчанию в исходном источнике документов:
http://localhost
Если все заработает, то у вас, наверное, установлен сервер Apache, а также может быть установлена и запущена база данных MySQL, но чтобы окончательно удостовериться в этом, проверьте факт их установки вместе со своим системным администратором.
Если веб-сервер не установлен, можете воспользоваться версией Zend Server СЕ, которую можно загрузить со страницы http://tinyurl.com/zendce.
Все необходимые инструкции можно найти на странице загрузки. Вы можете точно следовать этим инструкциям или воспользоваться предоставляемыми сценариями, и тогда вы сможете работать со всеми примерами, приведенными в книге.
Работа в удаленном режиме
Если есть доступ к веб-серверу, на котором уже имеются сконфигурированные РНР и MySQL, то вы всегда можете применить его для веб-разработок. Но это не самый лучший вариант, если только вы не пользуетесь высокоскоростным подключением. Разработка, выполняемая на локальной машине, позволяет протестировать новые модификации практически без задержки или с небольшой задержкой на загрузку.
Может вызвать трудности и удаленный доступ к MySQL. Для ручного создания баз данных и установки прав доступа из командной строки может понадобиться сервер Telnet или SSH. Компания, предоставляющая веб-хостинг, посоветует, как это можно сделать наилучшим образом, и предоставит пароль для доступа к MySQL (а в первую очередь, разумеется, для доступа к самому серверу).
Вход в систему
Я рекомендую пользователям Windows для доступа к Telnet и SSH (следует помнить, что уровень безопасности у SSH значительно выше, чем у Telnet) установить как минимум программу PuTTY, доступную по адресу http://putty.org.
На компьютере Мае система SSH будет доступна изначально. Нужно только выбрать папку Applications, перейти к папке Utilities, а затем запустить программу Terminal. В окне терминала нужно войти на сервер, используя SSH в следующей команде:
ssh mylogin @ server.com
где server .сот —это имя сервера, на который необходимо войти, a mylogin — имя пользователя, под которым нужно войти в систему. Затем у вас будет запрошен пароль для данного имени пользователя, и если вы введете правильный пароль, вход состоится.
Использование FTP
Для переноса файлов на веб-сервер и обратно понадобится FTP-программа. Если искать подходящую программу в Интернете, можно найти так много программ, что на выбор нужной именно вам уйдет уйма времени.
На данный момент я рекомендую программу FireFTP, потому что она обладает следующими преимуществами:
О она является дополнением к веб-браузеру Firefox и поэтому будет работать на любой платформе, на которой работает этот браузер;
О вызвать ее так же просто, как выбрать закладку;
О это одна из самых быстрых и простых в использовании FTP-программ из тех, которые мне когда-либо попадались.
Вы можете возразить: «Но я пользуюсь только Microsoft Internet Explorer, a FireFTP для него недоступна». Я бы на это ответил, что, раз уж вы решили заняться разработкой веб-страниц, вам все равно понадобится установить на свой компьютер все основные браузеры, что уже предлагалось сделать в начале этой главы.
ttДля установки FireFTP нужно пройти на веб-сайт http://fireftp.mozdev.org, используя Firefox, и щелкнуть на ссылке Download FireFTP (Скачать FireFTP). Программа занимает примерно 500 Кбайт и устанавливается очень быстро. После инсталляции нужно перезапустить Firefox, и тогда можно будет получить доступ к FireFTP из пункта меню Tools (Инструменты) (рис. 2.20).
t
t
Й @ | w:\lpmj.net* | growse
@
@D
-• ' Change
-a&lpmj.net >©10 >£Э11 ►ЁЭ12 >£Э13 1>СЭ14 (►Й15 »£)16
{►Q17 »Ol8 >Ql9 >C)20
>CJ4
Ю5
►СЭ7 t>CD8 ► 09 МСЭе
l£E3
Name-Size Date
Ljl-php2 KB...Mar 19 2011
plO.php4 KB...Mar 192011
(jll.php2 KB,.Mar 19 2011
Ql2.php2 KB...Mar 19 2011
LJl3.php2 KB...Mar 19 2011
pl4 .php2 KB.Mar 19 2011
Ljl5 .php3 KB..Mar 19 2011
[Jl6.php•3 KB...Mar 19 2011
L)17.php2 KB...Mar 19 2011
Qie.php2 KB...Mar 19 2011
£)l9.php2 KB...Mar 19 2011
□ 2.php2 KB Mar19 2011
Lj20.php3 KB...Mar19 2011
L)3.php3 KB...Mar19 2011
p 4php4 KB...Mar19 2011
Qs.php4 KB...Mar19 2011
p6,php3 KB...Mar19 2011
p7.php4KB ... Dec 32010
Qe .php4 KB...Mar19 2011
•ak3lpmj.net
►Do t>CDU
Ю12
>Di3
>£>16 МЭ17 ИСЭ18
►e)i9
Ю20
► 103
>C)4
>05
>06
>07
►08
>09
l>Qe
NameSee ... Date
Ljl.php2KB_Mar 19 2011
lO.php4KB_Mar 19 2011
j 11.php2 KB.Mar 19 2011
pl2 .php2KB_Mar 19 2011
Ql3 .php2 KBMar 19 2011
P 14. php2 KB...Mar 19 2011
Ljl5.php3 KB...Mar 19 2011
Lj l^.php3K6Mar 19 2011
J 17.php2 KB.Mar 19 2011
pl8 .php2 KBMar 192011
Lj 19.php2 КБMar 192011
U2.php2 KBMar 192011
|_]20.php3 КБMar 192011
Lj3.php3KBMar 192011
Q4.php4 KB..Mar 19 2011
p5.php4 KB..Mar 19 2011
L]6.php3KB . Mar 19 2011
[_j7.php4 KB...Mar 19 2011
Q8 .php4 KB-Mar 19 2011
*
Log [.Queue I
гоЫлпарп.«кп
disconnect ftfit Abort
220 robinixon.com FTP server (Version 6.00LS) ready.
P\
| loots Це)р
Р> robmnixoncom - FireFTP - Mozifla Firefox
File Edit View History Bookmarks Jools Help
<=о@oa efFchrome//fireftp/coritent/fireftp.xul
BinaryBinaryLocal listing: 62 object(sX 521 MB. Disk Space Available: 8Л GB
Binary
Binary
Рис. 2.20. FireFTP предоставляет полный доступ к FTP прямо из Firefox
Еще одной замечательной FTP-программой является FileZilla, доступная по адресу http://filezilla-project.org в версиях для Windows, Linux и Mac OS X 10.5 и выше.
Разумеется, если у вас уже есть FTP-программа, вы можете использовать ее.
Использование редактора программ
Хотя для редактирования HTML, РНР, JavaScript и CSS подходит любой текстовый редактор, существуют очень удобные приложения, специально предназначенные для редактирования текста программ, в которых имеются весьма полезные возможности, например цветная подсветка синтаксиса. Современные редакторы программ хорошо продуманы и могут еще до запуска программы на выполнение показывать места, в которых допущены синтаксические ошибки. Перейдя к использованию современного редактора, вы будете удивлены тому, что раньше обходились без него.
Есть множество хороших и доступных программ, но я остановил свой выбор на программе Editra, поскольку она распространяется бесплатно и доступна для Mac OS X, Windows и Linux/Unix (рис. 2.21). Программы можно загрузить, перейдя на веб-сайт http://editra.org и выбрав ссылку Download (Скачать) в левом верхнем углу страницы, где также можно найти и документацию на редактор.
Рис. 2.21. Окно программы Editra
Editra выделяет синтаксис, используя соответствующие цвета, что очень удобно. Можно поместить курсор за квадратными или фигурными скобками, и Editra подсветит соответствующую парную скобку, давая возможность определить лишние или недостающие скобки. В Editra предлагается также множество других свойств, облегчающих работу с текстом программы.
Использование IDE
При всех достоинствах специализированных редакторов программ, позволяющих повысить производительность труда программиста, они не могут сравниться с интегрированными средами разработки (Integrated Development Environment, IDE), предлагающими множество дополнительных возможностей, например проведение отладки и тестирования программ прямо в редакторе, а также отображение описаний функций и многое другое.
На рис. 2.22 показана популярная интегрированная среда разработки phpDesigner с программой РНР, которая загружена в главное окно, и с расположенным в правой части анализатором кода Code Explorer, в котором перечислены различные классы, функции и переменные, используемые в этой программе.
il phpDesigner 8 - [\\MAlNVDocument$\Pocumenb\Web$ites\rtebdevelopmentaokbookxom\WDCphfj?2^
5-Rod Goto Insert Fownat CSS JavaScript f Debug Project Tools Svn (St Highlighters View Window Help : Trial expires in 21 days
LQjJLLjAljfAJX^vT.-JyLgvaB/.^^jLriiV’iLl^^
j0»DCphp] :
CodeExpkiref
28472847284828492860285120522853285428552056284828492860285120522853285428552056if (Stcolor)if (Stsize) Stallif(Slabela)Stallif(Slegends)Stallif(Scolors)Stallif(Sbgfill)Stallif (Stcolor)if (Stsize) Stallif(Slabela)Stallif(Slegends)Stallif(Scolors)Stallif(Sbgfill)StallSurl"http: //chart. apie.google. com/chart?§j|§|§!";Surl"http: //chart. apie.google. com/chart?§j|§|§!";2857гл chart in-ag*285828592660Siitage ■» imagecreatefrompng ($url);28612862Sw * imagesx(Simage);2063Sh = imagesy{$ireage);га«4Sijcage2 * imagecreatetraecolor (Sw+ Sborder 2 ,2865Sh + Sborder * 2);2866Sclr * imagecolorallocate(Slmage,2867hexdec(sobstr($bcolor,0,2)),2868hexdecfsubstr(Sbcolor,2,2)),2869hexdec(sobstr(Sbcolor,4,2)));2870 imagefilledreatangle ($itr.age2, 0, 0, Sv + Sborder ' 2 ,2871Sh + Sborder * 2, Sclr);2872 imagecopy($iuage2, Siitage, Sborder, Sborder, 0, 0, Sw, Sh);2873imagedestroy(Simage);2874return Sin>4ge2;287528762877 function CurlGetContenta(Surl, Sagent) // Recipe 722857гл chart in-ag*285828592660Siitage ■» imagecreatefrompng ($url);28612862Sw * imagesx(Simage);2063Sh = imagesy{$ireage);га«4Sijcage2 * imagecreatetraecolor (Sw+ Sborder 2 ,2865Sh + Sborder * 2);2866Sclr * imagecolorallocate(Slmage,2867hexdec(sobstr($bcolor,0,2)),2868hexdecfsubstr(Sbcolor,2,2)),2869hexdec(sobstr(Sbcolor,4,2)));2870 imagefilledreatangle ($itr.age2, 0, 0, Sv + Sborder ' 2 ,2871Sh + Sborder * 2, Sclr);2872 imagecopy($iuage2, Siitage, Sborder, Sborder, 0, 0, Sw, Sh);2873imagedestroy(Simage);2874return Sin>4ge2;287528762877 function CurlGetContenta(Surl, Sagent) // Recipe 72^ Ы ■ & Mjncoona tUMj& £ AddUBerToOetStabte, Snrnax, jhroax, isaltl, $sajB-QAnayamfinder($*ofd, $ftename)=Э£AutoBad<ljnfcs($f3ename)£BBCode($rtmQ);S-£EloddUse»®yCootae{$act>on,$hande, Sexpre)B- BypassCapteheO■ B-fiCapsContrQK$*e*t,Stype)Й - A ChedcCapttha($captiha, $tofeen, $ea<U, $salt2)© Q Ched4r*s(Spagef Stmeout, $rurtrne)| E&- Q OoseSessonO! Ф~ ConvertCurrency($aniount, $from, $to)i S £ ]) ComerGrf($CDmer, {border,: e |]auitTrf($uito)i \ фЬ 43 CreateCeiPtha(N», length, $font Sfoldkr, Sa; Щ- Q CreateGoogteO«rt{$Wie, ftcdor, $tsae, $type,j ® - Q CreateUst($ttHTBf «start, $type, SbJet): (£)- ^ DeateSesaorKShandk, (pass, $iame, $onaJ); Ф CieateShortURl($uH, Vedrect, *ел, $Se)! ф- CuriGetContents($Lrl/ Sagent)j ф- © OrectDryOaKSpath)j ф Q ttsptovBngMap($tat, $»ong, Sroora, Sstyte,‘ I ®b<Q BnbeUfouTi»eveeo(*j, Swй^ {height, $K^i,; G9- Q EvakjateExpressondexpr}j Й FT.FNKSf, St, $b, $e)i : (Б- Д FetchFld<TStream^№xr*t)"3 FetrhWloPage(Sentry)!Г1йсГ@с EF @r 8f. ГЗт gc^ Ы ■ & Mjncoona tUMj& £ AddUBerToOetStabte, Snrnax, jhroax, isaltl, $sajB-QAnayamfinder($*ofd, $ftename)=Э£AutoBad<ljnfcs($f3ename)£BBCode($rtmQ);S-£EloddUse»®yCootae{$act>on,$hande, Sexpre)B- BypassCapteheO■ B-fiCapsContrQK$*e*t,Stype)Й - A ChedcCapttha($captiha, $tofeen, $ea<U, $salt2)© Q Ched4r*s(Spagef Stmeout, $rurtrne)| E&- Q OoseSessonO! Ф~ ConvertCurrency($aniount, $from, $to)i S £ ]) ComerGrf($CDmer, {border,: e |]auitTrf($uito)i \ фЬ 43 CreateCeiPtha(N», length, $font Sfoldkr, Sa; Щ- Q CreateGoogteO«rt{$Wie, ftcdor, $tsae, $type,j ® - Q CreateUst($ttHTBf «start, $type, SbJet): (£)- ^ DeateSesaorKShandk, (pass, $iame, $onaJ); Ф CieateShortURl($uH, Vedrect, *ел, $Se)! ф- CuriGetContents($Lrl/ Sagent)j ф- © OrectDryOaKSpath)j ф Q ttsptovBngMap($tat, $»ong, Sroora, Sstyte,‘ I ®b<Q BnbeUfouTi»eveeo(*j, Swй^ {height, $K^i,; G9- Q EvakjateExpressondexpr}j Й FT.FNKSf, St, $b, $e)i : (Б- Д FetchFld<TStream^№xr*t)"3 FetrhWloPage(Sentry)!Г1йсГ@с EF @r 8f. ГЗт gcVfWNlpoaxBents'poaembWebdtesVeMfiVfWNlpoaxBents'poaembWebdtesVeMfi
2847
2847
2848
2849
2860
2851
2052
2853
2854
2855
2056
2848
2849
2860
2851
2052
2853
2854
2855
2056
if (Stcolor)
if (Stsize) Stall
if(Slabela)Stall
if(Slegends)Stall
if(Scolors)Stall
if(Sbgfill)Stall
if (Stcolor)
if (Stsize) Stall
if(Slabela)Stall
if(Slegends)Stall
if(Scolors)Stall
if(Sbgfill)Stall
Surl"http: //chart. apie.google. com/chart?§j|§|§!";
Surl"http: //chart. apie.google. com/chart?§j|§|§!";
2857гл chart in-ag*
2858
2859
2660Siitage ■» imagecreatefrompng ($url);
2861
2862Sw * imagesx(Simage);
2063Sh = imagesy{$ireage);
га«4Sijcage2 * imagecreatetraecolor (Sw+ Sborder 2 ,
2865Sh + Sborder * 2);
2866Sclr * imagecolorallocate(Slmage,
2867hexdec(sobstr($bcolor,0,2)),
2868hexdecfsubstr(Sbcolor,2,2)),
2869hexdec(sobstr(Sbcolor,4,2)));
2870 imagefilledreatangle ($itr.age2, 0, 0, Sv + Sborder ' 2 ,
2871Sh + Sborder * 2, Sclr);
2872 imagecopy($iuage2, Siitage, Sborder, Sborder, 0, 0, Sw, Sh);
2873imagedestroy(Simage);
2874return Sin>4ge2;
2875
2876
2877 function CurlGetContenta(Surl, Sagent) // Recipe 72
2857гл chart in-ag*
2858
2859
2660Siitage ■» imagecreatefrompng ($url);
2861
2862Sw * imagesx(Simage);
2063Sh = imagesy{$ireage);
га«4Sijcage2 * imagecreatetraecolor (Sw+ Sborder 2 ,
2865Sh + Sborder * 2);
2866Sclr * imagecolorallocate(Slmage,
2867hexdec(sobstr($bcolor,0,2)),
2868hexdecfsubstr(Sbcolor,2,2)),
2869hexdec(sobstr(Sbcolor,4,2)));
2870 imagefilledreatangle ($itr.age2, 0, 0, Sv + Sborder ' 2 ,
2871Sh + Sborder * 2, Sclr);
2872 imagecopy($iuage2, Siitage, Sborder, Sborder, 0, 0, Sw, Sh);
2873imagedestroy(Simage);
2874return Sin>4ge2;
2875
2876
2877 function CurlGetContenta(Surl, Sagent) // Recipe 72
^ Ы ■ & Mjncoona tUMj
& £ AddUBerToOetStabte, Snrnax, jhroax, isaltl, $saj
B-QAnayamfinder($*ofd, $ftename)
=Э£AutoBad<ljnfcs($f3ename)
£BBCode($rtmQ)
;S-£EloddUse»®yCootae{$act>on,$hande, Sexpre)
B- BypassCapteheO
■ B-fiCapsContrQK$*e*t,Stype)
Й - A ChedcCapttha($captiha, $tofeen, $ea<U, $salt2)
© Q Ched4r*s(Spagef Stmeout, $rurtrne)
| E&- Q OoseSessonO
! Ф~ ConvertCurrency($aniount, $from, $to)
i S £ ]) ComerGrf($CDmer, {border,
: e |]auitTrf($uito)
i \ фЬ 43 CreateCeiPtha(N», length, $font Sfoldkr, Sa
; Щ- Q CreateGoogteO«rt{$Wie, ftcdor, $tsae, $type,
j ® - Q CreateUst($ttHTBf «start, $type, SbJet)
: (£)- ^ DeateSesaorKShandk, (pass, $iame, $onaJ)
; Ф CieateShortURl($uH, Vedrect, *ел, $Se)
! ф- CuriGetContents($Lrl/ Sagent)
j ф- © OrectDryOaKSpath)
j ф Q ttsptovBngMap($tat, $»ong, Sroora, Sstyte,
‘ I ®b<Q BnbeUfouTi»eveeo(*j, Swй^ {height, $K^i,
; G9- Q EvakjateExpressondexpr}
j Й FT.FNKSf, St, $b, $e)
i : (Б- Д FetchFld<TStream^№xr*t)
"3 FetrhWloPage(Sentry)!
Г1йсГ@с EF @r 8f. ГЗт gc
^ Ы ■ & Mjncoona tUMj
& £ AddUBerToOetStabte, Snrnax, jhroax, isaltl, $saj
B-QAnayamfinder($*ofd, $ftename)
=Э£AutoBad<ljnfcs($f3ename)
£BBCode($rtmQ)
;S-£EloddUse»®yCootae{$act>on,$hande, Sexpre)
B- BypassCapteheO
■ B-fiCapsContrQK$*e*t,Stype)
Й - A ChedcCapttha($captiha, $tofeen, $ea<U, $salt2)
© Q Ched4r*s(Spagef Stmeout, $rurtrne)
| E&- Q OoseSessonO
! Ф~ ConvertCurrency($aniount, $from, $to)
i S £ ]) ComerGrf($CDmer, {border,
: e |]auitTrf($uito)
i \ фЬ 43 CreateCeiPtha(N», length, $font Sfoldkr, Sa
; Щ- Q CreateGoogteO«rt{$Wie, ftcdor, $tsae, $type,
j ® - Q CreateUst($ttHTBf «start, $type, SbJet)
: (£)- ^ DeateSesaorKShandk, (pass, $iame, $onaJ)
; Ф CieateShortURl($uH, Vedrect, *ел, $Se)
! ф- CuriGetContents($Lrl/ Sagent)
j ф- © OrectDryOaKSpath)
j ф Q ttsptovBngMap($tat, $»ong, Sroora, Sstyte,
‘ I ®b<Q BnbeUfouTi»eveeo(*j, Swй^ {height, $K^i,
; G9- Q EvakjateExpressondexpr}
j Й FT.FNKSf, St, $b, $e)
i : (Б- Д FetchFld<TStream^№xr*t)
"3 FetrhWloPage(Sentry)!
Г1йсГ@с EF @r 8f. ГЗт gc
VfWNlpoaxBents'poaembWebdtesVeMfi
VfWNlpoaxBents'poaembWebdtesVeMfi
Рис. 2.22. При использовании такой IDE, как phpDesigner,разработка PHP-программы идет намного быстрее и проще
При разработке продукта в IDE можно установить контрольные точки, а затем запустить весь код целиком (или по частям), тогда его выполнение будет останавливаться в контрольных точках и вам будет предоставляться информация о текущем состоянии программы.
Помочь изучению процесса создания программ может то, что приводимые в данной книге примеры могут быть введены в IDE и запущены в этой среде, тогда вызывать браузер уже не понадобится.
6161Проверьте ваши знания
61
61
Существует несколько интегрированных сред разработки, доступных для различных платформ. Большинство из них являются коммерческими продуктами, но встречаются и бесплатные версии. В табл. 2.1 приведены некоторые наиболее популярные интегрированные среды разработки PHP-программ и URL-адреса, с которых их можно загрузить.
Таблица 2.1. Интегрированные среды разработки для РНР
IDE
URL-адрес загрузки
Цена
Win
Mac
Linux
Eclipse PDT
http ://eclipse.org/pdt/downloads/
Бесплатно
✓
✓
✓
Komodo IDE
http://activestate.com/Products/komodo_ide
$245
✓
✓
V
NetBeans
http://www.netbeans.org
Бесплатно
✓
✓
phpDesigner
http://mpsoftware.dk
$39
✓
-
-
PHPEclipse
http://phpeclipse.de
Бесплатно
✓
✓
V
PhpED
http://nusphere.com
$119
✓
-
Y
PHPEdit
http://phpedit.com
$119
✓
-
-
Zend Studio
http://zend.com/en/downloads
$395
✓
✓
✓
Выбор IDE может иметь сугубо индивидуальный характер, поэтому, если вы склоняетесь к использованию какой-нибудь одной среды, я советую загрузить не менее двух разновидностей этих программ, чтобы сначала опробовать их в работе. Все они либо бесплатны, либо имеют пробную версию, поэтому вы не понесете никаких затрат.
Вооружившись редактором программ или IDE, вы подготовитесь к переходу к главе 3, в которой начнется углубленное изучение РНР и совместной работы HTML и РНР, а также структуры самого языка РНР. Но перед тем, как перейти к этой главе, я предлагаю проверить вновь приобретенные знания, ответив на следующие вопросы.
Проверьте ваши знания
В чем разница между WAMP, МАМР и LAMP?
Что общего у 1Р-адреса 127.0.0.1 и URL-адреса http://localhost?
Для чего предназначена FTP-программа?
Назовите основной недостаток работы на удаленном веб-сервере.
Почему лучше воспользоваться редактором программ, а не обычным текстовым редактором?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 2».
2 Введение в PHP
В главе 1 о РНР говорилось как о языке, заставляющем сервер генерировать динамическую, потенциально разную выходную информацию при каждом запросе браузером веб-страницы. В данной главе начнется изучение этого простого, но мощного языка, которое продолжится в следующих главах и завершится в главе 6.
Я призываю вас выполнять разработку кода РНР в одной из интегрированных средств разработки (IDE), упомянутых в главе 2. Она поможет выявить опечатки, что существенно ускорит обучение, по сравнению с работой в менее функциональных редакторах.
Многие из IDE позволяют запускать код, рассматриваемый в этой главе, и изучать производимую им выходную информацию. Мы также изучим методы вставки кода РНР в файл HTML, чтобы иметь представление о внешнем виде выходной информации на веб-странице (то есть о том виде, в котором она в итоге предстанет перед пользователями).
В процессе создания веб-страницы будут представлять собой комбинацию РНР, HTML, JavaScript, инструкций MySQL и форматирования с помощью CSS. Кроме того, каждая страница может привести на другие страницы, предоставляя пользователям возможности щелкать на ссылках и заполнять формы. Хотя при изучении этих языков можно обойтись и без этих сложностей. На данном этапе нужно сконцентрироваться исключительно на написании PHP-кода и на достижении предсказуемости содержимого выходной информации или по крайней мере на умении разбираться в характере этой информации.
Включение РНР в HTML
По умолчанию в конце имен PHP-документов ставится расширение РНР. Когда веб-сервер встречает в запрашиваемом файле это расширение, он автоматически передает файл PHP-процессору. Веб-серверы имеют довольно широкий диапазон настроек, и некоторые веб-разработчики выбирают такой режим работы, при котором для разбора PHP-процессору принудительно передаются также файлы с расширениями НТМ или HTML. Обычно это связано с тем, что разработчики хотят скрыть факт использования РНР.
Программа на РНР отвечает за возвращение файла в чистом виде, пригодном для отображения в веб-браузере. В простейшем случае на выходе документа РНР
будет получаться только код HTML. Чтобы убедиться в этом, можно взять любой обычный HTML-документ, например файл index.html, сохранить его под именем i ndex. php, и он будет отображаться точно так же, как и исходный файл.
Вызов парсера РНР
Для запуска команд РНР нужно изучить новый тег. Его открывающая часть имеет следующий вид:
<?php
Первое, что может броситься в глаза, — незавершенность тега. Это обусловлено тем, что внутри тега могут помещаться целые фрагменты кода РНР. Они заканчиваются, только когда встречается закрывающая часть тега, имеющая следующий вид: ?>
Небольшая PHP-программа Hello World может иметь вид, показанный в примере 3.1.
Пример 3.1. Вызов РНР
<?php
echo "Hello world":
?>
Этот тег очень гибок в использовании. Некоторые программисты открывают тег в начале документа, а закрывают его в самом конце и выводят любой код HTML путем непосредственного использования команды РНР.
Другие программисты предпочитают помещать в эти теги как можно меньшие фрагменты кода РНР, и именно в тех местах, где нужно воспользоваться динамическими сценариями, а весь остальной документ составлять из стандартного кода HTML.
Сторонники последнего метода программирования зачастую аргументируют свой выбор тем, что такой код выполняется быстрее, а сторонники первого метода утверждают, что увеличение скорости настолько мизерное, что оно не может оправдать дополнительные сложности многочисленных вставок РНР в отдельно взятый документ.
По мере изучения языка вы, несомненно, определитесь в своих стилевых предпочтениях при создании разработок на РНР, но в целях упрощения примеров, приводимых в этой книге, я свел количество переходов между РНР и HTML к минимуму, в среднем к одному-двум переходам на один документ.
Кстати, существует и несколько иной вариант синтаксиса РНР Если поискать примеры PHP-кода в Интернете, то можно встретить код, где используется следующий синтаксис открывающего и закрывающего тегов: <? echo "Hello world": ?>
Несмотря на то что здесь неочевиден вызов PHP-парсера, это вполне приемлемый альтернативный синтаксис, который, как правило, также работает. Но я не
советую его использовать, поскольку он несовместим с XML и в настоящее время его применение не приветствуется (это значит, что он больше не рекомендуется и может быть удален в будущих версиях).советую его использовать, поскольку он несовместим с XML и в настоящее время его применение не приветствуется (это значит, что он больше не рекомендуется и может быть удален в будущих версиях).Если в файле содержится только код РНР, то закрывающий тег ?> можно опустить. Именно так и нужно делать, чтобы гарантировать отсутствие в файлах РНР лишнего пустого пространства (что имеет особую важность при написании объектно-ориентированного кода).Если в файле содержится только код РНР, то закрывающий тег ?> можно опустить. Именно так и нужно делать, чтобы гарантировать отсутствие в файлах РНР лишнего пустого пространства (что имеет особую важность при написании объектно-ориентированного кода).Примеры, приводимые в этой книгеПримеры, приводимые в этой книгеЧтобы вы не тратили время на набор примеров, приводимых в этой книге, все они заархивированы на веб-сайте http://lpmj.net, откуда можно загрузить файл 2nd_eci t i on_ examples .zip, щелкнув на ссылке Download Examples (Загрузить примеры), которая находится в верхней части страницы (рис. 3.1).Чтобы вы не тратили время на набор примеров, приводимых в этой книге, все они заархивированы на веб-сайте http://lpmj.net, откуда можно загрузить файл 2nd_eci t i on_ examples .zip, щелкнув на ссылке Download Examples (Загрузить примеры), которая находится в верхней части страницы (рис. 3.1).СЭ©||Фhttp://lpmj.nrt/j Eil« Edit View Favorites Xools HelpСЭ©||Фhttp://lpmj.nrt/j Eil« Edit View Favorites Xools HelpP ~ 8 С x | 1Щ Learning PHP, MySQL & Ja.P ~ 8 С x | 1Щ Learning PHP, MySQL & Ja.О REILLY’О REILLY’Learning РНР, MySQL & JavaScriptBy Robin Nixon (O'Reilly 2009, ISBN 978-0596157135)Learning РНР, MySQL & JavaScriptBy Robin Nixon (O'Reilly 2009, ISBN 978-0596157135)Home I >t I Buv Paperback / Kindle [ Download ExamplesHome I >t I Buv Paperback / Kindle [ Download ExamplesFind out for yourself why Learning PHP; MySQL & JavaScript is the number-one best-selling blockbuster that has been at the top of the charts for over two years worldwide, is the first result returned on PHP by Amazon US, UK and Canada, the first foreign language title on PHP returned on European Amazon websites, and in the top 10 foreign books on PHP on Amazon Japan and China!Learning PHP, MySQL A JavaScript will teach you how to create responsive, data-driven websites with the three central technologies of PHP, MySQL and JavaScript - whether or not you know how to program. This simple, streamlined guide explains how the powerful combination of PHP and MySQL provides a painless way to build modern websites with dynamic data and user interaction. You'll also learn how to add JavaScript to create rich Internet websites and applications, and how to use Ajax to handle background communication with a web server.Find out for yourself why Learning PHP; MySQL & JavaScript is the number-one best-selling blockbuster that has been at the top of the charts for over two years worldwide, is the first result returned on PHP by Amazon US, UK and Canada, the first foreign language title on PHP returned on European Amazon websites, and in the top 10 foreign books on PHP on Amazon Japan and China!Learning PHP, MySQL A JavaScript will teach you how to create responsive, data-driven websites with the three central technologies of PHP, MySQL and JavaScript - whether or not you know how to program. This simple, streamlined guide explains how the powerful combination of PHP and MySQL provides a painless way to build modern websites with dynamic data and user interaction. You'll also learn how to add JavaScript to create rich Internet websites and applications, and how to use Ajax to handle background communication with a web server.NNPHP, MySQLPHP, MySQL& JavaScript& JavaScriptContents of 2nd EditionContents of 2nd EditionIntroduction to Dynamic Web ContentSetting Up a Development ServerIntroduction to PHPExpressions and Control Flow in PHPPHP Functions and ObjectsPHP ArraysPractical PHPIntroduction to MySQLMastering MySQLAccessing MySQL Using PHPForm HandlingCookies, Sessions and AuthenticationExploring JavaScriptIntroduction to Dynamic Web ContentSetting Up a Development ServerIntroduction to PHPExpressions and Control Flow in PHPPHP Functions and ObjectsPHP ArraysPractical PHPIntroduction to MySQLMastering MySQLAccessing MySQL Using PHPForm HandlingCookies, Sessions and AuthenticationExploring JavaScript^1001^1001
советую его использовать, поскольку он несовместим с XML и в настоящее время его применение не приветствуется (это значит, что он больше не рекомендуется и может быть удален в будущих версиях).
советую его использовать, поскольку он несовместим с XML и в настоящее время его применение не приветствуется (это значит, что он больше не рекомендуется и может быть удален в будущих версиях).
Если в файле содержится только код РНР, то закрывающий тег ?> можно опустить. Именно так и нужно делать, чтобы гарантировать отсутствие в файлах РНР лишнего пустого пространства (что имеет особую важность при написании объектно-ориентированного кода).
Если в файле содержится только код РНР, то закрывающий тег ?> можно опустить. Именно так и нужно делать, чтобы гарантировать отсутствие в файлах РНР лишнего пустого пространства (что имеет особую важность при написании объектно-ориентированного кода).
Примеры, приводимые в этой книге
Примеры, приводимые в этой книге
Чтобы вы не тратили время на набор примеров, приводимых в этой книге, все они заархивированы на веб-сайте http://lpmj.net, откуда можно загрузить файл 2nd_eci t i on_ examples .zip, щелкнув на ссылке Download Examples (Загрузить примеры), которая находится в верхней части страницы (рис. 3.1).
Чтобы вы не тратили время на набор примеров, приводимых в этой книге, все они заархивированы на веб-сайте http://lpmj.net, откуда можно загрузить файл 2nd_eci t i on_ examples .zip, щелкнув на ссылке Download Examples (Загрузить примеры), которая находится в верхней части страницы (рис. 3.1).
СЭ©||Фhttp://lpmj.nrt/
j Eil« Edit View Favorites Xools Help
СЭ©||Фhttp://lpmj.nrt/
j Eil« Edit View Favorites Xools Help
P ~ 8 С x | 1Щ Learning PHP, MySQL & Ja.
P ~ 8 С x | 1Щ Learning PHP, MySQL & Ja.
О REILLY’
О REILLY’
Learning РНР, MySQL & JavaScript
By Robin Nixon (O'Reilly 2009, ISBN 978-0596157135)
Learning РНР, MySQL & JavaScript
By Robin Nixon (O'Reilly 2009, ISBN 978-0596157135)
Home I >t I Buv Paperback / Kindle [ Download Examples
Home I >t I Buv Paperback / Kindle [ Download Examples
Find out for yourself why Learning PHP; MySQL & JavaScript is the number-one best-selling blockbuster that has been at the top of the charts for over two years worldwide, is the first result returned on PHP by Amazon US, UK and Canada, the first foreign language title on PHP returned on European Amazon websites, and in the top 10 foreign books on PHP on Amazon Japan and China!
Learning PHP, MySQL A JavaScript will teach you how to create responsive, data-driven websites with the three central technologies of PHP, MySQL and JavaScript - whether or not you know how to program. This simple, streamlined guide explains how the powerful combination of PHP and MySQL provides a painless way to build modern websites with dynamic data and user interaction. You'll also learn how to add JavaScript to create rich Internet websites and applications, and how to use Ajax to handle background communication with a web server.
Find out for yourself why Learning PHP; MySQL & JavaScript is the number-one best-selling blockbuster that has been at the top of the charts for over two years worldwide, is the first result returned on PHP by Amazon US, UK and Canada, the first foreign language title on PHP returned on European Amazon websites, and in the top 10 foreign books on PHP on Amazon Japan and China!
Learning PHP, MySQL A JavaScript will teach you how to create responsive, data-driven websites with the three central technologies of PHP, MySQL and JavaScript - whether or not you know how to program. This simple, streamlined guide explains how the powerful combination of PHP and MySQL provides a painless way to build modern websites with dynamic data and user interaction. You'll also learn how to add JavaScript to create rich Internet websites and applications, and how to use Ajax to handle background communication with a web server.
N
N
PHP, MySQL
PHP, MySQL
& JavaScript
& JavaScript
Contents of 2nd Edition
Contents of 2nd Edition
Introduction to Dynamic Web Content
Setting Up a Development Server
Introduction to PHP
Expressions and Control Flow in PHP
PHP Functions and Objects
PHP Arrays
Practical PHP
Introduction to MySQL
Mastering MySQL
Accessing MySQL Using PHP
Form Handling
Cookies, Sessions and Authentication
Exploring JavaScript
Introduction to Dynamic Web Content
Setting Up a Development Server
Introduction to PHP
Expressions and Control Flow in PHP
PHP Functions and Objects
PHP Arrays
Practical PHP
Introduction to MySQL
Mastering MySQL
Accessing MySQL Using PHP
Form Handling
Cookies, Sessions and Authentication
Exploring JavaScript
^1001
^1001
Рис. 3.1. Примеры, приводимые в этой книге, можно просмотреть по адресу http://lpmj.net
Все примеры хранятся под номерами и сгруппированы по главам (например, example3-l. php). Кроме того, на сайте имеется архив examples. zip, в котором дополнительно есть папка named_examples, где можно найти все примеры, которые пред-
лагалось сохранять в файлах с конкретными именами (как в показанном далее примере 3.4, который нужно будет сохранить в файле с именем testt1l. php).
Структура РНР
В этом разделе будет рассмотрено довольно много основных положений. Разобраться во всем этом будет несложно, но я рекомендую проработать материал как можно тщательнее, поскольку он служит основой для понимания всей остальной книги. Как всегда, в конце главы будут заданы вопросы, с помощью которых можно будет проверить, насколько глубоко усвоен материал.
Использование комментариев
Существует два способа добавления комментариев к коду РНР. Первый, предусматривающий размещение в начале строки двух прямых слешей, превращает в комментарий отдельную строку:
// Это комментарий
Он хорошо подходит для временного исключения из программы строки кода, являющейся источником ошибок. Например, такой способ комментирования можно применить для того, чтобы скрыть строку кода до тех пор, пока в ней не возникнет необходимость:
// echo "X equals $х";
Такой комментарий можно также вставить сразу же после строки кода, чтобы описать ее действие:
$х += 10: // Увеличение значения $х на 10
Когда понадобится комментарий, состоящий из нескольких строк, нужно воспользоваться вторым способом комментирования, который показан в примере 3.2.
Пример 3.2. Многостоочный комментарий
<?php
/* Это область
многострочнс-: ксм*ентария.
которая не 6yze~
подвергаться /н’ернретации */
?>
Для открытия и закрытия комментария можно воспользоваться парами символов /* и */ практически в любом произвольно выбранном месте кода. Если не все, то большинство программистов используют эту конструкцию для временного превращения в комментарий целого неработоспособного раздела кода или такого раздела, который по тем или иным причинам нежелательно интерпретировать.
Типичной ошибкой является применение пар символов /* и */ для того, чтобы закомментировать большой фрагмент кода, который уже содержит закомментированную область, где используются эти же пары символов. Комментарии не могут быть вложенными друг в друга, поскольку PHP-интерпретатор не поймет, где заканчивается комментарий, и выведет на экран сообщение об ошибке. Но если вы используете редактор программ или интегрированную среду разработки с подсветкой синтаксиса, то ошибку такого рода нетрудно будет заметить.
Основной синтаксис
РНР —очень простой язык, уходящий своими корнями в языки С и Perl, но все же больше похожий на Java. Он очень гибок, но существует несколько правил, относящихся к его синтаксису и структуре, которые следует изучить.
Точки с запятыми
В предыдущих примерах можно было заметить, что команды РНР завершаются точкой с запятой:
$х += 10;
Возможно, чаще всего причиной ошибок, с которыми приходится сталкиваться при работе с РНР, становится забывчивость. Если не поставить эту точку с запятой, РНР вынужден будет рассматривать в качестве одной сразу несколько инструкций, при этом он не сможет разобраться в ситуации и выдаст ошибку синтаксического разбора — Parse error.
Символ $
Символ $ используется в разных языках программирования в различных целях. Например, в языке BASIC символ $ применялся в качестве завершения имен переменных, чтобы показать, что они относятся к строкам.
А в РНР символ $ должен ставиться перед именами всех переменных. Это нужно для того, чтобы PHP-парсер работал быстрее, сразу же понимая, что имеет дело с переменной. К какому бы типу ни относились переменные, к числам, строкам или массивам, все они должны выглядеть так, как показано в примере 3.3.
Пример 3.3. Три разновидности присваивания значений переменным
<?php
Smycounter = 1;
$mystring = "Hello":
Smyarray = arrayC'One". "Two". "Three"): ?>
Вот, собственно, и весь синтаксис, который нужно усвоить. В отличие от таких языков, как Python, в котором отношение к способам отступа текста программы и размещения кода очень строгое, РНР дает полную свободу использования (или игнорирования) любых отступов и любого количества пробелов по вашему усмотрению. В действительности же разумное использование того, что называется свободным
пространством, обычно поощряется (наряду с всесторонним комментированием), поскольку помогает разобраться в собственном коде, когда к нему приходится возвращаться по прошествии некоторого времени. Это помогает и другим программистам, вынужденным поддерживать ваш код.
Осмысление переменных
Понять, что такое переменные РНР, поможет простая метафора. Думайте о них, как о небольших (или больших) спичечных коробках! Именно как о спичечных коробках, которые вы раскрасили и на которых написали некие имена.
Строковые переменные
Представьте, что у вас есть коробок, на котором написано слово username (имя пользователя). Затем вы пишете на клочке бумаги Fred Smith и кладете эту бумажку в коробок (рис. 3.2). Этот процесс похож на присваивание переменной строкового значения:
$username = "Fred Smith":
Рис. 3.2. Переменные можно представить в виде спичечного коробка, содержащего какие-то предметы
Кавычки служат признаком того, что Fred Smith является строкой символов. Каждую строку нужно заключать либо в двойные, либо в одинарные кавычки (апострофы). Между этими двумя видами кавычек есть весьма существенное различие, которое будет рассмотрено далее.
Когда хочется посмотреть, что находится внутри коробка, вы его открываете, вынимаете бумажку и читаете, что на ней написано. В РНР подобное действие выглядит следующим образом:
echo $username:
Можно также присвоить содержимое другой переменной (то есть сделать ксерокопию бумажки и поместить ее в другой коробок):
$current_user = Susername:
Если вы стремитесь самостоятельно освоить работу с РНР, то можете попробовать вводить примеры, приводимые в этой главе, в интегрированную среду разработки (согласно рекомендациям, которые были даны в конце главы 2), чтобы тут же посмотреть на результаты, или же можете ввести код примера 3.4 в редактор программ (который также рассматривался в главе 2) и сохранить этот код в каталоге исходного источника документов вашего сервера под именем test1l. php.
Пример 3.4. Ваша первая РНР-программа
<?php // testl.php
$username = "Fred Smith":
echo Susername:
echo "<br />";
$current_user = Susername:
echo $current_user:
?>
Теперь эту программу можно запустить путем ввода в адресную строку браузера следующего адреса:
http://localhost/testl.php
nr --1~
Если в ходе установки веб-сервера (рассмотренной в главе 2) вы изменили назначенный V?» А « серверу порт на какой-нибудь другой, отличающийся от порта 80, вы должны поместить — — 9?*' номер этого порта в URL в этом и всех последующих примерах из этой книги. Например, если вы изменили порт на 8080, предыдущий URL приобретет следующий вид: http://localhost:8080/testl.php
Не забудьте об этом при тестировании других примеров из книги или при написании собственного кода.
Результатом запуска этого кода будет двойное появление имени Fred Smith, первое — в результате выполнения команды echo Susername, а второе — в результате выполнения команды echo $current_user.
Числовые переменные
Переменные могут содержать не только строки, но и числа. Пользуясь аналогией со спичечным коробком, сохранение в переменной Scount числа 17 будет эквивалентно помещению, скажем, 17 бусин в коробок, на котором написано слово count:
Scount = 17:
Можно также использовать числа с плавающей точкой (содержащие десятичную точку); синтаксис остается прежним:
Scount = 17.5:
Чтобы узнать о содержимом коробка, его нужно просто открыть и посчитать бусины. В РНР можно присвоить значение переменной Scount другой переменной или вывести его с помощью веб-браузера на экран, воспользовавшись командой echo.
Массивы
Массивы можно представить в виде нескольких склеенных вместе спичечных коробков. Например, нам нужно сохранить имена пяти футболистов одной команды в массиве Steam. Для этого мы склеим вместе боковыми сторонами пять коробков, запишем имена всех игроков на отдельных клочках бумаги и положим каждый клочок в свой коробок.
Вдоль всей верхней стороны склеенных вместе коробков напишем слово team (рис. 3.3). В РНР эквивалентом этому действию будет следующий код:
Steam = array( ' B i l1l’. 'Joe', 'Mike'. 'Chris'. 'Jim');
Рис. 3.3. Массив похож на несколько склеенных вместе спичечных коробков
Этот синтаксис несколько сложнее рассмотренных ранее инструкций. Код создания массива представляет собой следующую конструкцию:
аггауО;
с пятью строками внутри круглых скобок. Каждая строка заключена в одинарные кавычки.
Когда потребуется узнать, кто является игроком номер 4, можно воспользоваться следующей командой:
echo $team[3]; // Эта команда отображает имя Chris
Использование в предыдущем примере числа 3, а не 4 обусловлено тем, что первый элемент PHP-массива является, как правило, нулевым, поэтому номера игроков распределяются в интервале от 0 до 4.
Двумерные массивы
Диапазон использования массивов очень широк. Например, вместо выстраивания одномерных рядов коробков из них можно построить двумерную матрицу, а массивы могут иметь три и более измерения.
Чтобы привести пример двумерного массива, представим, что нужно отслеживать ход игры в крестики-нолики, для чего требуется структура данных, состоящая из девяти клеток, сгруппированных в квадрат 3 4 3. Чтобы представить это в виде спичечных коробков, вообразите себе девять коробков, склеенных в матрицу, состоящую из трех строк и трех столбцов (рис. 3.4).
Рис. 3.4. Многомерный массив, смоделированный с помощью коробков
Теперь для каждого хода можно класть в нужные коробки клочки бумаги с крестиком или ноликом. Чтобы сделать это в коде РНР. необходимо создать массив, содержащий три других массива, как в примере 3.5. в котором массив создается для отображения уже ведущейся игры.
Пример 3.5. Определение двумерного массив^
<?php
Soxo = array (аггау( ’ х’ . ” ,'о'),
arrayCo'. 'о', ’ х ’). arrayCx'. 'о', " ));
?>
Мы сделали еще один шаг к усложнению, но смысл его нетрудно понять, если усвоен основной синтаксис массива. Здесь три конструкции array!; вложены во внешнюю по отношению к ним конструкцию = .
Для возвращения в дальнейшем третьего элемента во второй строке этого >4асси- ва можно воспользоваться следующей PHP-командой, которая отобраз^гг с*о*вал х:
Не забывайте о том, что отсчет индексов массива (указателей на элементы внутри мас- ^ I сива) начинаются с нуля, а не с единицы, поэтому в предыдущей команде индекс [1] *\ Jo ссылается на второй из трех массивов, а индекс [2] — на третью позицию внутри этого нг*.' массива. Указанная выше команда вернет содержимое третьего слева и второго сверху коробка.
Как уже упоминалось, поддерживаются даже массивы с большей размерностью, получаемые путем простого создания большего количества вложенных друг в друга массивов. Но в данной книге массивы с размерностью больше двух рассматриваться не будут.
Подробнее массивы будут рассмотрены в главе 6.
Правила присваивания имен переменным
При создании PHP-переменных следует придерживаться следующих четырех правил.
О Имена переменных должны начинаться с буквы или с символа _ (подчеркивания).
О Имена переменных могут содержать только символы: a-z, A-Z, 0-9, и _ (подчеркивание).
О Имена переменных не должны включать в себя пробелы.
Если имя переменной нужно составить более чем из одного слова, то в качестве разделителя следует использовать символ подчеркивания (например, $user_name).
О Имена переменных чувствительны к регистру символов. Переменная $Hi gh_Score отличается от переменной $high_score.
Операторы
Операторы — это математические, строковые, логические команды и команды сравнения, такие как плюс, минус, умножить и разделить. Код РНР во многом похож на обычные арифметические записи. Например, в результате работы следующего оператора выводится число 8:
echo 6+2:
Перед тем как приступить к изучению возможностей РНР, следует уделить немного внимания изучению предоставляющих эти возможности различных операторов.
Арифметические операторы
Арифметические операторы проделывают вполне ожидаемую работу. Они применяются для выполнения математических операций. Их можно использовать для проведения четырех основных операций (сложения, вычитания, умножения и деления), а также для нахождения модуля (остатка от деления) и увеличения или уменьшения значения на единицу (табл. 3.1).
Таблица 3.1. Арифметические операторы
Оператор
Описание
Пример
+
Сложение
$j + 1
-
Вычитание
$j-6
*
Умножение
$j * 11
/
Деление
$j/4
%
Модуль (остаток от деления)
$j % 9
++
Инкремент (приращение)
++$j
--
Декремент (отрицательное приращение)
"$j
Операторы присваивания
Эти операторы используются для присваивания значений переменным. К ним относится самый простой оператор =, а также операторы +=, -= и т. д. (табл. 3.2). Оператор += вместо полного замещения находящегося слева значения добавляет к нему значение, которое находится справа от него. Итак, если переменная Scount имела начальное значение 5, то оператор
Scount += 1;
устанавливает значение Scount равным 6 точно так же, как более привычный оператор присваивания:
Scount = Scount + 1;
Таблица 3.2. Операторы присваивания
Оператор
Пример
Эквивалент
=
$j = 15
$j = 15
+=
$j+=5
$j = $j + 5
-=
$j "= 3
$j = $j - 3
*=
$j *= 8
$ j = $j * 8
/=
$j /= 16
$j = $j / 16
.—
$j •= $k
$j = $j • $k
%=
$j %= 4
$j = $j % 4
У строк есть собственный оператор, точка (.), который будет более подробно рассмотрен в пункте «Объединение строк» далее.
Операторы сравнения
Как правило, операторы сравнения используются внутри таких конструкций, как инструкция i f, в которых требуется сравнивать значения двух элементов. Например, если необходимо узнать, не достигло ли значение переменной, подвергающееся приращению, какого-то конкретного значения или не превышает ли значение другой переменной установленного значения и т. д. (табл. 3.3).
Таблица 3.3. Операторы сравнения
Оператор
Описание
Пример
==
Равно
$j == 4
!=
Не равно
« != 21
>
Больше чем
$j > з
<
Меньше чем
$j < 100
>=
Больше чем или равно
$j > = 15
<=
Меньше чем или равно
$j<=8
Учтите, что операторы = и == предназначены для разных действий. Если первый из них является оператором присваивания, то второй — оператором сравнения. Иногда в спешке даже более опытные программисты могут вместо одного из них поставить другой, поэтому будьте внимательны, используя эти операторы.
Логические операторы
Если логические операторы вам раньше не встречались, то поначалу они могут показаться чем-то необычным. Нужно представить, что вы делаете логические заключения на простом разговорном языке. Например, можно сказать самому себе: «Если время уже больше 12, но меньше 14 часов, значит, нужно пообедать». В РНР код для такого высказывания может выглядеть следующим образом:
if ($hour > 12 && Shour < 14) dolunchO:
Здесь набор инструкций для самого обеда помещен в функцию по имени do! unch, которую позже нужно будет создать. В этой инструкции отсутствует элемент then (тогда), поскольку его присутствие само собой разумеется.
Как видно из предыдущего примера, логический оператор обычно используется для объединения результатов работы двух операторов сравнения, показанных в предыдущем разделе. Результат работы одного логического оператора может служить входным значением для другого логического оператора («Если время уже больше 12, но меньше 14 часов, или же если в комнате пахнет жареным и тарелки уже стоят на столе...»). Как правило, если какое-то действие имеет истинное или ложное значение — TRUE или FALSE, оно может служить входным значением для логического оператора, который берет два истинных или ложных входных значения и выдает в качестве результата истинное или ложное значение. Логические операторы показаны в табл. 3.4.
Таблица 3.4. Логические операторыI
Оператор
Описание
Пример
&&
И
$j == 3 && $k == 2
and
Низкоприоритетное И
$j == 3 and $k == 2
II
ИЛИ
$ j<5 11$j > 10
or
Низкоприоритетное ИЛИ
$j < 5 or $j > 10
!
НЕ
! ($j == $k)
xor
Исключающее ИЛИ
$j xor $k
Заметьте, что оператор &&обычно взаимозаменяем с оператором and; то же самое справедливо и для операторов 11 и ог. Но у операторов and и or более низкий приоритет, поэтому в некоторых случаях, для того чтобы принудительно расставить приоритеты, могут понадобиться дополнительные круглые скобки. В то же время бывают случаи, когда применимы только операторы and или or, как в следующем предложении, использующем оператор or (объяснения даны в главе 10):
mysql_select_db($database) or die("HeB03M0«H0 выбрать базу данных"):
Наиболее непривычным из этих операторов является хог, предназначенный для операции исключающего И Л И , который возвращает истинное значение TRUE, если любое из входных значений истинно, и возвращает ложное значение FALSE, если оба они имеют значение TRUE или FALSE. Чтобы понять его работу, представьте, что хотите изобрести чистящее средство для дома. Как аммиак (ammonia), так и хлорка (bleach) обладают хорошими чистящими свойствами, поэтому нужно, чтобы ваше средство содержало одно из этих веществ. Но оба они не могут в нем присутствовать, поскольку их сочетание опасно. В РНР это можно представить в следующем виде:
$ingredient = Sammonia xor Sbleach:
В представленном фрагменте если любая из двух переменных, Sammonia или $bl each, имеет значение TRUE, то значение переменной Si ngredi ent также будет установлено в TRUE. Но если обе они имеют значение TRUE или значение FALSE, значение переменной Si ngredi ent будет установлено в FALSE.
Присваивание значений переменным
Синтаксис присваивания значения переменной всегда имеет вид переменная = значение. Для передачи значения другой переменной он имеет немного иной вид другая переменная = переменная.
Есть еще несколько дополнительных операторов присваивания, которые могут оказаться полезными. Например, нам уже встречался оператор:
$х += 10:
который предписывает РНР-нарсеру добавить значение, расположенное справа от него (в данном случае это значение равно 10), к значению переменной $х. Подобным образом можно вычесть значение:
$у -= 10:
Увеличение и уменьшение
значения переменной на единицу
Добавление или вычитание единицы является настолько часто встречающейся операцией, что РНР предоставляет для этого специальные операторы. Вместо операторов += и -= можно воспользоваться одним из следующих операторов:
++$х:
-$у:
В сочетании с проверкой (инструкцией i f) можно воспользоваться следующим кодом:
i f (++$х == 10) echo $х:
Этот код предписывает РНР сначала увеличить значение переменной $х на единицу, а затем проверить, не имеет ли она значение 10; если переменная имеет такое значение, его следует вывести на экран. Можно также потребовать от РНР увеличить значение переменной на единицу (или, как в следующем примере, уменьшить на единицу) после того, как ее значение будет проверено:
i f ($у-- == 0) echo $у;
что дает несколько иной результат. Предположим, что первоначальное значение переменной $у до выполнения оператора было равно нулю. Операция сравнения вернет результат TRUE, но после того, как она будет проведена, значение переменной $у будет установлено в -1. Тогда что же отобразит инструкция echo: 0 ил и -1 ? Попробуйте догадаться, а потом, чтобы подтвердить свою догадку, испытайте работу инструкции в PHP-процессоре. Поскольку такая комбинация операторов может вас запутать, ее можно применять только в качестве обучающего примера, но ни в коем случае не рассматривать в качестве приемлемого стиля программирования.
Короче говоря, когда именно увеличено или уменьшено на единицу значение переменной, до или после проверки, зависит от того, где помещен оператор инкремента или декремента, перед именем переменной или после него.
Кстати, правильный ответ на предыдущий вопрос таков: инструкция echo отобразит результат -1, потому что значение переменной $у было уменьшено на единицу сразу же после того, как к ней получила доступ инструкция i f, и до того, как к ней получила доступ инструкция echo.
Объединение строк
При объединении строк, когда к одной строке символов добавляется другая строка, используется символ точки (.). Самый простой способ объединения строк выглядит следующим образом:
echo "У вас " . Smsgs . " сообщений.":
Если предположить, что переменной $msgs присвоено значение 5, то эта строка кода выведет следующую информацию:
У вас 5 сообщений.
Так же, как с помощью оператора += можно добавить значение к числовой переменной, с помощью оператора .= можно добавить одну строку к другой:
Sbulletin .= Snewsflash;
В данном случае, если в переменной $ bu lletin содержится сводка новостей, а в переменной Snewsflash — экстренное сообщение, команда добавляет это сообщение к сводке новостей и теперь переменная $bul 1eti п включает в себя обе строки текста.
Типы строк
В РНР поддерживаются два типа строк, которые обозначаются типом используемых кавычек. Если требуется присвоить переменной значение текстовой строки, сохраняя ее точное содержимое, нужно воспользоваться одинарными кавычками (апострофами):
Sinfo = 'Предваряйте имена переменных символом $. как в данном примере: Svariable';
В данном случае переменной $i nfo присваивается каждый символ, находящийся внутри строки в одинарных кавычках. Если воспользоваться двойными кавычками, то РНР попытается вычислить Svari abl е и получить значение переменной.
В то же время, если требуется включить в состав строки значение переменной, используется строка, заключенная в двойные кавычки:
echo "За всю историю было Scount президентов США
Из этого следует, что данный синтаксис предлагает более простую форму объединения, в которой для добавления одной строки к другой не нужно использовать символ точки или закрывать и снова открывать кавычки. Этот прием называется подстановкой переменной. Можно заметить, что в некоторых приложениях он используется довольно часто, а в других не применяется вообще.
Изменение предназначения символов
Иногда в строке должны содержаться символы, имеющие специальное предназначение, которые могут быть неправильно интерпретированы. Например, следующая строка кода не будет работать, потому что вторая кавычка (апостроф), встреченная в слове sister ’ s, укажет PHP-парсеру на то, что достигнут конец строки. Следовательно, вся остальная часть строки будет отвергнута как ошибочная:
Stext = 'My sister's car is a Ford’ : /7 Ошибочный синтаксис
Чтобы исправить ошибку, нужно непосредственно перед вызывающим неоднозначное толкование символом кавычки добавить обратный слеш, чтобы заставить РНР рассматривать этот символ буквально и не подвергать его интерпретации:
Stext = 'My sisterVs car is a Ford':
Этот прием можно применить практически во всех ситуациях, где в противном случае РНР вернул бы ошибку, пытаясь интерпретировать символ. Например, следующая строка, заключенная в двойные кавычки, будет присвоена переменной без ошибок:
Stext = "My Mother always said V'Eat your greensV'.";
Кроме того, для вставки в строку различных специальных символов, например табуляции, новой строки и возврата каретки, могут применяться управляющие символы соответственно \t, \п и \г. Вот пример, в котором символы табуляции используются для разметки заголовка (они включены в строку исключительно для иллюстрации применения символа обратного слеша, поскольку существуют более подходящие способы разметки веб-страниц):
Sheading = "ДатаиИмяШлатеж";
Эти специальные символы, предваряемые символами обратного слеша, работают только в строках, заключенных в двойные кавычки. Если заключить предыдущую строку в одинарные кавычки, то вместо символов табуляции в ней будут отображены нелепые последовательности символов \t. Внутри строк, заключенных в одинарные кавычки, в качестве символов с измененным предназначением распознаются только измененный апостроф ( \') и сам измененный обратный слеш (\\).
Многострочные команды
Иногда нужно вывести из РНР большой объем текста, а использование нескольких инструкций echo (или print), заняло бы много времени и было неразумным. РНР предлагает два удобных средства, предназначенных для того, чтобы справиться с подобной ситуацией. Первое из них состоит в заключении в кавычки нескольких строк, как в примере 3.6. Переменным также можно присвоить значения способом, показанным в примере 3.7.
Пример 3.6. Инструкция echo, использующая несколько строк
<?php
Sauthor = "Альфред Ньюмен";
echo "Это заголовок
Это первая строка.
Это вторая строка.
Автор Sauthor.";
?>
Пример 3.7. Многострочное присваивание
<?php
Sauthor = "Альфред Ньюмен";
Stext = "Это заголовок
Это первая строка.
Это вторая строка.
Автор Sauthor.";
?>
В РНР можно также воспользоваться многострочной последовательностью, используя оператор <«, который обычно называют here-document ( «здесь документ» ) или, для краткости, heredoc. Он представляет собой способ указания строкового литерала, сохраняющего в тексте обрывы строк и другие пустые пространства (включая отступы). Его использование показано в примере 3.8.
Пример 3.8. Еще один вариант инструкции echo, использующей сразу несколько строк <?php
Sauthor = "Альфред Ньюмен";
echo «< END
Это заголовок
Это первая строка.
Это вторая строка.
- Автор 5Sauthor.
_END;
?>
Этот код предписывает РНР вывести все, что находится между двумя тегами _END, как будто все это является строкой, заключенной в двойные кавычки. Это означает, что разработчику можно, например, написать целый раздел HTML-кода прямо в коде РНР, а затем заменить конкретные динамические части переменными РНР.
Важно запомнить, что закрывающий тег END: должен появляться строго в начале новой строки и он должен быть единственным содержимым этой строки — к ней не разрешается добавлять даже комментарии (нельзя ставить даже одиночный пробел). Как только многострочный блок закрыт, можно снова воспользоваться тем же самым именем тега.
Запомните: используя heredoc-конструкцию <<<_END..._END;/ вы не должны добавлять « символы \п, чтобы отправить команду на перевод строки, достаточно просто нажать кла- S вишу Enter и приступить к набору новой строки. В отличие от других строк, заключенных в одинарные или двойные кавычки, внутри конструкции heredoc можно по своему усмотрению совершенно свободно пользоваться всеми одинарными или двойными кавычками, не изменяя их первоначального предназначения с помощью обратного слеша (\).
В примере 3.9 показано, как использовать этот же синтаксис для присваивания переменной многострочного значения.
Пример 3.9. Присваивание переменной многострочного значения
<?php
Sauthor = "Альфред Ньюмен":
Sout = <«_END
Это заголовок
Это первая строка.
Это вторая строка.
-/Автор 5Sauthor.
_END;
?>
После этого переменная Sout будет наполнена содержимым, размещенным между двумя тегами. Если не присваивать, а добавлять значение, то для добавления строки к значению переменной Sout вместо оператора = можно воспользоваться оператором .=.
Будьте внимательны, не ставьте точку с запятой сразу же за первым тегом END, поскольку она прервет многострочный блок еще до его начала и вызовет сообщение обошибке синтаксическогоэразбораа.—Parse error. Точку/с запятой нужноэставитьь
только после закрывающего тега END, хотя внутри блока можно свободно пользоваться точкой с запятой как обычным текстовым символом.
Кстати, тег END —лишь один из многих, я выбрал его для этих примеров, поскольку его использование где-нибудь еще в коде РНР маловероятно. Вы можете использовать по собственному усмотрению любой тег, например _SECTI0N1 или _ OUTPUT и т. д. И еще, чтобы отличать подобные теги от переменных или функций, обычно в начале их имени ставят знак подчеркивания; но если не хотите, можете им не пользоваться.
Многострочную разметку текста можно рассматривать как удобное средство, упрощающее чтение вашего кода РНР, поскольку как только текст отображается на веб-странице, вступают в силу правила форматирования HTML и пустые пространства скрываются (но имя переменной $author по-прежнему заменяется ее значением в соответствии с правилами вставки значений переменных).
Так, например, если загрузить эти примеры многострочного вывода в браузер, они не будут отображены в виде нескольких строк, потому что все браузеры рассматривают символы новой строки просто как пробелы. Но если воспользоваться свойством браузера, позволяющим просматривать исходный код, обнаружится, что все символы новой строки правильно расставлены и вывод появляется на нескольких строках.
Типы переменных
РНР относится к очень слабо типизированным языкам. Это значит, что переменные не требуют объявления перед своим использованием и что РНР всегда преобразует переменные в тот тип, который требуется для их окружения на момент доступа к ним.
Например, можно создать число, состоящее из нескольких цифр, и извлечь из него п-ю цифру, просто предположив, что это число является строкой. В следующем фрагменте кода (пример 3.10) перемножаются числа 12 345 и 67 890 и возвращается результат 838 102 050, который затем помещается в переменную Snumber.
Пример 3.10. Автоматическое преобразование числа в строку
<?php
Snumber = 12345 * 67890;
echo substr($number, 3, 1);
?>
Когда присваивается значение, Snumber является числовой переменной. Но во второй строке кода вызов значения этой переменной помещен в РНР-функцию substr, которая должна вернуть из переменной Snumber один символ, стоящий на четвертой позиции (не забывайте, что в РНР отсчет позиции начинается с нуля). Для выполнения этой задачи РНР превращает Snumber в строку, состоящую из девяти символов, чтобы функция substr могла получить к ней доступ и вернуть символ, в данном случае 1.
То же самое происходит при необходимости превратить строку в число и т. д. А в примере 3.11 переменной Spl присвоено строковое значение, которое затем
в третьей строке кода автоматически превращается в число с плавающей точкой, чтобы стать частью уравнения по вычислению площади круга, которое выводит значение 78,5398175.
Пример 3.11. Автоматическое преобразование строки в число
<?php
$pi = ”3.1415927";
Sradius = 5;
echo $pi * (Sradius * Sradius);
?>
На практике все это означает, что вам не стоит слишком волноваться за типы переменных. Им следует просто присвоить значения, имеющие для вас смысл, и РНР при необходимости их преобразует. Затем, если понадобится извлечь значение, их нужно просто запросить, например, с помощью инструкции echo.
Константы
Константы!, как и переменные, хранят информацию для последующего доступа, за исключением того, что они оправдывают свое название констант (постоянных). Иными словами, после определения констант их значения устанавливаются для всей остальной программы и не могут быть изменены.
К примеру, константа может использоваться для хранения местоположения корневого каталога вашего сервера (папки, содержащей основные файлы вашего веб-сайта). Определить такую константу можно следующим образом:
define("R00T_L0CATI0N”. "/us г/1 оса1/www/");
Затем для чтения содержимого константы нужно просто сослаться на нее, как на обычную переменную (но не предваряя ее имя знаком доллара):
Sdirectory = R00T_L0CATI0N;
Теперь, как только понадобится запустить ваш PHP-код на другом сервере с другой конфигурацией папок, нужно будет изменить только одну строку кода.
Нужно помнить о двух основных особенностях констант: перед их именами не нуж но ставить * символ $ (как перед именами обычных переменных) и их можно определить только с помощью функции define.
По общепринятому соглашению считается правилом хорошего тона использовать в именах констант буквы только верхнего регистра, особенно если ваш код будет также читать кто-нибудь другой.
Предопределенные константы
РНР поставляется в виде готового продукта, с десятками предопределенных констант, которые редко используют такие новички, как вы. Тем не менее существуют константы, известные как волшебные, которые могут оказаться для вас полезными с самого начала. У имен волшебных констант в начале и в конце всегда стоят два
символа подчеркивания, чтобы нельзя было случайно назвать одну из собственных констант уже занятым под эти константы именем. Подробности о волшебных константах приведены в табл. 3.5. Понятия, упомянутые в таблице, будут раскрыты в следующих главах.
Таблица 3.5. Волшебные константы РНР
Волшебная константа
Описание
_LINE_ __
Номер текущей строки в файле
_FILE_
Полное путевое имя файла. Если используется внутри инструкции include, то возвращается имя включенного файла. В версии РНР 4.0.2,_FILE_всегда содержит абсолютныйлпуть с раскрытымии символическими ссылками, а в предыдущих версиях при определенных обстоятельствах она может содержать относительный путь
_DIR_
Каталог файла. Если используется внутри инструкции include, возвращается каталог включенного файла. Такой же результат дает применение функции dirname(_FILE_). В этом имени каталога отсутствует замыкающий слеш, если только этот каталог не является корневым. (Добавлена в РНР 5.3.0)
_FUNCTION_
Имя функции. Начиная с РНР 5, возвращает имя функции, под которым она была объявлена (с учетом регистра символов). В РНР 4 возвращаемое значение всегда составлено из символов нижнего регистра. (Добавлена в РНР 4.3.0)
_CLASS_ __
Имя класса. Начиная с РНР 5, возвращает имя класса, под которым он был объявлен (с учетом регистра символов). В РНР 4 возвращаемое значение всегда составлено из символов нижнего регистра. (Добавлена в РНР 4.3.0)
_METHOD_
Имя метода класса. Возвращает имя метода, под которым он был объявлен (с учетом регистра символов). (Добавлена в РНР 5.0.0)
_NAMESPACE_
Имя текущего пространства имен (с учетом регистра символов).
Эта константа определена во время компиляции. (Добавлена в РНР 5.3.0)
Эти константы полезны при отладке, когда нужно вставить строку кода, чтобы понять, до какого места дошло выполнение программы:
echo "Это строка " . _LINE_ . " в файле " . _FILE_:
Эта команда выведет в веб-браузер текущую строку программы с указанием текущего файла, исполняемого в данный момент (включая путь к нему).
Различие между командами echo и print
Нам уже встречались разнообразные способы использования команды echo для вывода текста с сервера в браузер. В одних случаях выводится строковый литерал, в других сначала происходило объединение строк или вычисление значений переменных. Был также показан вывод, распространяющийся на несколько строк.
Но команде echo есть альтернатива, которой также можно воспользоваться: команда pri nt. Эти две команды очень похожи друг на друга, но print — это функция, воспринимающая единственный параметр, a echo — конструкция языка РНР.
В общем, команда echo работает при выводе обычного текста быстрее print, поскольку не является функцией и не устанавливает возвращаемое значение.
С другой стороны, поскольку она не является функцией, ее, в отличие от pri nt, нельзя использовать как часть более сложного выражения. В следующем примере для вывода информации о том, является ли значение переменной истинным (TRUE) или ложным (FALSE), используется функция pri nt, но сделать то же самое с помощью команды echo не представляется возможным, поскольку она выведет на экран сообщение об ошибке синтаксического разбора —Parse error:
$b ? print "TRUE" : print "FALSE":
Вопросительный знак является простым способом задать вопрос о том, какое значение имеет переменная $Ь, истинное или ложное. Команда, которая располагается слева от двоеточия, выполняется в том случае, если $Ь имеет истинное значение, а команда, которая располагается справа, выполняется, если $Ь имеет ложное значение.
Тем не менее в приводимых здесь примерах чаще всего используется команда echo, и я рекомендую применять именно ее до тех пор, пока вам при РНР-разработ- ке реально не потребуется задействовать функцию pri nt.
Функции
Функции используются для выделения блоков кода, выполняющих конкретную задачу. Например, если вам часто приходится искать какие-то данные и выводить их в определенном формате, то вполне разумно будет обратиться к функции. Код, выполняющий эту задачу, может занимать всего три строки, но пока вы не воспользуетесь функцией, необходимость вставлять этот код в программу десятки раз делает ее неоправданно большой и сложной. А если вы чуть позже захотите изменить формат вывода данных, помещение кода в функцию будет означать, что вам придется внести изменения только в одном месте программы.
Помещение кода в функцию не только сокращает размер исходного кода и делает его более удобным для чтения, но и дает дополнительные функциональные возможности (эта игра слов носит преднамеренный характер), поскольку функциям могут передаваться параметры, которые вносят изменения в характер их работы. Функции также могут возвращать значения вызывающему их коду.
Чтобы создать функцию, нужно ее объявить, как показано в примере 3.12.
Пример 3.12. Простое объявление функции
<?php
function longdate($timestamp)
{ return dateC'l F jS Y", Stimestamp);
}
?>
Эта функция использует в качестве входных данных отметку времени системы Unix (целое число, отображающее дату и время на основе количества секунд, прошедших с нуля часов 1 января 1970 года), а затем вызывает PHP-функцию date
с нужным форматом строки, чтобы вернуть дату в формате «Понедельник август 1 2016». Между стоящими после имени функции круглыми скобками может размещаться любое количество параметров, но для этой функции выбран прием только одного параметра. Весь код, который выполняется при последующем вызове функции, заключается в фигурные скобки.
Чтобы с помощью этой функции вывести сегодняшнюю дату, нужно поместить в свой код следующий вызов:
echo longdate(timeO):
В этом вызове для извлечения текущей отметки времени Unix и передачи ее только что созданной функции 1 ongdate, которая затем возвращает для отображения соответствующую строку команде echo, используется встроенная РНР-функция time. Если требуется вывести дату семнадцатидневной давности, нужно сделать следующий вызов:
echo 1ongdate[i ' те() - 17 * 24 * 60 * 60):
в котором функции I ongdate передается текущая отметка времени Unix, уменьшенная на количество секлил, которое прошло за 17 дней (17 дней х 24 ч * 60 мин * 60 с).
Функции могут также воспринимать несколько параметров и возвращать несколько результатов, используя технологию, которая будет рассмотрена в следующих главах.
Область видимости переменной
Если программа очень длинная, то с подбором подходящих имен переменных могут возникнуть трудности, но, программируя на РНР, можно определить область видимости переменной. Иными словами, можно, к примеру, указать, что переменная $temp будет использоваться только внутри конкретной функции, чтобы забыть о том, что она после возврата из кода функции применяется где-нибудь еще. Фактически именно такой в РНР является по умолчанию область видимости переменных.
В качестве альтернативы можно проинформировать РНР о том, что переменная имеет глобальную область видимости и доступ к ней может быть осуществлен из любого места программы.
Локальные переменные
Локальные переменные создаются внутри функции, и к ним имеется доступ только из кода этой функции. Обычно это временные переменные, которые используются до выхода из функции для хранения частично обработанных результатов.
Одним из наборов локальных переменных является перечень аргументов функции. В предыдущем разделе была определена функция, воспринимающая параметр по имени Stimestamp. Значение этого параметра действительно только в теле функции, за пределами этой функции его значение нельзя ни получить, ни установить.
Чтобы привести еще один пример локальной переменной, рассмотрим функцию longdate еще раз в немного измененном варианте (пример 3.13).
Пример 3.13. Расширенная версия функции longdate
<?php
function 1ongdate(Stimestamp)
{
$temp = dateC'l F jS Y", Stimestamp);
return "Дата: Stemp";
} ?>
В этом примере значение, возращенное функцией date, присваивается временной переменной Stemp, которая затем вставляется в строку, возвращаемую определяемой функцией. Как только будет осуществлен выход из функции, значение переменной Stemp удаляется, как будто она вообще никогда не использовалась.
Теперь, чтобы посмотреть на области видимости переменных в действии, изучим похожий код, показанный в примере 3.14. Здесь переменная Stemp была создана еще до вызова функции 1 ongdate.
Пример 3.14. Неудачная попытка получить доступ к переменной Stemp в функции longdate
<?php
Stemp = "Дата: "; echo longdate(timeO);
function longdate(Stimestamp)
{
return $temp . dateC'l F jS Y”, Stimestamp):
} ?>'
Поскольку переменная Stemp не была создана внутри функции 1 ongdate, а также не была передана ей в качестве параметра, функция longdate не может получить к ней доступ. Поэтому этот фрагмент кода выведет только дату без предшествующего ей текста. На самом деле сначала будет отображено сообщение об ошибке, предупреждающее об использовании неопределенной переменной (Noti се: Undefi ned variable: temp).
Причина в том, что по умолчанию переменные, созданные внутри функции, являются локальными для этой функции, а переменные, созданные за пределами любой функции, могут быть доступны только из того кода, который не входит в код ни одной из функций.
В примерах 3.15 и 3.16 показан ряд способов исправления кода, приведенного в примере 3.14.
Пример 3.15. Решить проблему можно путем переноса ссылки на переменную Stemp в ее локальную область видимости
<?php
Stemp = "Дата:
echo Stemp . longdate(timeO);
function longdate(Stimestamp)
return dateC'l F jS Y". Stimestamp);
}
?>
В примере 3.15 ссылка на Stemp перемещена за пределы функции. Эта ссылка появляется в той же области видимости, в которой была определена переменная.
Пример 3.16. Альте:>5’ ивное решение: передача Stemp в качестве аргумента
<?php
Stemp = "Дата:
echo 1ongdate($tenr. : 'T5());
function longcaie.S.eA*. Stimestamp)
{
return Stext . caie-." • F jS Y". Stimestamp):
} ?>
В примере 3.16 принято другое решение: передать значение переменной Stemp функции 1опдс~~е в качестве дополнительного аргумента. Функция longdate считывает это значение во временную переменную, которую она создает под именем Stext, и выводит желаемый результат.
Программисты часто допускают ошибку, забывая об области видимости переменных, поэтому, если не пошить принципы ее работы, это поможет в отладке некоторых весьма неочевидных ошибок программного кода. Достаточно сказать, что, если вы не объявили переменную каким- нибудь особым образом, ее область видимости ограничена локальным пространством: либо в пределах кода текущей функции, либо в пределах кода, не принадлежащего никаким функциям, в зависимое™ оттого, где эта переменная была впервые создана или где к ней впервые был получен доступ, внутри функции или за ее пределами.
Глобальные переменные
Бывают случаи, когда требуется переменная, имеющая глобальную область видимости, поскольку нужно, чтобы к ней имелся доступ из всего кода программы. К тому же некоторые данные могут быть настолько объемными и сложными, что их не захочется передавать функциям в качестве аргументов.
Чтобы объявить переменную, имеющую глобальную область видимости, используется ключевое слово global. Предположим, что существует некий способ входа пользователей на ваш веб-сайт, и нужно, чтобы весь код программы знал, с кем он имеет дело — с зарегистрированным пользователем или с гостем. Один из способов решения этой задачи заключается в создании глобальной переменной Si s_l ogged_i n: global $is_logged_in;
Теперь вашей функции входа в систему нужно лишь при удачной попытке входа на сайт присвоить этой переменной значение 1, а при неудачной попытке — значение 0. Поскольку переменная обладает глобальной областью видимости, доступ к ней может быть получен из любой строки кода вашей программы.
Но пользоваться глобальными переменными нужно с оглядкой. Я рекомендую создавать их только в том случае, если без них совершенно невозможно добиться нужного результата. Вообще-то программы, разбитые на небольшие фрагменты и отдельные данные, содержат меньше ошибок и проще в обслуживании. Если ваша программа состоит из нескольких тысяч строк кода (а когда-нибудь такое случится) и оказалось, что где-то глобальная переменная имеет неверное значение, то сколько времени уйдет на поиски кода, который присваивает ей это значение?
Кроме того, если используется слишком много глобальных переменных, возникает риск воспользоваться одним из их имен еще раз в локальном пространстве или, по крайней мере, полагать, что такая переменная применяется локально, хотя на самом деле она уже была объявлена в качестве глобальной. Из таких вот ситуаций и возникают разные непонятные ошибки.
Иногда я придерживаюсь соглашения о написании имен всех глобальных переменных в верх. не *р нем регистре (что совпадает с-Iрекомендациями о написании в этом же регистре имен тант), чтобы можно было с первого взгляда определить область видимости переменной.
Статические переменные
В пункте «Локальные переменные» выше было упомянуто, что значение переменной стирается сразу же после выхода из функции. Если функция вызывается многократно, она начинает свою работу со свежей копией переменной и ее прежние установки не имеют никакого значения.
Интересно, а что, если внутри функции есть такая локальная переменная, к которой не должно быть доступа из других частей программы, но значение которой желательно сохранять до следующего вызова функции? Зачем? Возможно, потому, что нужен некий счетчик, чтобы следить за количеством вызовов функции. Решение, показанное в примере 3.17, заключается в объявлении статическ < ой переменной.
Пример 3.17. Функция, использующая статическую переменную
<?php
function test О
{
static $count = 0;
echo Scount;
$count++;
} ?>
В этом примере в самой первой строке функции создается статическая переменная по имени $count, которой присваивается нулевое начальное значение. В следующей строке выводится значение переменной, а в последней строке это значение увеличивается на единицу.
При следующем вызове функции, поскольку переменная Scount уже была объявлена, первая строка функции пропускается и до нового увеличения значения переменной Scount отображается ее предыдущее значение.
Планируя использование статических переменных, следует учесть, что при их определении присвоить им результат какого-нибудь выражения невозможно. Они могут инициализироваться только предопредатенными значениями (пример 3.18).
Пример 3.18. Допустимые v’объявления статических переменных
<?php
static $int = 0:t i и ~
static Sint = 1+2:-e:: -..(вызовет ошибку синтаксического
:5з:::5 D5rse error))
static Sint = sqrtd--^':-£2С'"/2-/мэ
?>
Суперглобальные переменные
Начиная с версии РНР 4.1.0. стали доступны некоторые предопределенные перемен ныс. Они известны как суперглобальные переменные. Смысл этого названия заключается в том. что они предоставляются средой окружения РНР и имеют глобальную область видимости внутри программы, то есть доступны абсолютно из любого ее места.
В этих суперглобальных переменных содержится масса полезной информации о текущей работающей программе и ее окружении (табл. 3.6). Эти переменные имеют структуру ассоциативных массивов, которые будут рассмотрены в главе 6.
Таблица З.б. Суперглобальные переменные РНР
Имя суперглобальной переменно
Ее содержимое
$GLOBALS
Все переменные, которые на данный момент определены в глобальной области видимости сценария. Имена переменных служат ключами массива
$_SERVER
Информация о заголовках, путях, местах расположения сценариев. Элементы этого массива создаются веб-сервером, и это не дает гарантии, что каждый веб-сервер будет предоставлять какую-то часть информации или ее всю
$_GET
Переменные, которые передаются текущему сценарию методом HTTP GET
$_POST
Переменные, которые передаются текущему сценарию методом HTTP POST
$_FILES
Элементы, подгруженные к текущему сценарию методом HTTP POST
$_COOKIE
Переменные, переданные текущему сценарию посредством HTTP cookies*
$_SESSION
Переменные сессии, доступные текущему сценарию
$_REQUEST
Содержимое информации, переданной от браузера; по умолчанию S_GET, S_POST и $_СООК1Е
$_ENV
Переменные, переданные текущему сценарию методом environment
В именах всех суперглобальных переменных (кроме первой) присутствует один знак подчеркивания и используются только заглавные буквы, поэтому, чтобы избежать путаницы, не следует присваивать своим переменным имена, оформленные в таком же стиле.
Для иллюстрации порядка применения суперглобальных переменных рассмотрим часть той информации, которая может быть использована сайтами. Среди многой другой интересной информации, предоставляемой суперглобальными переменными, есть и URL-адрес той страницы, с которой пользователь был перенаправлен на текущую веб-страницу. Эта информация может быть получена следующим образом:
$came_from = $_SERVER[' HTTP_REFERRER’];
Как видите, ничего сложного. Если же пользователь зашел непосредственно на вашу страницу, к примеру набрав ее URL-адрес непосредственно в браузере, переменной $came_fгот будет присвоена пустая строка.
Суперглобальные переменные и проблемы безопасности Обратите внимание, что суперглобальные переменные часто используются злоумышленниками, пытающимися отыскать средства для атаки и вмешательства в работу вашего веб-сайта. Они загружают в S POST, $_GET или в другие суперглобальные переменные вредоносный код, например команды Unix или MySQL, которые, если вы по незнанию к ним обратитесь, могут разрушить или отобразить незащищенные данные.
Именно поэтому перед применением суперглобальных переменных их всегда следует подвергать предварительной обработке. Для этого можно воспользоваться PHP-функцией htmlentities. Она занимается преобразованием всех символов в элементы HTML. Например, символы «меньше чем» и «больше чем» (< и >) превращаются в строки < и >, то же самое делается для перевода в безопасное состояние всех кавычек, обратных слешей и т. д.
Поэтому более подходящий способ доступа к $_SERVER (и другим суперглобальным переменным) выглядит следующим образом:
$came_from = htm lentiti es($_SERVER['HTTP_REFERRER’]):
В этой главе были заложены надежные основы, необходимые для работы с РНР. В главе 4 мы приступим к практическому использованию изученного материала для построения выражений и управления ходом программы, иными словами, перейдем к реальному программированию.
Но перед изучением новой главы я рекомендую проверить свои знания, ответив на приведенные далее вопросы, чтобы убедиться в том, что вы полностью усвоили содержимое этой главы.
Проверьте ваши знания
Какой тег РНР служит основанием для того, чтобы приступить к интерпретации программного кода? Как выглядит краткая форма этого тега?
Какие два вида тегов используются для добавления комментариев?
Какой символ должен стоять в конце каждой инструкции РНР?
Какой символ используется в начале имен всех переменных РНР?
Что может храниться в переменных?
В чем разница между выражениями Svariable = 1 и Svariable == 1?
Как вы считаете, почему подчеркивание разрешено использовать в именах переменных ($current_user), а дефисы — нет (Scurrent-user)?
Чувствительны ли имена переменных к регистру букв?
Можно ли в именах переменных использовать пробелы?
Как преобразовать значение одного типа переменной в значение другого типа (скажем, строку в число)?
В чем разница между ++$j и $j++?
Являются ли взаимозаменяемыми операторы &&и and?
Как создается многострочный вывод: с использованием команды echo или присвоением многострочного значения?
Можно ли переопределить константу?
Как изменить исходное предназначение кавычки?
В чем разница между командами echo и pri nt?
Каково назначение функций?
Как сделать переменную доступной для всего кода РНР-программы?
Какими двумя способами можно передать всей остальной программе данные, которые были созданы внутри функции?
Что является результатом объединения строки с числом?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 3».
4
Выражения
и управление процессом выполнения программы в РНР
В предыдущей главе уже упоминались некоторые темы, которые более полно будут рассмотрены в данной главе, например выбор (ветвление) и создание сложных выражений. В главе 3 мне хотелось сконцентрировать внимание на наиболее общих вопросах синтаксиса и работы в РНР, но при этом невозможно было не затронуть темы более высокого уровня. А вот теперь можно преподнести вам основы, необходимые для полноценного использования всех сильных сторон РНР.
В этой главе будет заложен фундамент практики программирования на РНР и рассмотрены основные способы управления процессом выполнения программы.
Выражения
Начнем с базовой части любого языка программирования — выражения.
Выражение представляет собой сочетание значений, переменных. :■г.ераторов и функций, в результате вычисления которого выдается новое значение Оно знакомо всем, кто когда-либо имел дело с обыкновенной школьной алгебр: и:
у = 3(abs(2x) + 4)
что в РНР приобретает следующий вид:
$у = 3 * (abs(2*$x) + 4);
Возвращаемое значение (в данном случае у или $у) может быть числом . строкой или булевым значением (названным так в честь Джорджа Буля. антлн5!с:*:ого математика и философа XIX века). Первые два типа значений вам уже должны быть знакомы, потому я объясню, что такое третий тип.
Элементарное булево значение может быть либо истинным —~_Е. либо ложным —FALSE. Например, выражение 21 > г*(20 больше 9) является истинным ( - --), а выражение 5 == 6 (5 равно 6) — ложным (Г-_5Е). (Булевы, или лопгческие. операции могут быть объединены путем использования таких операторов, как 11.11Л И
и исключающее ИЛИ, то есть AND, OR и XOR, которые будут рассмотрены в этой главе.)
Обратите внимание на то, что для имен TRUE и FALSE я использую буквы верхнего регистра. Это обусловлено тем, что в РНР они являются предопределенными константами. При желании можно также применять и их версии, составленные из букв нижнего регистра, поскольку они также являются предопределенными константами. Кстати, версия, в которой задействуются буквы нижнего регистра, является более надежной, потому что РНР не допускает ее переопределения, а версия, использующая буквы верхнего регистра, может быть переопределена, и это нужно иметь в виду при импортировании чужого кода.
В примере 4.1 показаны некоторые простые выражения: два, о которых уже упоминалось, плюс еще два выражения. Для каждой строки выводится буква от а до d, за которой следуют двоеточие и результат выражения (тег <Ьг /> используется в HTML для переноса и разбивает выходную информацию на четыре строки).
Пример 4.1. Четыре простых булева выражения
<?php
echo "a:
["
. (20 > 9)
"]<br />"
echo "b:
["
. (5 == 6)
"]<br />"
echo "c:
["
. (1 == 0) .
"]<br />"
echo "d:
[”
. (1 == 1) .
"]<br />"
?>
Этот код выведет следующую информацию:
а: [1]
Ь: [] с: [] d: [1]
Обратите внимание на то, что результаты вычисления обоих выражений, а: и d:, являются истинными (TRUE), имеющими значение 1. А результаты вычисления выражений Ь: и с: ложны (FALSE) и вообще не показывают никакого значения, поскольку в РНР константа FALSE определена как NULL (ничто). Чтобы убедиться в этом, можно ввести код, показанный в примере 4.2.
Пример 4.2. Вывод значений TRUE и FALSE
<?php // test2.php
echo "a: [" TRUE "]<br />";
echo "b: [" . FALSE . "]<br />”; ?>
Этот код выведет следующую информацию:
а: [1]
Ь: []
Кстати, в некоторых языках константа FALSE может быть определена как 0 или даже как -1, поэтому в каждом языке ее определение стоит проверить.
Литералы и переменные
Простейшей формой выражения является литерал, означающий нечто, вычисляющееся само в себя, например число 73 или строка Hello. Выражение может также быть просто переменной, которая вычисляется в присвоенное этой переменной значение. Обе эти формы относятся к типам выражений, поскольку они возвращают значение.
В примере 4.3 показаны пять различных литералов, все они возвращают значения, хотя и разных типов.
Пример 4.3. Пять типов литералов
<?php
Smyname = "Brian";
Smyage = 37;
echo
"a: "
. 73
"<br />":
// Числовой литерал
echo
"b: "
"Hello"
"<br />";
// Строковый литерал
echo
"C: •"
. FALSE
"<br />";
// Литерал константы
echo
"d: "
. Smyname
., "<br />";
// Литерал строковой переменной
echo
"e: "
. Smyage
. "<br />":
// Литерал числовой переменной
?>
Как и ожидалось, в выходной информации вы увидите возвращаемое значение всех этих выражений, за исключением выражения с ;, результатом вычисления которого является FALSE и которое ничего не возвращает: а: 73 b: Hello с: d: Brian е: 37
Объединив простейшие выражения с операторами, можно создать более сложные выражения, итогом вычисления которых являются какие-нибудь полезные результаты.
При объединении присваивания или управляющей конструкции с выражениями получается инструк(ция. В примере 4.4 показано по одной инструкции каждого вида. В первой из них осуществляется присваивание результата выражения 366 - $day_number переменной $days_to_new_year, а во второй выводится приветственное сообщение, если выражение $days_to_new_year < 30 вычисляется как TRUE.
Пример 4.4. Выражение и инструкция
<?php
$days_to_new_year = 366 - $day_number; // Выражение i f ($days_to_new_year < 30)
{ echo "Скоро Новый Год!":// Инструкция
}
?>
Операторы
В РНР имеется множество мощных операторов, от арифметических, строковых и логических до операторов присваивания, сравнения и многих других операторов (табл. 4.1).
Таблица 4.1. Типы оперэторов РНР
Оператор
Описание
Пример
Арифметический
Элементарная математика
$а + $Ь
Для работы с массивом
Слияние массивов
$а + $Ь
Присваивания
Присваивание значений
$а = $Ь + 23
Поразрядный
Манипуляция битами в байте
12 А 9
Сравнения
Сравнение двух значений
$а < $Ь
Выполнения
Выполнение содержимого, заключенного в обратные кавычки
'Is -аГ
Инкремента/декремента
Добавление или вычитание единицы
$а++
Логический
Выполнение булевых сравнений
$а and $b
Строковый
Объединение строк
$а . $Ь
Различные типы операторов воспринимают разное количество операндов.
О Унарные операторы, такие как оператор)инкремента1($а++))или изменения знака числа (-$а), воспринимают только один операнд.
О Бинарные операторы, представленные большим количеством операторов РНР, включая операторы сложения, вычитания, умножения и деления, воспринимают два операнда.
0 Один трехкомпонентныIй оператор, имеющий форму х ? у : z. По сути, это состоящая из трех частей однострочная инструкция i f, в которой осуществляется выбор между двумя выражениями, зависящий от результата вычисления третьего выражения. Этот условный оператор воспринимает три операнда.
Приоритетность операторов
Если бы у всех операторов был один и тот же уровень приоритета, то они обрабатывались бы в том порядке, в котором встречались интерпретатору. Фактически многие операторы имеют одинаковый уровень приоритета, что и показано в примере 4.5.
Пример 4.5. Три эквивалентных выражения
1 +22+ +3 - 4 + 5
2 -44+ +5 + 3 + 1
5 +22 - 44+ 1 + 3
Из примера видно, что, несмотря на перестановку чисел (и предшествующих им операторов), результат каждого выражения имеет значение 7, поскольку у операторов «плюс» и «минус» одинаковый уровень приоритета. Можно проделать то же самое с операторами умножения и деления (пример 4.6).
Пример 4.6. Три выражения, которые также являются эквивалентными
L*2*3/4*5
2/4*5*3*1 5*2/44*3
В этом примере получаемое значение всегда равно 7,5. Но все меняется, когда в выражении присутствуют операторы с разными уровнями приоритета, как в примере 4.7.
Пример 4.7. Три выражения, в которых присутствуют операторы с разными уровнями приоритета
1 + 2 * 3 - 4 * 5 2-4*5*3 + 1 5 + 2- 4 + 1*3
Если бы не существовало приоритетности операторов, то в результате вычисления этих выражений получались бы числа 25, -29 и 12 соответственно. Но поскольку операторы умножения и деления имеют более высокий уровень приоритета по сравнению с операторами сложения и вычитания, вокруг частей выражения с их участием предполагается наличие скобок, и если их сделать видимыми, выражения будут выглядеть так, как показано в примере 4.8.
Пример 4.8. Три выражения, в которых отображены предполагаемые скобки
1 + (2 * 3) - (4 * 5)
2 - (4 * 5 * 3) + 1
5+2 - 4 + (1*3)
Очевидно, что РНР должен сначала вычислить подвыражения, заключенные в скобки, чтобы получились частично вычисленные выражения, показанные в примере 4.9.
Пример 4.9. Выражения после вычисления подвыражений в скобках
1 + (6) - (20)
2 - (60) + 1
5 + 2 - 4 + (3)
Окончательный результат вычисления этих выражений равен соответственно -13, -57 и 6 (что абсолютно отличается от результатов 25, -29 и 12, которые мы увидели бы при отсутствии приоритетности операторов).
Разумеется, исходную приоритетность операторов можно отменить, расставив собственные скобки, и принудительно получить результаты, показанные в самом начале, которые были бы получены в отсутствие приоритетности операторов (пример 4.10).
Пример 4.10. Принудительное выполнение вычислений справа налево
((1 + 2) * 3 - 4) * 5
(2 - 4) * 5 * 3 + 1
(5 +'2 - 4 + 1) * 3
Теперь, если скобки расставлены правильно, мы увидим значения 25, -29 и 12 соответственно.
В табл. 4.2 перечислены операторы РНР в порядке их приоритетности от самого высокого до самого низкого уровня.
Таблица 4.2. Операторы РНР, расположенные по уровню их приоритетности (сверху вниз)
Оператор(ы)
Тип
0
Скобки
++ --
Инкремент/декремент
!
Логический
*/%
Арифметические
+ -
Арифметические и строковые
<< >>
Побитовые
<<=>> = о
Сравнения
—1|—
Сравнения
&
Поразрядный (и ссылочный)
А
Поразрядный
1
Поразрядный
&&
Логический
II
Логический
? ;
Трехкомпонентный
= += -= *= /= .= %= &= != А= <
<= >
> =
Присваивания
and
Логический
xor
Логический
or
Логический
Взаимосвязанность операторов
Мы рассматривали обработку выражений слева направо, за исключением тех случаев, в которых вступала в силу приоритетность операторов. Но некоторые операторы могут также потребовать обработки справа налево. Направление обработки обусловливается взаимосвязанностью операторов.
Взаимосвязанность приобретает большое значение в тех случаях, когда вы явным образом не меняете приоритетности. В табл. 4.3 перечислены все операторы, имеющие взаимосвязанность справа налево.
Таблица 4.3. Операторы, имеющие взаимосвязанность справа налево
Оператор
Описание
NEW
Создание нового объекта
j
Логическое НЕ
Поразрядное НЕ
++ "
Инкремент и декремент
Продолжение &
Таблица 4.3 (продолжение)
Оператор
Описание
+ -
Унарный плюс и изменение знака числа
(■nt)
Преобразование в целое число
(double)
Преобразование в число с плавающей точкой
(string)
Преобразование в строковое значение
(array)
Преобразование в массив
(object)
Преобразование в объект
@
Подавление сообщения об ошибке
=
Присваивание
Рассмотрим оператор присваивания, показанный в примере 4.11, где всем трем переменным присваивается значение 0.
Пример 4.11. Оператор множественного присваивания
<?php
$1evel = Sscore = $time = 0;
?>
Такое множественное присваивание возможно только в том случае, если сначала вычисляется самая правая часть выражения, а затем процесс продолжается справа налево.
ИГ S
1
Новичкам следует научиться в процессе работы с РНР избегать потенциальных просчетов « в вопросах взаимосвязанности операторов и всегда принудительно задавать порядок вы?' числений, заключая подвыражения в круглые скобки. Это поможет и другим программистам, которые будут обслуживать ваш код, понять, что в нем происходит.
Операторы отношения
Операторы отношения проверяют значения двух операндов и возвращают логический результат, равный либо TRUE, либо FALSE. Существует три типа операторов отношения: операторы равенства, сравнения и логические операторы.
Операторы равенства
С оператором равенства == (двойным знаком равенства) мы уже не раз встречались в этой книге. Его не следует путать с оператором присваивания = (одинарным знаком равенства). В примере 4.12 первый оператор присваивает значение, а второй проверяет его на равенство.
Пример 4.12. Присваивание значения и проверка его на равенство
<?php
Smonth = "Март";
i f (Smonth == "Март") echo "Весна наступила";
?>
Как видно из примера, возвращая значение TRUE или FALSE, оператор сравнения позволяет проверять условия, используя инструкцию i f. Но это еще не все, поскольку РНР является языком со слабой типизацией. Если два операнда выражения равенства имеют разные типы, РНР преобразует их к тому типу, который имеет для него наибольший смысл.
К примеру, любые строки, составленные полностью из цифр, при сравнении с числами будут преобразованы в числа. В примере 4.13 переменные $а и $Ь являются двумя разными строками, и поэтому вряд ли стоило ожидать, что какая-то из инструкций i f выведет результат.
Пример 4.13. Операторы равенства и тождественности
<?php
$а = "1000";
$b = "+1000":
i f ($а == $b) echo "Iм;
if ($а === $b) echo "2";
?>
Но если запустить этот пример, то он выведет число, что означает, что результат вычисления первой инструкции i f является TRUE. Причина в том, что обе строки сначала конвертируются в числа, и 1000 имеет такое же числовое значение, что и +1000.
В отличие от первой, во второй инструкции i f используется оператор тождественности — тройной знак равенства, который удерживает РНР от автоматического преобразования типов. Поэтому переменные $а и $Ь сравниваются как строки и теперь считаются отличающимися друг от друга, поэтому на экран ничего не выводится.
Как и в случае с принудительным заданием уровня приоритетности операторов, если возникнут сомнения в том, будет ли РНР конвертировать типы операндов, для отмены такого поведения интерпретатора можно воспользоваться оператором тождественности.
Аналогично применению оператора равенства, для определения равенства операндов можно проверить их на неравенство, используя оператор неравенства !=. Пример 4.14 является измененным примером 4.13, в котором операторы равенства и тождественности были заменены противоположными им операторами.
Пример 4.14. Операторы неравенства и нетождественности
<?php
$а = "1000";
$b = "+1000";
if ($а != $b) echo "1":
i f ($а !== $b) echo "2";
?>
Как, наверное, и ожидалось, первая инструкция i f не выводит на экран число 1, потому что в коде ставится вопрос о неравенстве числовых значений переменных $а и $Ь. Вместо этого будет выведено число 2, поскольку вторая инструкция if ставит вопрос о нетождественности прежнего типа операндов переменных $а и $Ь, и ответом будет TRUE, потому что они не тождественны.
Операторы сравнения
Используя операторы сравнения, можно расширить круг проверок, не ограничивая его только равенством и неравенством. РНР предоставляет вам для этого операторы > (больше), < (меньше), >= (больше или равно) и <= (меньше или равно). В примере 4.15 показано использование этих операторов.
Пример 4.15. Четыре оператора сравнения
<?php
$а =2: $Ь = 3;
if($а>t$b) echo"$аatбольше $b<br />";
if($а<t$b) echo"$аауменьше $b<br />";
if($а>= $b)echo”$аatбольше или равно$b<br/>":
if($а<= $b)echo"$аapменьше или равно$Ь<Ьг/>";
?>
Этот пример, в котором переменная $а имеет значение 2, а переменная $Ь — значение 3, выведет на экран следующую информацию:
2 меньше 3
2 меньше или равно 3
Попробуйте самостоятельно запустить этот пример, меняя значения переменных $а и $Ь, чтобы увидеть результаты. Присвойте им одинаковые значения и посмотрите, что из этого получится.
Логические операторы
Логические операторы выдают истинные или ложные результаты, поэтому их также называют булевыми операторами. Всего имеется четыре таких оператора (табл. 4.4).
Таблица 4.4. Логические операторы
Логический оператор
Описание
AND
Возвращает истинное значение (TRUE), если оба операнда имеют истинные значения
OR
Возвращает истинное значение (TRUE), если любой из операндов имеет истинное значение
XOR
Возвращает истинное значение (TRUE), если один из двух операндов имеет истинное значение
NOT
Возвращает истинное значение (TRUE), если операнд имеет ложное значение, или ложное значение (FALSE), если он имеет истинное значение
Использование этих операторов показано в примере 4.16. Обратите внимание, что РНР требует использовать вместо слова NOT символ !. Кроме того, операторы могут быть составлены из букв нижнего или верхнего регистра.
Пример 4.16. Использование логических операторов
<?php
$а = 1: $Ь = 0:
echo ($а AND $b)"<brr/>”:
echo ($а or $b)"<brr />":
echo ($а XOR $b) . "<br />":
echo !$a<br />";
?>
Этот пример выводит на экран NULL, 1, 1, NULL. Это значит, что только вторая и третья инструкции echo получают в результате вычисления значение TRUE. (Следует помнить, что NULL, или ничто, отображает значение FALSE.) Такой результат получается, потому что оператору AND, чтобы вернуть значение TRUE, нужно, чтобы оба операнда имели истинное значение, а четвертый оператор проводит над значением переменной $а операцию NOT, превращая его из TRUE (значения, равного единице) в FALSE. Если есть желание поэкспериментировать, запустите этот код, присваивая переменным $а и $Ь разные значения, выбранные из 1 и 0.
Занимаясь программированием, следует помнить, что у операторов AND и OR более низкий vy J « уровень приоритета, чем у других версий этих операторов — && и 11. Поэтому в сложных выраженияхHболееDбезопасным будет, наверное, применение=операторов3 &&3 и 11.и II-
Использование в инструкции i f оператора OR может стать причиной непредвиденных проблем, поскольку второй операнд не будет вычисляться, если в результате вычисления первого операнда уже получено значение TRUE. В примере 4.17 функция getnext никогда не будет вызвана, если переменная Sfinished имеет значение 1.
Пример 4.17. Инструкция, использующая оператор 0R
<?php
if (Sfinished == 1 OR getnext0) == 1) exit; ?>
Если нужно, чтобы функция getnext вызывалась для каждой инструкции if, следует внести в код изменения, показанные в примере 4.18.
Пример 4.18. Изменения в инструкции if ... OR. гарантирующие вызов функции getnext <?php
$gn = getnextO;
if (Sfinished == 1 OR $gn == 1) exit;
?>
В этом случае код в функции getnext будет выполнен и возвращенное значение будет сохранено в переменной $gn еще до выполнения инструкции i f.
—”
Другое решение заключается в том, чтобы обеспечить выполнение функции getnext за счет чiу 4 « простой перестановки условий местами, поскольку тогда вызов функции будет появляться — —^ в выражении первым.
В табл. 4.5 показаны все допустимые варианты использования логических операторов. Следует заметить, что ! TRUE является эквивалентом FALSE, а 1FALSE — эквивалентом TRUE.
Таблица 4.5. Все логические выражения, допустимые в РНР
Входные данные
Операторы и результаты
а
b
AND
OR
XOR
TRUE
TRUE
TRUE
TRUE
FALSE
TRUE
FALSE
FALSE
TRUE
TRUE
FALSE
TRUE
FALSE
TRUE
TRUE
FALSE
FALSE
FALSE
FALSE
FALSE
Условия
Условия изменяют процесс выполнения программы. Они позволяют задавать конкретные вопросы и по-разному реагировать на полученные ответы. Условия играют важную роль при разработке динамических веб-страниц — основной цели использования РНР, поскольку они облегчают создание разных вариантов выводимой при каждом просмотре веб-страницы информации.
Существует три типа нециклических условных инструкций: i f, swi tch и ?. Называя их нециклическими, я имел в виду, что после действий, инициированных инструкцией, процесс выполнения программы продолжается, а при использовании циклических условных инструкций (которые еще предстоит рассмотреть) код выполняется снова и снова до тех пор, пока не будет соблюдено определенное условие.
Инструкция if
Процесс выполнения программы можно представить себе как езду на машине по однополосной магистрали. Эта магистраль большей частью прямолинейна, но иногда встречаются различные дорожные знаки, задающие направление движения.
Когда встречается инструкция i f, можно представить, что машина подошла к знаку объезда, предписаниям которого необходимо следовать, когда определенные условия вычисляются как TRUE. При этом вы съезжаете с магистрали и следуете по объездному пути до тех пор, пока не вернетесь снова на магистраль и не продолжите движение по исходному маршруту. Или же, если условие не вычисляется как TRUE, вы игнорируете объезд и продолжаете ехать по магистрали как ни в чем не бывало (рис. 4.1).
Содержимым условной инструкции if может быть любое допустимое РНР- выражение, включая равенство, сравнение, проверку на нуль и NULL и даже значения, возвращаемые функциями (как встроенными, так и созданными самостоятельно).
Действия, предпринимаемые при вычислении условия в TRUE, помещаются, как правило, в фигурные скобки { }. Но эти скобки можно опустить, если нужно выполнить всего одну инструкцию. Тем не менее, если всегда использовать фигурные скобки, можно избежать «охоты» на трудно отслеживаемые ошибки, возникающие, к примеру, когда к условной инструкции добавляется еще одна строка, но забывается о необходимости добавить фигурные скобки, из-за чего строка не вычисляет-
ся. (Учтите, что в целях экономии места и доходчивости материала, если в примерах, приводимых в книге, была всего одна исполняемая инструкция, я не следовал этому совету и опускал фигурные скобки.)
В примере 4.19 следует представить, что подошел конец месяца и нужно платить по всем счетам, поэтому вы проводите некоторые операции с банковским счетом.
Пример 4.19. Инструкция if, в которой используются фигурные скобки
<?php
if ($bank_balance < 100)
{
Smoney= 1000;
$bank_balance += Smoney;
} ?>
В этом примере проверяется, не стал ли баланс ниже $100 (или 100 единиц другой используемой вами валюты). Если баланс стал ниже этой суммы, вы платите сами себе $1000, а затем прибавляете их к балансу. (Хорошо бы так просто зарабатывать деньги!)
Если баланс счета в банке равен $100 или превышает эту сумму, условные инструкции игнорируются и процесс выполнения программы переходит на следующую строку кода (которая здесь не показана).
Некоторые разработчики предпочитают ставить первую фигурную скобку справа от условного выражения, а некоторые начинают с нее новую строку. В этой книге открывающая фигурная скобка располагается обычно на новой строке. Подойдет любой из этих вариантов, поскольку РНР позволяет оставлять на ваше усмотрение какие угодно свободные пространства (пробелы, символы новых строк и табуляции). Но код будет легче читаться и отлаживаться, если у каждого уровня условий будет свой отступ, сформированный с помощью символа табуляции.
Инструкция else
Бывают случаи, когда условие не вычисляется как TRUE, но вам не хочется сразу же продолжать выполнение основного кода программы, а вместо этого нужно сделать что-либо другое. В таком случае пригодится инструкция else. С ее помощью на вашей магистрали можно организовать второй объезд, показанный на рис. 4.2.
Если при использовании конструкции if. . .else условие вычисляется как TRUE, выполняется первая условная инструкция. Но если это условие вычисляется как FALSE, выполняется вторая условная инструкция. Для выполнения должна быть выбрана одна из этих двух инструкций, но обе сразу они не будут выполнены ни при каких условиях и обязательно будет выполнена хотя бы одна их них. Использование конструкции i f... el se показано в примере 4.20.
Пример 4.20. Конструкция if...e lse , в которой используются фигурные скобки <?php
if ($bank_balance < 100)
{
Smoney= 1000;
$bank_balance += Smoney;
} else
{
Ssavings += 50:
$bank_balance -= 50:
}
?>
Если в этом примере будет установлено, что в банке лежит более $100, выполняется инструкция el se, с помощью которой часть этих денег перемещается на ваш сберегательный счет.
Точно так же, как и у i f, если у инструкции el se есть только одна условная инструкция, фигурные скобки можно не ставить. (Хотя фигурные скобки рекомендуется использовать в любом случае. Во-первых, при их наличии легче разобраться в коде, а во- вторых, они облегчают последующее добавление инструкций к этому ветвлению.)
Инструкция elseif
Случается, что на основе последовательности условий нужно осуществить сразу несколько действий. Достичь желаемого результата можно, используя инструкцию el sei f. Можно предположить, что она похожа на инструкцию el se, за исключением того, что до кода условия вставляется еще одно условное выражение. Полноценная конструкция if. . .e1lseiIf.. .else показана в примере 4.21.
Пример 4.21. Конструкция i f . . .e1lseif. . .else, в которой используются фигурные скобки <?php if ($bank_balance < 100) {
Smoney= 1000:
$bank_balance += Smoney:
}
elseiif ($bank_balance > 200)
{
Ssavings += 100:
$bank_balance -= 100:
} else
{
Ssavings += 50:
$bank_balance -= 50;
} ?>
В этом примере инструкция else1if была вставлена между инструкциями if и el se. Она проверяет, не превышает ли баланс банковского счета сумму $200, и если превышает, принимается решение о том, что в этом месяце можно позволить себе положить на сберегательный счет $100.
Это все можно представить в виде набора объездов в нескольких направлениях (рис. 4.3).
Инструкция else завершает либо конструкцию if...else, либо конструкцию if...elseif...else. Если она не нужна, финальную инструкцию else можно опустить, но ни одна из этих инструкций не должна стоять перед инструкцией elseif, точно так же как ни одна инструкция elseif не должна стоять перед инструкцией if.
Количество используемых инструкций elseif не ограничено. Но по мере роста количества этих инструкций будет лучше, наверное, обратиться к инструкции swi tch, если, конечно, она отвечает вашим запросам. Именно ее мы сейчас и рассмотрим.
Инструкция switch
Инструкция switch применяется в тех случаях, когда у одной переменной или у результата вычисления выражения может быть несколько значений, каждое из кото-
Рассмотрим, например, управляемую кодом РНР систему меню, которая в соответствии с пожеланием пользователя передает отдельную строку коду основного меню. Предположим, что есть следующие варианты: Home, About, News, Logi п и Li nks, а переменная $page принимает одно из этих значений в соответствии с информацией, введенной пользователем.
Код реализации этого замысла с использованием конструкции i f .. .e1lseiIf.. .else может иметь вид, показанный в примере 4.22.
Пример 4.22. Многострочная инструкция if. . .e lseif <?php
if
(Spage ==
"Home")
echo
"Вы
выбрали
Ноте"
elseiif
(Spage ==
"About")
echo
"Вы
выбрали
About
elseiif
(Spage ==
"News")
echo
"Вы
выбрали
News"
elseiif
(Spage ==
"Login")
echo
"Вы
выбрали
Login
elseiif
(Spage ==
"Links")
echo
"Вы
выбрали
Links
?>
Код, в котором используется инструкция switch, показан в примере 4.23.
Пример 4.23. Инструкция switch
<?php
switch (Spage)
{
case "Home” :
echo "Вы выбрали Home":
break;
case "About":
echo "Вы выбрали About";
break;
case "News":
echo "Вы выбрали News";
break;
case "Login":
echo "Вы выбрали Login":
break;
case "Links":
echo "Вы выбрали Links": break;
} ?>
Как видите, переменная Spage используется только один раз — в начале инструкции switch. После этого все соответствия проверяются командой case. Когда найдено соответствие, выполняется его условная инструкция. Разумеется, в настоящей программе в этом месте будет применяться код отображения или перехода на страницу, а не простое сообщение пользователю о том, что именно он выбрал.
В инструкциях switch внутри команд case фигурные скобки не используются. Вместо этого инструкции начинаются с двоеточия и заканчиваются командой break. Тем не менее весь перечень команд case в инструкции switch заключается в фигурные скобки.
Прекращение работы инструкции switch
Если нужно, чтобы инструкция switch прекратила свою работу из-за выполнения условия, используется команда break. Эта команда предписывает РНР прекратить работу инструкции switch и перейти к выполнению следующей инструкции.
Если в примере 4.23 не расставить команды break и результат вычисления команды case, проверяющей условие Ноте, получится TRUE, будут выполнены все пять условных инструкций, следующих за командами case. Или же, если переменная $раде имела значение News, то, начиная с этого места, будут выполнены все оставшиеся команды case. Это сделано преднамеренно для расширения возможностей программирования, но в большинстве случаев не следует забывать ставить команду break во всех местах, где набор условных инструкций, следующих за командами case, завершает свою работу. Надо сказать, что случайный пропуск команд break является весьма распространенной ошибкой.
Действие по умолчанию
Типичным требованием для инструкции switch является переход к действию по умолчанию, если не будет выполнено ни одно из условий, содержащихся в командах case. Например, к коду меню, показанному в примере 4.23, можно непосредственно перед закрывающей фигурной скобкой добавить код, показанный в примере 4.24.
Пример 4.24. Инструкция default для добавления к примеру 4.23 default: echo "Нераспознанный выбор":
break;
Хотя здесь ставить команду break не требуется, поскольку default является заключительной внутренней инструкцией и процесс выполнения программы автоматически продолжится после закрывающей фигурной скобки, но если вы решите поставить инструкцию default выше этого места, ей определенно понадобится команда break, того чтобы процесс выполнения программы не затронул все стоящие ниже условные инструкции. Лучше перестраховаться и в конце этой инструкции всегда ставить команду break.
Альтернативный синтаксис
Открывающую фигурную скобку инструкции switch можно заменить двоеточием, а закрывающую фигурную скобку — командой endswitch (пример 4.25).
Такой вариант используется довольно редко, и здесь он упоминается на тот случай, если придется столкнуться с ним в коде, созданном кем-нибудь другим.
Пример 4.25. Альтернативный синтаксис инструкции switch
<?php
switch (Spage):
case "Home":
echo "Вы выбрали Home";
break;
// и т. д.
case "Links"
echo "Вы выбрали Links";
break;
endswitch;
?>
Оператор ?
Использование трехкомпонентного оператора ? позволяет избежать многословности инструкций if и else. Необычность этого оператора заключается в том, что он использует не два, как большинство других операторов, а три операнда.
В главе 3 уже состоялось краткое знакомство с этим оператором при выяснении разницы между pri nt и echo, где он приводился в качестве примера оператора, который хорошо работает с pri nt, но не работает с echo.
Оператору ? передаются выражение, которое он должен вычислить, и два выполняемых оператора: один для выполнения, когда результат вычисления выражения TRUE, а другой — когда FALSE.
В примере 4.26 показан код, который может использоваться для вывода предупреждения об уровне топлива в автомобиле на его панель приборов.
Пример 4.26. Использование оператора ?
<?php
echo Sfuel <= 1 ? "Требуется дозаправка" : "Топлива еще достаточно";
?>
Если топлива остается всего 1 галлон1 или меньше (иными словами, переменная Sfuel имеет значение, равное единице или меньше ее), этот оператор возвращает предыдущей команде echo строку «Требуется дозаправка». В противном случае он возвращает строку «Топлива еще достаточно». Значение, возвращаемое оператором ?, можно также присвоить какой-нибудь переменной (пример 4.27).
Пример 4.27. Присваивание условного результата оператора ? переменной
<?php
Senough = Sfuel <= 1 ? FALSE : TRUE; ?>
В этом примере переменной Senough будет присвоено значение TRUE только в том случае, если в баке более 1 галлона топлива, в противном случае ей будет присвоено значение FALSE.
Если вы считаете синтаксис оператора ? слишком запутанным, можете вместо него воспользоваться инструкцией i f, но о нем все равно нужно знать, поскольку он может встретиться в программном коде, созданном другим программистом. Чтение кода, в котором используется этот оператор, может быть сильно затруднено из-за частого использования в нескольких местах одной и той же переменной. Например, весьма популярен код такого вида:
Ssaved = Ssaved >= Snew ? Ssaved : Snew;
1 галлон (американский) = 3,79 л. — Примеч. ред.
Понять, что он делает, можно только после тщательного разбора:
Ssaved=Ssaved >= $new ?$saved$new;Ssaved=Ssaved >= $new ?$saved$new;// / Присваивание значенияя переменной1 $savedj
Ssaved=
Ssaved >= $new ?
$saved
$new;
Ssaved=
Ssaved >= $new ?
$saved
$new;
// Сравнение $savedj и Snew
/// Еслиa сравнение выдаетг истинныйА результат ...
/// .. . ейЙ присваиваетсяЯ текущеее значениее $savedd
// Если сравнение выдает ложный результат ...
// ... ей присваивается значение переменной $new
Это весьма компактный способ отслеживания самого большого значения, которое может встретиться в процессе выполнения программы. Самое большое значение содержится в переменной Ssaved и при поступлении нового значения сравнивается со значением переменной $new. Программисты, освоившие оператор ?, считают, что для таких коротких сравнений его удобнее применять, чем инструкции i f. Если этот оператор не используется для создания компактного кода, то он обычно применяется для принятия решений, умещающихся на одной строке, например для проверки того, установлено ли значение переменной, перед передачей ее функции.
Организация циклов
Компьютеры славятся своей способностью быстро и неутомимо повторять вычисления. Зачастую от программы требуется снова и снова повторять одну и ту же последовательность кода, пока не произойдет какое-нибудь событие, например ввод значения пользователем или достижение программой своего естественного окончания. Имеющиеся в РНР разнообразные структуры организации циклов предоставляют великолепные способы решения подобных задач.
Чтобы представить, как это работает, посмотрите на рис. 4.4. Он очень похож на метафору с магистралью, которая использовалась для иллюстрации работы инструкции i f, за исключением того, что у объезда также есть замкнутый участок, из которого машина может выйти только при соблюдении определенных программных условий.
Рис. 4.4. Представление цикла как части программы магистральной разметкиРис. 4.4. Представление цикла как части программы магистральной разметки
Рис. 4.4. Представление цикла как части программы магистральной разметки
Рис. 4.4. Представление цикла как части программы магистральной разметки
Циклы while
Давайте превратим автомобильную панель приборов из примера 4.26 в цикл, постоянно проверяющий уровень топлива при езде на машине, в котором используется инструкция цикла whi 1 е (пример 4.28).
Пример 4.28. Цикл while
<?php
Sfuel = 10;
while (Sfuel > 1)
{
// Продолжение поездки...
echo "Топлива еще достаточно";
} ?>
Вообще-то вы можете предпочесть выводу текста горящий зеленый сигнал, но суть в том, что любая разновидность позитивной индикации об уровне топлива помещается в цикл while. Кстати, учтите, что если вы запустите этот пример на выполнение, то он будет постоянно выводить строку до тех пор, пока вы не остановите работу браузера.
Здесь, как и в случае с инструкциями if, для хранения инструкций внутри цикла while используются фигурные скобки, если только в этом цикле не задействована всего одна инструкция.
В примере 4.29 показан еще один вариант использования цикла whi 1 е, в котором выводится таблица умножения на 12.
Пример 4.29. Цикл while для вывода таблицы умножения на 12
<?php
Scount = 1;
while (Scount <= 12)
{
echo "Число Scount. умноженное на 12, равно " . Scount * 12 . "<bг />"; ++$count;
} ?>
В этом примере переменной Scount присваивается начальное значение 1, а затем запускается цикл whi 1е, в котором используется выражение сравнения Scount <= 12. Этот цикл будет выполняться до тех пор, пока значение переменной не станет больше 1 12. - Данный код выведетследующий 1текст:
Число 1, У^умноженное на 1 12. pравно 12
Число 2, ytумноженное на 1 12. pравно 24
Число 3. Уумноженное на 1 12. pравно 36
и т. д.
Внутри цикла осуществляется вывод строки, а также значения переменной Scount, умноженного на 12. Чтобы упорядочить вывод, после всего этого использован тег <Ьг />, вызывающий переход на новую строку. Затем перед закрывающей фигурной скобкой, предписывающей РНР вернуться к началу цикла, значение переменной Scount увеличивается на единицу.
Теперь значение переменной Scount опять проверяется, чтобы узнать, не превышает ли оно число 12. Оно не превышает этого числа, но теперь оно равно 2, и после одиннадцати последующих прохождений цикла оно станет равно 13. Когда это произойдет, код, находящийся внутри цикла whi 1 е, будет пропущен и станет выполняться код, следующий за циклом, в данном случае это будет завершение программы.
При отсутствии оператора ++$count (вместо которого с таким же успехом может быть применен оператор $count++) этот цикл будет похож на первый, показанный в этом разделе. Он никогда не закончится и будет снова и снова выводить один и тот же результат 1x1 I 2.
Но есть и более изящный способ написания этого цикла, который должен вам понравиться. Посмотрите на код примера 4.30.
Пример 4.30. Укороченная версия примера 4.29
<?php
Scount = 0;
while (++$count <= 12)
echo "Число Scount. умноженное на 12. равно " . Scount * 12 . "<br />": ?>
В этом примере оператор ++Scount был удален из тела цикла while и помещен непосредственно в выражение условия цикла. Теперь РНР вычисляет значение переменной Scount в начале каждого прохода цикла (итерации) и, заметив, что перед именем переменной стоит оператор инкремента, сначала увеличивает значение переменной на 1 и только потом сравнивает его с числом 12. Следовательно, теперь переменной Scount присваивается начальное значение 0, а не 1, поскольку это значение увеличивается сразу же, как только происходит вход в цикл. Если оставить начальное значение, равное 1, то будут выведены результаты для чисел между 2 и 12.
Циклы do...while
Цикл do... whi 1е представляет собой небольшую модификацию цикла whi 1е, используемую в том случае, когда нужно, чтобы блок кода был исполнен хотя бы один раз, а условие проверялось только после этого.
В примере 4.31 показана модифицированная версия таблицы умножения на 12, в которой использован этот цикл.
Пример 4.31. Цикл do...while, используемый для вывода таблицы умножения на 12 <?php
Scount = 1;
do
echo "Число Scount. умноженное на 12. равно " . Scount * 12 . "<br />": while (++$count <= 12);
• ?>
Заметьте, что теперь мы вернулись к присваиванию переменной $count начального значения 1 (а не 0), потому что код выполняется сразу же, без увеличения значения переменной на 1. Во всем остальном этот код очень похож на показанный в примере 4.29.
Разумеется, если внутри цикла do... whi 1 е находится несколько инструкций, то не следует забывать ставить вокруг них фигурные скобки, как показано в примере 4.32.
Пример 4.32. Расширенная версия примера 4.31, использующая фигурные скобки
<?php
$count = 1;
do {
echo "Число Scount, умноженное на 12. равно " . Scount * 12;
echo "<br />":
} while (++$count <= 12);
?>
Циклы for
Цикл for, являющийся последней разновидностью инструкций цикла, к тому же еще и самый мощный из них, поскольку в нем сочетаются возможности установки значения переменных при входе в цикл, проверки соблюдения условия при каждом проходе цикла (итерации) и модификации значений переменных после каждой итерации.
В примере 4.33 продемонстрирована возможность вывода таблицы умножения с использованием цикла for.
Пример 4.33. Вывод ’ асл/цы умножения на 12 из цикла for
<?php
for (Scount = 1 ; Scojrt <= 12 ; ++Scount)
echo "Число $coi>rt. умноженное на 12, равно " . Scount * 12 . "<br />";
?>
Как видите, весь код сведен к одной инструкции for, в которой содержится одна условная инструкция. И вот что из этого получается. Каждая инструкция for воспринимает три параметра:
О выражение инициализации;
О выражение условия;
О выражение модификации.
Эти три выражения отделяются друг от друга точкой с запятой: for (выражение1 ; выражение2 ; выражениеЗ). В начале первой итерации выполняется выражение инициализации. В нашем коде таблицы умножения переменная Scount инициализируется значением 1. Затем при каждой итерации проверяется выражение условия (в данном случае Scount <= 12), и выход из цикла осуществляется только в том случае, если результат вычисления условия будет TRUE. И наконец, в завершение каждой итерации выполняется выражение модификации. В случае с таблицей умножения значение переменной Scount увеличивается на 1.
Эта структура в явном виде исключает любые требования по размещению управляющих элементов цикла в его собственном теле, освобождая его для инструкций, требующих циклического выполнения.
Если в теле цикла for содержится более одной инструкции, не забудьте воспользоваться фигурными скобками (пример 4.34).
Пример 4.34. Цикл for из примера 4.33 с добавлением фигурных скобок
<?php
for (Scount = 1 : Scount <= 12 ; ++$count)
{
echo "Число Scount, умноженное на 12, равно " . Scount * 12;
echo "<br />";
} ?>
Сравним условия, при которых следует использовать циклы for, с условиями, при которых следует использовать циклы whi 1 е. Цикл for явно создавался под отдельное значение, изменяющееся на постоянную величину. Обычно мы имеем дело с увеличивающимся значением — это похоже на то, что вам был передан перечень того, что выбрал пользователь, и от вас требуется обработать каждый его выбор по очереди. Но переменную можно видоизменять по вашему усмотрению. Более сложная форма инструкции for позволяет даже осуществлять со всеми тремя параметрами сразу несколько операций:
for (Si = 1. Sj = 1 ; Si + Sj < 10 ; Si++ . Sj++) {
II ...
}
Но новичкам использовать такую сложную форму не рекомендуется. Тут главное — отличать запятые от точки с запятой. Все три параметра должны быть отделены друг от друга точкой с запятой.
Несколько операторов внутри каждого параметра должны быть отделены друг от друга запятыми. Первый и третий параметры в предыдущем примере содержат по два оператора:
Si = 1. Sj = 1Si + Sj < 10 $i++ , Sj++Si = 1. Sj = 1Si + Sj < 10 $i++ , Sj++// Инициализация переменных Si и Sj
Si = 1. Sj = 1
Si + Sj < 10 $i++ , Sj++
Si = 1. Sj = 1
Si + Sj < 10 $i++ , Sj++
// Условие окончания работы цикла
// Модификация Si и Sj в конце каждой итерации
Главное, что следует уяснить из этого примера, — три секции параметров должны разделяться точкой с запятой, а не запятыми (которые должны использоваться только для разделения операторов внутри каждой секции параметров).
Тогда при каких условиях следует отдавать предпочтение инструкциям while перед инструкциями for? Когда ваше условие не зависит от простого изменения переменной на постоянной основе. Например, инструкция whi 1 е применятся в том случае, если нужно проверить, не введено ли какое-то вполне определенное значение или не возникла ли какая-то конкретная ошибка, и завершить1цикл сразу же, как только это произойдет.
Прекращение работы цикла
Прекратить работу цикла for можно точно так же, как и работу рассмотренной уже инструкции switch, — используя команду break. К примеру, это может понадобиться, когда одна из ваших инструкций вернет ошибку и продолжать выполнение цикла станет небезопасно.
Один из таких случаев может произойти, когда при записи файла возникнет ошибка, возможно, из-за нехватки места на диске (пример 4.35).
Пример 4.35. Запись файла, использующая цикл for с перехватом ошибки
<?php
$fp = fopen("text.txt". 'wb');
for ($j = 0 ; $j < 100 ; ++$j)
{
$written = fwrite($fp, "data");
if (Swritten = FALSE) break;
}
fclose(Sfp):
?>
Это наиболее сложный из всех ранее приведенных фрагментов кода, но вы уже готовы к его пониманию. Команды обработки файлов будут рассмотрены в одной из следующих глав, а сейчас нужно лишь знать, что в первой строке кода открывается файл text. txt для записи в двоичном режиме, а затем переменной $fр возвращается указатель на него, который в дальнейшем используется для ссылки на этот открытый файл.
Затем осуществляется 100 проходов цикла (от 0 до 99), записывающих строку data в файл. После каждой записи функция fwrite присваивает переменной Swritten значение, представляющее собой количество успешно записанных символов. Но если происходит ошибка, функция fwrite присваивает этой переменной значение FALSE.
Поведение функции fwrite облегчает коду проверку переменной Swritten на наличие значения FALSE, и если она имеет такое значение, код прекращает работу цикла и передает управление инструкции, закрывающей файл.
При желании улучшить код можно упростить строку
if (Swritten = FALSE) break;
за счет использования оператора NOT:
if (!Swritten) break:
Фактически пара инструкций, находящихся внутри цикла, может быть сокращена до одной:
if ( !fwrite(Sfp. "data")) break;
Но команда break обладает более широкими возможностями, чем можно было бы предположить, поскольку, если нужно прекратить работу кода, вложенного глубже
чем на один уровень, после команды break можно поставить число, показывающее, работу скольких уровней нужно прекратить, например:
break 2;
Инструкция continue
Инструкция continue немного похожа на команду break, только она предписывает РНР остановить процесс текущего цикла и перейти непосредственно к его следующей итерации, то есть вместо прекращения работы всего цикла осуществляется выход только из текущей итерации.
Этот прием может пригодиться в тех случаях, когда известно, что нет смысла продолжать выполнение текущего цикла и нужно сберечь процессорное время или избежать ошибки путем перехода сразу к следующей итерации цикла. В примере 4.36 инструкция conti nue используется для того, чтобы избежать ошибки деления на ноль за счет ее вызова в тот момент, когда переменная $j имеет значение 0.
Пример 4.36. Перехват ошибки деления на ноль с помощью инструкции continue
<?php
$j = 10;
while ($j > -10)
{
if ($j == 0) continue:
echo (10 / $j) . "<br />":
>}
Для всех значений переменной $j в диапазоне чисел между 10 и -10 за исключением 0 отображается результат деления числа 10 на значение переменной $j. Но для конкретного случая, когда значение $j равно 0, вызывается инструкция continue, и дальнейшее выполнение итерации сразу же пропускается с переходом к следующей итерации цикла.
Неявное и явное преобразование типов
РНР является языком со слабой типизацией, который позволяет объявлять переменную и ее тип путем простого использования этой переменной. При необходимости он также осуществляет автоматическое преобразование одного типа в другой. Этот процесс называется неявным преобразованием шипов.
Однако могут возникнуть ситуации, когда присущее РНР неявное преобразование типов станет совсем нежелательным действием. Рассматривая пример 4.37, обратите внимание на то, что входные данные для операции деления являются целыми числами. По умолчанию РНР осуществляет преобразование выходных данных к числу с плавающей точкой, чтобы получалось наиболее точное значение —4,66 и 6 в периоде.
Пример 4.37. Этот пример возвращает число с плавающей точкой
<?php
$а = 56;
$Ь = 12:
$с = $а / Sb: echo $с;
?>
Но что делать, если вместо этого нужно получить значение переменной $с в виде целого числа? Этого можно добиться разными способами, одним из которых является принудительное преобразование результата $а/$Ь в целое число путем использования оператора преобразования (int):
$с = (int) (Sa / Sb):
Этот способ называется явным преобразованием типов. Обратите внимание на то, что для обеспечения преобразования в целое число значения всего выражения это выражение помещено в круглые скобки. В противном случае преобразованию подверглось бы только значение переменной $а, что не имело бы никакого смысла, поскольку деление на значение переменной $Ь все равно вернуло бы результат в виде числа с плавающей точкой.
Можно провести явное преобразование значений в те типы, которые показаны в табл. 4.6, но обычно его можно избежать, используя преобразование за счет вызова одной из встроенных функций РНР. Например, для получения целочисленного значения можно использовать функцию intval. Этот раздел, как и многие другие в данной книге, предназначен в основном для того, чтобы помочь разобраться с чужим кодом, который может вам встретиться.
Таблица 4.6. Типы преобразований, доступных в РНР
Тип преобразования
|Описание
(int) (integer)
Преобразование в целое число путем отбрасывания десятичной части
(bool) (boolean)
Преобразование в булево значение
(float) (double) (real)
i Преобразование в число с плавающей точкой
(string)
Преобразование в строку
(array)
1 Преобразование в массив
(object)
1 Преобразование в объект
Динамическое связывание в РНР
Поскольку РНР является языком программирования и получаемая в результате его работы выходная информация может быть совершенно разной для различных пользователей, есть возможность запускать целый веб-сайт с одной веб-страницы, созданной с помощью РНР. При каждом щелчке пользователя на каком-нибудь элементе подробности могут отправляться назад той же веб-странице, которая будет принимать решение, что делать дальше, в соответствии с различными объектами cookie и/или данными сессии, которые могут быть сохранены.
Но, несмотря на возможность создания таким способом целого веб-сайта, этого делать не рекомендуется, поскольку исходный код будет все время разрастаться и приобретет громадные размеры по мере того, как ему придется принимать во внимание разнообразные действия пользователя.
Будет куда более благоразумно разделить разработку веб-сайта на несколько разных частей. Например, один автономный процесс будет заниматься подпиской на веб-сайт со всеми вытекающими отсюда проверками допустимости адреса электронной почты, незадействованности имени пользователя и т. д.
Второй модуль неплохо было бы создать для регистрации пользователей, предшествующей их допуску к основной части вашего веб-сайта. Затем можно создать модуль вывода сообщений, в котором пользователи могли бы оставлять свои комментарии, модуль, содержащий ссылки и полезную информацию, еще один модуль, позволяющий загружать на сайт фотографии, и т. д.
Как только будет создано средство для отслеживания действий пользователя на вашем веб-сайте, использующее объекты cookie или переменные сессии (оба этих средства будут более подробно рассмотрены в следующих главах), можно разделить веб-сайт на удобные секции PHP-кода, каждая из которых будет независима от других. Таким образом, вы существенно облегчите себе будущую разработку каждого нового свойства и обслуживание уже имеющихся.
Динамическое связывание в действии
Одним из наиболее популярных в настоящее время приложений, управляемых РНР, является платформа для ведения блогов WordPress (рис. 4.5). При ведении или чтении блога этого можно и не понять, но для каждой основной секции выделен свой основной PHP-файл, а огромное количество совместно используемых функций помещено в отдельные файлы, которые включаются основными РНР- страницами по мере необходимости.
Вся платформа держится на закулисном отслеживании сессии, поэтому вы вряд ли знаете о том, когда осуществляется переход от одной подчиненной секции к другой. Поэтому, если веб-разработчик хочет провести тонкую настройку WordPress, ему нетрудно найти конкретный файл, который для этого применяется, и выполнить его проверку и отладку, не теряя понапрасну времени на не связанные с ним части программы.
Когда в следующий раз будете использовать WordPress, проследите за адресной строкой своего веб-браузера, особенно при управлении блогом, и тогда вы сможете заметить обращения к некоторым из разнообразных PHP-файлов, которые используются в этом приложении.
В этой главе были рассмотрены обширные сведения, закладывающие основу для дальнейшего изучения материала книги. Теперь вы уже должны уметь составлять свои собственные небольшие PHP-программы. Но перед тем как перейти к следующей главе, посвященной функциям и объектам, можете проверить приобретенные знания, ответив на следующие вопросы.
Проверьте ваши знанияПроверьте ваши знания117117@e Jjdit 5fiew Hiftoiy bookmarks loots ttdpс x <s* 9" мхтгтhttp://myblog.com/blog/wp-admin/edit.php~йП1ВT|Google@e Jjdit 5fiew Hiftoiy bookmarks loots ttdpс x <s* 9" мхтгтhttp://myblog.com/blog/wp-admin/edit.php~йП1ВT|GoogleMyBlog |Visit Sfafe)MyBlog |Visit Sfafe)Write Manage CommentsWrite Manage CommentsProfileProfilePosts Pages LinksPosts Pages LinksCategories Tags Link Categories Media LibraryCategories Tags Link Categories Media LibraryWordPress 2.6.3 is available! Please notify the site administrator.WordPress 2.6.3 is available! Please notify the site administrator.Manage PostsManage PostsSearch PostsSearch PostsAll Posts Published (123)All Posts Published (123)Scheduled (I)Scheduled (I)ШШШШ339; Next»9; Next»£8 п .?£<-■Лит ho 7C.itugoriesО 9 hours from nowEco peko: Organic fashion for kidsRobinEco-friendly, Organic, RetailingAustralia, eko peko(3 15 hours agoHow ethical is eco fashion?RobinEthical, FairtradeColor, ColourО 2008/П /05Bamboo: The latestRobinEco-friendly, OrganicBamboo£8 п .?£<-■Лит ho 7C.itugoriesО 9 hours from nowEco peko: Organic fashion for kidsRobinEco-friendly, Organic, RetailingAustralia, eko peko(3 15 hours agoHow ethical is eco fashion?RobinEthical, FairtradeColor, ColourО 2008/П /05Bamboo: The latestRobinEco-friendly, OrganicBambooDeleteDeleteShow ай datesShow ай datesView all categoriesView all categoriesFilterFilterStatusStatusScheduledScheduledPublishedPublishedPublishedPublished
Проверьте ваши знания
Проверьте ваши знания
117
117
@e Jjdit 5fiew Hiftoiy bookmarks loots ttdp
с x <s* 9" мхтгтhttp://myblog.com/blog/wp-admin/edit.php~йП1ВT|Google
@e Jjdit 5fiew Hiftoiy bookmarks loots ttdp
с x <s* 9" мхтгтhttp://myblog.com/blog/wp-admin/edit.php~йП1ВT|Google
MyBlog |Visit Sfafe)
MyBlog |Visit Sfafe)
Write Manage Comments
Write Manage Comments
Profile
Profile
Posts Pages Links
Posts Pages Links
Categories Tags Link Categories Media Library
Categories Tags Link Categories Media Library
WordPress 2.6.3 is available! Please notify the site administrator.
WordPress 2.6.3 is available! Please notify the site administrator.
Manage Posts
Manage Posts
Search Posts
Search Posts
All Posts Published (123)
All Posts Published (123)
Scheduled (I)
Scheduled (I)
ШШ
ШШ
3
3
9; Next»
9; Next»
£8 п .?£<-■
Лит ho 7
C.itugories
О 9 hours from now
Eco peko: Organic fashion for kids
Robin
Eco-friendly, Organic, Retailing
Australia, eko peko
(3 15 hours ago
How ethical is eco fashion?
Robin
Ethical, Fairtrade
Color, Colour
О 2008/П /05
Bamboo: The latest
Robin
Eco-friendly, Organic
Bamboo
£8 п .?£<-■
Лит ho 7
C.itugories
О 9 hours from now
Eco peko: Organic fashion for kids
Robin
Eco-friendly, Organic, Retailing
Australia, eko peko
(3 15 hours ago
How ethical is eco fashion?
Robin
Ethical, Fairtrade
Color, Colour
О 2008/П /05
Bamboo: The latest
Robin
Eco-friendly, Organic
Bamboo
Delete
Delete
Show ай dates
Show ай dates
View all categories
View all categories
Filter
Filter
Status
Status
Scheduled
Scheduled
Published
Published
Published
Published
Рис. 4.5. Платформа WordPress, предназначенная для ведения блогов, написана на РНР
Проверьте ваши знания
Какие основные значения представлены ключевыми словами TRUE и FALSE?
Что представляют собой две самые простые формы выражений?
В чем разница между унарными, бинарными и трехкомпонентными операторами?
В чем заключается наилучший способ установки собственной приоритетности операторов?
Что означает понятие взаимосвязанности операторов?
Когда следует использовать оператор идентичности (===)?
Назовите три типа условных инструкций.
Какую команду можно использовать для пропуска текущей итерации цикла и перехода к следующей итерации?
Почему цикл for считается более мощным, чем цикл whi 1 е?
Каккинструкции ^ и1лг ' е интерпретируют1условныеЕвыражения, составленныее из разных типов данных?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 4».
Р Функции и объекты РНР
К основным требованиям к любому языку программирования относится наличие места для хранения данных, средств для направления процесса выполнения программы и ряда других мелочей, таких как вычисление выражений, управление файлами и вывод текста. РНР обладает всем этим, и вдобавок у него имеется облегчающий жизнь инструментарий наподобие инструкций el se и el sei f. Но даже если все это входит в наш набор инструментов, программирование может быть слишком нудным и утомительным занятием, особенно если регулярно будет возникать необходимость вновь и вновь набирать очень похожие друг на друга фрагменты кода.
И тут нам на помощь приходят функции и объекты. Нетрудно догадаться, что функция — это набор инструкций, который выполняет конкретную задачу и в дополнение к этому может вернуть какое-нибудь значение. Можно извлечь фрагмент кода, который используется более одного раза, поместить его в функцию и вызвать функцию по имени в тот момент, когда этот код нужно будет выполнить.
По сравнению с непрерывным линейным кодом у функций есть масса преимуществ.
О Экономия времени при наборе текста программы.
О Сокращение количества синтаксических и прочих ошибок программирования.
О Сокращение времени загрузки файлов программы.
О Сокращение времени выполнения, поскольку каждая функция компилируется только один раз, независимо от частоты ее вызовов.
О Возможность использовать функции как в рядовых, так в особенных случаях, поскольку они воспринимают аргументы.
Объекты являются дальнейшим развитием этой концепции. Объект объединяет одну или несколько функций и данные, которые ими используются, в единую структуру, которая называется классом.
В этой главе будет рассмотрено все, что касается использования функций, от их определения и вызова до различных способов передачи данных. Вооружившись этими знаниями, вы сможете создавать функции и использовать их в собственных объектах (в которых они будут упоминаться как методыJ).
Функции РНР
РНР поставляется с несколькими сотнями готовых к работе встроенных функций, превращающих его в язык с очень богатыми возможностями. Чтобы воспользоваться функцией, ее нужно вызвать по имени. Посмотрим, например, как работает функция print:
pri nt("print является функцией");
Круглые скобки сообщают РНР, что вы ссылаетесь на функцию. В противном случае будет считаться, что вы ссылаетесь на константу, и может быть выдано уведомление об использовании неопределенной константы:
Notice: Use of undefined constant fname - assumed 'fname'
за которым последует текстовая строка fname, согласно предположению, что вы, наверное, хотели поместить в код текстовую строку. (Ситуация запутается еще больше, если константа по имени fname будет существовать на самом деле и РНР в таком случае воспользуется ее значением.)
Собственно говоря, print является псевдофункцией, которая обычно называется конструк-
■, цией. Разница в том, что при ее использовании круглые скобки можно опустить: :
v print "print не требует использования круглых скобок";
А после любого другого имени вызываемой функции скобки нужно ставить всегда, даже если они останутся пустыми (в том случае, когда функции не передаются никакие аргументы).
Функции могут принимать любое количество аргументов, включая нулевое. Например, показанная ниже функция phpi nfo, отображает массу информации о текущей установке РНР и не требует никаких аргументов:
phpinfoO;
Результат вызова этой функции показан на рис. 5.1.
Функция phpinfo весьма полезна для получения информации о текущей установке РНР, но этой информацией могут воспользоваться и потенциальные злоумышленники. Поэтому никогда не оставляйте вызов этой функции в коде, подготовленном для работы в сети.
В примере 5.1 показан ряд встроенных функций, использующих один аргумент и более.
Пример 5.1. Три функции для работы со строками
<?php
echo sstrrevC' .dlrow сolleH "); // Реверсирование строки 1
echo sstr_repeat("Hip ". 2):// Повторение строки 1
echo sstrtoupperC'hooray!"):// Преобразование символов! Cстроки EвEверхний
// регистр
?>
Windows W ACER 61DtiMTROt (Windows /Home Premium EdBop Service Petit 1)Windows W ACER 61DtiMTROt (Windows /Home Premium EdBop Service Petit 1)SystemSystemC№rdgramite<x8e)lZeodtZeiid3erv«ieidpl>(i*eC№rdgramite<x8e)lZeodtZeiid3erv«ieidpl>(i*e(W)(W)Loaded Configuration №Loaded Configuration №Scan t№ dir for additional Jni 'Я®*'-Scan t№ dir for additional Jni 'Я®*'-AtchltetiiireAtchltetiiireConfigure commandConfigure commandConagurafionFRe C Windows (phpAlifPath ..::ConagurafionFRe C Windows (phpAlifPath ..::Budd Da® - , compaarBudd Da® - , compaarVlltuaroSedtoiyS«W ' ‘ 1VlltuaroSedtoiyS«W ' ‘ 1sqWeF "-«Mi-ixltpsquer ■ COVFatiCCI'dteaOledsqWeF "-«Mi-ixltpsquer ■ COVFatiCCI'dteaOledРис. 5.1. Информация, выводимая встроенной в РНР функцией phpinfoРис. 5.1. Информация, выводимая встроенной в РНР функцией phpinfoвцоо%вцоо%
Windows W ACER 61DtiMTROt (Windows /Home Premium EdBop Service Petit 1)
Windows W ACER 61DtiMTROt (Windows /Home Premium EdBop Service Petit 1)
System
System
C№rdgramite<x8e)lZeodtZeiid3erv«ieidpl>(i*e
C№rdgramite<x8e)lZeodtZeiid3erv«ieidpl>(i*e
(W)
(W)
Loaded Configuration №
Loaded Configuration №
Scan t№ dir for additional Jni '
Я®*'-
Scan t№ dir for additional Jni '
Я®*'-
Atchltetiiire
Atchltetiiire
Configure command
Configure command
ConagurafionFRe C Windows (phpAlifPath ..::
ConagurafionFRe C Windows (phpAlifPath ..::
Budd Da® - , compaar
Budd Da® - , compaar
VlltuaroSedtoiy
S«W ' ‘ 1
VlltuaroSedtoiy
S«W ' ‘ 1
sqWeF "-«Mi-ixltpsquer ■ COVFatiCCI
'dteaOled
sqWeF "-«Mi-ixltpsquer ■ COVFatiCCI
'dteaOled
Рис. 5.1. Информация, выводимая встроенной в РНР функцией phpinfo
Рис. 5.1. Информация, выводимая встроенной в РНР функцией phpinfo
вцоо%
вцоо%
В этом примере используются три функции для обработки строк, выводящие следующий текст:
Hello world. Hip Hip HOORAY!
Как следует из результата, функция strrev реверсирует порядок символов в строке, функция str_repeat дважды повторяет строку Hip (в соответствии с требованием второго аргумента), а функция strtoupper переводит буквы в слове hooray! в верхний регистр.
Определение функции
В общем виде для функции используется следующий синтаксис:
function имя_функции{[параметр [, ...]])
{
// Инструкции
}
В первой строке синтаксиса показано следующее:
О определение начинается со слова functi on;
О за ним следует имя, которое должно начинаться с буквы или символа подчеркивания, за которыми может следовать любое количество букв, цифр или знаков подчеркивания;
О наличие круглых скобок обязательно;
О к необязательному элементу относится один или несколько параметров, разделенных запятыми (что отображено с помощью квадратных скобок, не являющихся частью синтаксиса функции).
Имена функций нечувствительны к регистру использующихся в них букв, поэтому все следующие строки могут ссылаться на одну и ту же функцию print: PRINT, PrintnPrlnT.
С открывающей фигурной скобки начинаются инструкции, которые будут выполнены при вызове функции; они должны завершаться закрывающей фигурной скобкой, составляющей пару первой скобке. В составе этих инструкций должна быть одна или несколько инструкций return, заставляющих функцию прекратить выполнение и вернуть управление вызывавшему функцию коду. Если инструкция return продолжена каким-нибудь значением, то вызывающий код может его извлечь, что мы сейчас и увидим.
Возвращение значения
Рассмотрим простую функцию, преобразующую буквы чьих-нибудь полных имен в нижний регистр, а затем переводящую в верхний регистр первую букву каждого имени.
В примере 5.1 нам уже встречалась встроенная PHP-функция strtoupper. Для нашей текущей функции будет использована ее противоположность: функция strtolower:
$lowered = strtolower("люБОЕ нУжное Вам количество Букв и Знаков Пунктуации"): echo $lowered;
На выходе этого эксперимента получается следующая строка:
любое нужное вам количество букв и знаков пунктуации
Но нам не нужны имена, полностью состоящие из букв нижнего регистра, мы хотим, чтобы первые буквы были превращены в прописные. (Не будем в этом примере брать в расчет такие редкие имена, как Магу-Ann или Jo-En-Lai.) Нам и здесь сопутствует удача: РНР предоставляет также функцию ucfirst, которая переводит первую букву строки в верхний регистр:
Sucfixed = ucffirst("любое нужное вам количество букв и знаков пунктуации"); echo Sucfixed;
На выходе получается следующая строка:
Любое нужное вам количество букв и знаков пунктуации
Теперь мы можем внести свою первую лепту в конструирование программы: чтобы получить слово с первой прописной буквой, сначала для строки будет вызвана функция strtol ower, а затем будет вызвана функция ucfi rst. Для этого вызов функции strtolower будет вложен в вызов функции ucfi rst. Давайте посмотрим, зачем это делается, потому что нам важно понять порядок вычисления кода.
Если воспользоваться следующим простым вызовом функции print:
pri nt(5-8):
то сначала будет вычислено выражение 5-8 и на выходе будет получено число -3. (В предыдущей главе уже было показано, что РНР для отображения этого результата превращает его в строку.) Если выражение содержит функцию, то эта функция также в этот момент вычисляется:
pri nt(abs(5-8)):
Для выполнения этой короткой инструкции РНР совершает следующие действия.
1; Вычисляет 5-8, выдавая результат -3.
Использует функцию abs, превращая -3 в 3.
Превращает результат в строку и выводит его, используя функцию pri nt.
Иными словами, РНР вычисляет каждый элемент, начиная с самого внутреннего и заканчивая тем, который находится снаружи. То же самое происходит и при обработке следующего вызова:
ucIfirst(strtolower( "люБОЕ, нУжное Вам количество Букв и Знаков Пунктуации"))
РНР передает нашу строку функции strtol ower, а затем функции ucfi rst, выдавая следующий результат (который мы уже видели, когда вызывали функции отдельно друг от друга):
Любое, нужное вам количество букв и знаков пунктуации
Теперь определим функцию (показанную в примере 5.2), которая берет три имени и переводит их буквы в нижний регистр, после чего превращает первую букву в прописную.
Пример 5.2. При-ведение в порядок полного имени
<?php
echo fix_names("WILLIAM", "henry", "gatES");
function fix_names($nl, $n2, $n3)
{
$nl = ucfirst(st I rtolower($nl)):
$n2 = ucfirst(strtolower($n2)):
$n3 = ucfirst(strtolower($n3));
return $nl . " " . $n2 . " " . $n3;
}
?>
Пользователи часто забывают вовремя выключить режим Caps Lock, случайно ставят прописные буквы не там, где нужно, и даже вообще забывают о них, от чего вы тоже не застрахованы. В результате выполнения кода этого примера будет выведен следующий текст:
William Henry Gates
Возвращение массива
Выше была рассмотрена функция, возвращающая единственное значение. Но существуют также способы получения при выполнении функции сразу нескольких значений.
Самый подходящий из них возвращает эти значения в виде массива. В главе 3 уже было показано, что массив похож на связку переменных в одной строке. Использование массива для возвращения значений функции отображено в примере 5.3.
Пример 5.3. Возвращение нескольких значений в массиве
<?php
Snames = fix_names("WILLIAM", "henry", "gatES");
echo $names[0] . " " . $names[l] . " " . $names[2];
function fix_names($nl, $n2, $n3) {
$nl = ucfirst(strtolower($nl));
$n2 = ucfirst I (strtolower($n2)):
$n3 = ucfirst(strtolower($n3));
return array($nl, $n2. $n3);
} ?>
У этого метода есть преимущество, заключающееся в том, что все три имени содержатся по-отдельности, а не объединяются в одну строку, что дает возможность обращаться к любому пользователю просто по его имени или фамилии, не извлекая каждое имя из возвращаемой строки.
Передача по ссылке
Когда в РНР перед именем переменной ставится символ &, парсер знает, что тем самым передается не сама переменная, а ссылка на нее. Это понятие может быть для вас сложным, поэтому вернемся к метафоре со спичечным коробком, которая использовалась в главе 3.
Представьте, что вы не вынимаете клочок бумаги из коробка, не читаете то, что на нем написано, не копируете эту надпись на другой клочок бумаги, не возвращаете оригинал в коробок и не передаете копию функции, а просто привязываете нитку к исходному клочку бумаги и передаете функции второй конец этой нитки (рис. 5.2).
Теперь, чтобы найти данные, к которым она обращается, функция может проследовать по нитке. Таким образом исключаются все издержки на создание копии переменной, предназначенной только для того, чтобы в функции можно было воспользоваться ее значением. Более того, теперь функция может изменить значение переменной.
Значит, пример 5.3 можно переписать: передать ссылки на все параметры, чтобы после этого функция напрямую смогла внести в них изменения (пример 5.4).
Рис. 5.2. Представление ссылки в виде нитки, привязанной кзначению переменной
Пример 5.4. Возвращение значений из функции по ссылке
<?php
$al = "WILLIAM":
$а2 = "henry";
$аЗ = "gatES";
echo Sal . " " . $a2 . " " . $a3 . "<br />";
fix_names($al, $a2, $a3);
echo Sal . " " . Sa2 . " " . Sa3;
function fix_names(&$nl, &Sn2, &Sn3) {
Snl = ucfirst(strtolower($nl));
Sn2 = .ucfirst(strtolower(Sn2));
Sn3 = ucfirst(strtolower(Sn3));
} ?>
Вместо передачи строк непосредственно функции они сначала присваиваются в качестве значений переменным и выводятся на экран, чтобы посмотреть их состояние «до». Затем, как и раньше, вызывается функция, но теперь перед именем каждого параметра ставится символ &, предписывающий РНР передать функции только ссылки на значения переменных.
Теперь к переменным Snl, $п2 и $пЗ привязаны «ниточки», ведущие к значениям переменных Sal, $а2 и $аЗ. Иными словами, существует одна группа значений, но два набора имен переменных, позволяющих к ним обратиться.
Поэтому функции fi x_names нужно только присвоить новые значения переменным Snl, Sn2 и $пЗ, чтобы обновить значения переменных Sal, $а2 и $аЗ. В результате выполнения этого кода будут выведены следующие строки:
WILLIAM henry gatES
William Henry Gates
Как видите, в обеих инструкциях echo используются только значения переменных Sal, Sa2 и $аЗ.
При передаче значений по ссылке следует соблюдать особую осторожность. Если нужно сохранить исходное значение, сделайте копии ваших переменных, а затем передайте значения этих копий по ссылке.
Возвращение глобальных переменных
Можно также дать функции доступ к переменной, созданной за ее пределами, объявив ее глобальной прямо из тела функции. За ключевым словом global должно следовать имя переменной, тогда полный доступ к этой переменной можно будет получить из любой части вашего кода (пример 5.5).
Пример 5.5. Возвращение значений в глобальных переменных
<?php
Sal = "WILLIAM";
Sa2 = "henry";
Sa3 = "gatES";
echo Sal . " " . Sa2 . " " . Sa3 . "<br />";
fix_names();
echo Sal . " " . Sa2 . " " . Sa3;
function fix_names()
{
global$alSal=ucfirst(strtolower($al))
global$a2Sa2=ucfirst(strtolower($a2))
global$a3Sa3=ucfirst(strtolower($a3))
}
?>
Теперь уже не нужно передавать функции параметры, и она не должна их принимать. После объявления эти переменные остаются глобальными и доступными коду всей остальной программы, включая ее функции.
По возможности в целях сохранения как можно большей локальной видимости переменных следует возвращать массивы или использовать переменные, переданные по ссылке. В противном случае начнут утрачиваться некоторые преимущества использования функций.
И еще раз об области видимости переменных
Кратко напомню те сведения, которые были получены при изучении главы 3.
О Локальные переменные доступны лишь из той части кода, в которой они были определены. Если это произошло за пределами функции, доступ к переменным будет возможен из всего кода, находящегося за пределами функций, классов и т. д. Если переменная была определена внутри функции, значит, доступ к ней
может получить только код этой функции и ее значение теряется при выходе из функции.
О Глобальные переменные доступны из любых частей вашего кода.
0 Статические переменные доступны только внутри функции, в которой они были объявлены, но при этом они сохраняют свое значение в процессе многократных вызовов функции.
Включение и запрос файлов
По мере приобретения навыков программирования на РНР вы, скорее всего, приступите к созданию библиотеки, состоящей из функций, которые, по вашему мнению, смогут пригодиться в будущем. Кроме того, наверное, вы начнете пользоваться библиотеками, созданными другими программистами.
Копировать эти функции и вставлять их в свой код не имеет никакого смысла. Можно сохранить эти функции в отдельных файлах и воспользоваться командами для их извлечения. Для этого существуют две команды: i ncl ude (включить) и requi re (затребовать).
Инструкция include
При использовании инструкции include можно потребовать у РНР извлечения конкретного файла и загрузки всего его содержимого. Это равносильно вставке включаемого файла в данное место текущего файла. В примере 5.6 показано, как нужно включать файл под названием 1i brary. php.
Пример 5.6. Включение файла РНР
<?php include "library.php";
// Сюда помещается ваш код ?>
Использование инструкции indude_once
При каждом использовании директивы include она снова вставляет требуемый файл, даже если он уже был вставлен. Предположим, к примеру, что в библиотеке 1iibrary. phpссодержится массагполезных функций. ВыiвключаетееееЕвСсвой файл но, кроме нее, включаете также еще одну библиотеку, которая содержит 1 ibrary. php. Из-за этой вложенности вы непреднамеренно вставляете 1 i brary. php дважды. В результате будут появляться сообщения об ошибках, потому что будет предпринята попытка несколько раз объявить одну и ту же константу или функцию. Поэтому вместо данной директивы нужно использовать инструкцию include_once (пример 5.7).
Пример 5.7. Однократное включение файла РНР
<?php include_once "library.php";
// Сюда помещается ваш код ?>
Теперь, если встретится еще одна инструкция i ncl ude или i ncl ude_once, похожая на ту, которая уже была выполнена, она будет полностью проигнорирована. Чтобы определить, был ли файл уже включен, абсолютный путь к нему сравнивается со всеми раскрытыми относительными путями и файлом, найденным в пути, который указан в вашей инструкции i ncl ude.
йг *»
Вообще-то, наверное, лучше будет придерживаться использования инструкции include_once
4 * и не применять инструкцию include. Тогда у вас никогда не будет проблем с тем, что файлы — -—*£• вставляются по нескольку раз.
Использование инструкций require и require_once
Потенциальная проблема, возникающая при использовании инструкций include и include once, состоит в том, что для вставки нужного файла РНР предпримет всего одну попытку. Выполнение программы продолжится даже в том случае, если файл не будет найден.
Когда вставка файла имеет принципиальную важность, его нужно затребовать, то есть применить инструкцию require. По тем же причинам, которые излагались при рассмотрении использования инструкции include_once, я рекомендую, чтобы вы, когда нужно затребовать файл, придерживались главным образом использования инструкции requi re_once (пример 5.8).
Пример 5.8. Однократное востребование файла РНР
<?php require_once "library.php";
// Сюда помещается ваш код
?>
Совместимость версий РНР
РНР продолжает совершенствоваться и существует в нескольких версиях. Если нужно проверить доступность в вашем коде какой-нибудь конкретной функции, можно воспользоваться функцией function exists, которая проверяет все предопределенные и созданные пользователем функции.
В примере 5.9 проверяется доступность функции array_combine, которая имеется в РНР версии 5.
Пример 5.9. Проверка существования функции
<?php
i f (f unction_exists(”array_combine"))
{
echo "Функция существует";
}
else
{
echo "Функция не существует, желательно создать ее самостоятельно";
}
?>
Используя подобный код, можно определить доступность любых функциональных возможностей в новых версиях РНР, которые вам придется смоделировать, если нужно будет, чтобы ваш код работал и в более ранних версиях. Ваши функции могут работать медленнее встроенных, но код по крайней мере будет обладать более широкой переносимостью.
Чтобы определить версию РНР, под которой запущен ваш код, можно также воспользоваться функцией phpversion. Возвращаемый результат в зависимости от версии будет иметь следующий вид:
5.2.8
Объекты РНР
Практически так же, как применение функций стало фактором существенного увеличения эффективности программирования на заре развития вычислительной техники (когда лучшим из доступных средств программной навигации порой была самая элементарная инструкция GOTO или G0SUB), объектно-ориентированное программирование (ООП) подняло использование функций на совершенно новый уровень.
Как только у вас появится навык сведения повторно используемых фрагментов кода в функции, останется сделать еще один небольшой шаг и присмотреться к связыванию функций и данных, которыми они оперируют, в объекты.
Рассмотрим сайт социальной сети, состоящий из множества различных частей. Одна из таких частей управляет всеми пользовательскими функциями: ее код позволяет новым пользователям записаться, а уже записавшимся — изменить свои личные данные. В стандартном РНР можно создать для управления всеми этими действиями ряд функций и встроить несколько вызовов к базе данных MySQL, чтобы вести данные по всем пользователям.
А теперь вообразите, насколько проще будет создать объект, представляющий текущего пользователя. Для этого можно создать класс по имени User, в котором будут содержаться весь код, необходимый для обслуживания пользователей, и все переменные, требующиеся для работы с данными внутри класса. Затем, когда понадобится управлять пользовательскими данными, можно будет просто создать новый объект класса User.
Этот новый объект можно будет рассматривать в качестве настоящего пользователя. Например, объекту можно передать имя, пароль и адрес электронной почты, спросите его о том, существует ли уже такой пользователь, и если нет, заставить его создать нового пользователя с данными атрибутами. Можно даже иметь объект мгновенных сообщений или объект, позволяющий учитывать дружеские отношения между двумя пользователями.
Терминология
При создании программы, рассчитанной на использование объектов, нужно сконструировать некую совокупность данных и кода, называемую классом. Каждый новый объект, основанный на этом классе, называется экземпляром (или случаем употребления) этого класса.
Данные, связанные с объектом, называются его свойствами, а используемые им функции — методами. При определении класса задаются имена его свойств и код для его методов. На рис. 5.3 показана метафора объекта в виде музыкального автомата. Компакт-диски в его карусели можно рассматривать в качестве его свойств, а метод их проигрывания заключается в нажатии кнопки на передней панели. Есть также щель для опускания монет (метод, используемый для активации объекта) и устройство чтения компакт-дисков (метод, используемый для извлечения музыки, или свойств, с компакт-дисков).
Рис. 5.3. Музыкальный автомат как подходящий пример автономного объекта
При создании объектов предпочтительно воспользоваться инкапсуляцией или создавать класс таким образом, чтобы с его свойствами могли работать только его
собственные методы. Иными словами, нужно запретить внешнему коду непосредственный доступ к данным объекта. Предоставляемые объектом методы известны как интерфейс объекта.
Такой подход упрощает отладку: дефектный код придется исправлять только в пределах класса.
Кроме того, когда нужно будет обновить программу, при использовании надлежащей инкапсуляции и поддержке одинакового интерфейса можно будет просто разработать новые классы для замены старых, полностью их отладить, а затем заменить ими старые классы. Если они будут в чем-то неработоспособными, можно будет вернуть назад старые классы для немедленного устранения проблемы перед дальнейшей отладкой новых классов.
Как только класс будет создан, может выясниться, что нужен еще один, похожий на него, но все же несколько отличающийся класс. Быстрее и проще всего будет определить новый класс, воспользовавшись наследованием. При этом ваш новый класс сохранит все свойства, присущие тому классу, чьим наследником он является. Исходный класс теперь будет называться суперклассом, а новый класс — подклассом (или производным классом).
Вернемся к примеру с музыкальным автоматом. Если вы изобретаете новый музыкальный автомат, который наряду с музыкой может воспроизводить и видеоклипы, то можете сохранить все свойства и методы исходного музыкального автомата и добавить ряд новых свойств (видеоклипов) и новых методов (видеоплееров).
Существенным преимуществом этой системы является то, что, если вы увеличили скорость работы или улучшили другие аспекты работы суперкласса, его подклассы пользуются теми же самыми усовершенствованиями.
Объявление класса
Перед тем как получить возможность использования объекта, нужно определить класс с помощью ключевого слова cl ass. Определение класса включает в себя имя класса (чувствительное к регистру букв), его свойства и методы. В примере 5.10 дается определение класса User, имеющего два свойства: Sname и $password (которые обозначены ключевым словом publ ic — см. подраздел «Область видимости свойств и методов в РНР 5» данного раздела). В нем также создается новый экземпляр этого класса (по имени $object).
Пример 5.10. Объявление класса и проверка объекта
<?php
Sobject = new User:
print_r($object):
class User
{ public $name, $password;
function save_user()
{
echo "Сюда помещается код. сохраняющий данные пользователя":
}
} ?>
Здесь также задействована поистине бесценная функция под названием pri ntjr. Она требует от РНР отобразить информацию о переменной в удобной для восприятия человеком форме, о чем говорит элемент г в ее имени (означающий readable, «читаемый»). Для нового объекта Sobject эта функция выводит следующую информацию:
User Object (
[name] =>
[password] =>
)
Но браузер сжимает все пустые пространства, поэтому выводимая в нем информация читается немного сложнее:
User Object ( [name] => [password] => )
В любом случае выведенная информация свидетельствует о том, что Sobject является объектом, определенным пользователем, и содержит свойства name и password.
Создание объекта
Для создания объекта определенного класса используется ключевое слово new, применяемое в выражении: %обьект = new Класс. Вот два способа создания объектов:
Sobject = new User;
Stemp = new User('name\ ’password'):
В первой строке мы просто назначаем объект классу User. А во второй строке передаем вызову параметры.
Класс может требовать или запрещать аргументы; он также может разрешать, но не требовать их.
Доступ к объектам
Добавим к примеру 5.10 еще несколько строк и проверим результаты. В примере 5.11 предыдущий код расширяется за счет установки свойств объекта и вызова метода.
Пример 5.11. Создание объекта и взаимодействие с ним
<?php
Sobject = new User;
print_r(Sobject): echo "<br />" :
$object->name = "Joe";
$object->password = "mypass": print_r($object): echo "<br />":
$object->save_ user():
class User
{
public Sname, Spassword;
function save_user()
{ echo "Сюда помещается код, сохраняющий данные пользователя":
}
} ?>
Из примера видно, что для доступа к свойству объекта используется следующий синтаксис: %объект->свойство. Похожим образом можно вызвать и метод: %объект- > метод{).
Можно было заметить, что перед именами свойств и методов отсутствуют символы доллара ($). Если на первой позиции имен поставить символ $, то код не будет работать, поскольку он попробует обратиться к значению, хранящемуся в переменной. Например, выражение $object->$property будет пытаться найти значение, присвоенное переменной по имени Sproperty (скажем, это значение является строкой brown), а затем обратиться к свойству $object->brown. Если переменная Sproperty не определена, то будет предпринята попытка обратиться к свойству $object->NULL, что спровоцирует возникновение ошибки.
Если организовать просмотр, используя имеющееся в браузере средство для просмотра исходного кода, то код примера 5.11 выведет следующую информацию:
User Object
(
[name] =>
[password] =>
)
User Object
(
[name] => Joe
[password] => mypass
)
Сюда помещается код. сохраняющий данные пользователя
Здесь также используется функция pri nt_r, которая предоставляет содержимое переменной $ object до и после присваивания свойству значения. В дальнейшем я не буду использовать инструкцию print r, но если материал этой книги будет прорабатываться на вашем разработочном сервере, вы сможете поместить в код несколько таких инструкций, чтобы иметь полное представление о том, что происходит.
Можно было также заметить, что благодаря вызову метода save user был выполнен код этого метода, который вывел строку, напоминающую о том, что нужно создать некий код.
Определения функций и классов можно помещать в любое место вашего кода, до или после инструкций, в которых они используются. Но правилом хорошего тона считается помещать их ближе к концу файла.
Клонирование объектов
Если объект уже создан, то в качестве параметра он передается по ссылке. Если воспользоваться метафорой спичечного коробка, то это похоже на привязывание сразу нескольких ниток к объекту, хранящемуся в коробке, что позволяет получить к нему доступ, следуя по любой из привязанных ниток.
Иными словами, присваивание объектов не приводит к их полному копированию.
Как это работает, показано в примере 5.12, где определяется очень простой пользовательский класс User, который не имеет методов и содержит всего лишь одно свойство name.
Пример 5.12. Копирование объекта?
<?php
$objectl = new UserO;
$objectl->name = "Alice";
$object2 = $objectl;
$object2->name = "Amy";
echo "objectl name = " . $objectl->name . "<br />";
echo "object2 name = " . Sobject2->name;
class User
{
public $name;
} ?>
Мы создали объект Sobjectl и присвоили свойству name значение Alice. Затем создали $object2, присвоили ему значение $objectl и присвоили значение Ату непосредственно свойству name объекта $object2 — или подумали, что присвоили. Но этот код выдаст следующую информацию:
objectl name = Amy
object2 name = Amy
Что же произошло? И Sobjectl, и $object2 ссылаются на один и то т же объект, поэтому изменение свойства name, принадлежащего $object2, на Ату устанавливает такое же значение и для свойства, принадлежащего Sobjectl.
Во избежание подобной путаницы следует использовать инструкцию clone, которая создает новый экземпляр класса и копирует значения свойств из исходного класса в новый экземпляр. Применение этой инструкции показано в примере 5.13.
Пример 5.13. Клонирование объекта
<?php
$objectl = new UserO;
$objectl->name = "Alice";
$object2 = clone $objectl;
$object2->name = "Amy";
echo "objectl name = " . $objectl->name . "<br>";
echo "object2 name = " . $object2->name;
class User
{ public $name;
}
?>
Вот и все. Этот код выдает то, что нам требовалось получить с самого начала:
objectl name = Alice
object2 name = Amy
Конструкторы
При создании нового объекта вызываемому классу можно передать перечень аргументов. Они передаются специальному методу внутри класса, который называется конструктором и занимается инициализацией различных свойств.
В прежние времена этому методу обычно давалось имя класса, как в примере 5.14.
Пример 5.14. Создание метода-конструктора
<?php
class User
{
function User($paraml, $param2)
{
// Сюда помещаются инструкции конструктора public Susername = "Guest";
}
} ?>
В примере 5.15 показано, что РНР 5 предоставляет более логичный подход к присвоению имени конструктору, при котором функции присваивается имя __construct (то есть к слову construct спереди добавляются два символа подчеркивания).
Пример 5.15. Создание метода-конструктора в РНР 5
<?php
class User
{
function _construct(Sparaml. $param2)
{
// Сюда помещаются инструкции конструктора public Jusername = "Guest”:
}
}
?>
Деструкторы в РНР 5
Еще одним нововведением в РНР 5 стала возможность создания методов-деструкторов. Эта возможность подходит для тех случаев, когда код ссылается на объект в последний раз или когда сценарий подошел к концу. В примере 5.16 показано, как создается метод-деструктор.
Пример 5.16. Создание в РНР 5 метода-деструктора
<?php
class User
{ function _destruct() { // Сюда помещается код деструктора
}
}
?>
Написание методов
Как видите, объявление метода похоже на объявление функции, но есть некоторые отличия. Например, имена методов, начинающиеся с двойного подчеркивания (__), являются зарезервированными словами и вы не должны больше создавать ничего подобного.
Кроме того, существует специальная переменная Sthis, которая может использоваться для доступа к свойствам текущего объекта. Чтобы понять, как это работает, посмотрите на код примера 5.17, содержащий еще один метод из определения класса User, который называется get_password.
Пример 5.17. Использование в методе переменной Sthis
<?php
class User
{ public Sname. Spassword:
function get_password() { return $this->password;
}
}
?>
Метод получения пароля — get_password — применяет переменную Sthis для доступа к текущему объекту, а затем возвращает значение свойства password,
принадлежащего этому объекту. Обратите внимание на то, как при использовании оператора -> в имени свойства Spassword опускается первый символ $. Если оставить его на прежнем месте, особенно при первом применении этого свойства, будет допущена весьма типичная ошибка.
Класс, определенный в примере 5.17, нужно использовать следующим образом:
Sobject= new User
$object->password = "secret" echo $object->get_password()
Этот код выводит пароль secret.
Статические методы в РНР 5
При работе в РНР 5 можно также определить метод как статически й, что означает возможность его вызова в классе, но не в объекте. Статический метод не имеет доступа ни к одному из свойств объекта, а создание такого метода и доступ к нему показаны в примере 5.18.
Пример 5.18. Создание статического метода и доступ к нему
<?php
User::pwd_string();
class User
{
static function pwd_string()
{ echo "Пожалуйста, введите свой пароль":
}
}
?>
Обратите внимание на то, как наряду со статическим методом вызывается сам класс и как при этом вместо оператора -> используется оператор двойного двоеточия (::), также известный как оператор разрешения области видимости. Статические функции полезны для совершения действий, относящихся к самому классу, но не к конкретным экземплярам этого класса. Еще один пример статического метода показан в примере 5.21 далее.
При попытке получить доступ к свойству текущего объекта с помощью выражения $this->property « или получить доступ к другим свойствам объекта внутри статической функции будет выдано сообщение об ошибке.
Объявление свойств
В явном объявлении свойств внутри классов нет необходимости, поскольку они могут быть определены неявным образом при первом же их использовании. Для иллюстрации этой особенности класс User в примере 5.19 не имеет ни свойств, ни методов, но при этом в коде его определения нет ничего противозаконного.
Пример 5.19. Неявное объявление свойства
<?php
Sobjectl= new UserO;
$objectl->name = "A1lice";
echo $objectl->name;
class User {} ?>
Этот код вполне корректно и без проблем выведет строку А1 i се, поскольку РНР неявным образом объявит для вас переменную $objectl->name. Но такой стиль программирования может привести к ошибкам, найти которые будет невероятно трудно, поскольку свойство name было объявлено за пределами класса.
Чтобы не создавать трудностей ни себе, ни тому, кто впоследствии будет обслуживать ваш код, я советую выработать привычку всегда объявлять свойства внутри класса в явном виде. И поверьте, вы об этом никогда не пожалеете.
К тому же, когда свойство объявляется внутри класса, ему можно присвоить значение по умолчанию. Используемое вами значение должно быть константой, а не результатом вызова функции или вычисления выражения. Несколько допустимых и недопустимых присваиваний показано в примере 5.20.
Пример 5.20. Допустимые и недопустимые объявления свойств
<?php
class Test
{
public Sname public Sage
= "Paul 1 Smith";
=42;
/// До Допустимое / // До Допустимое
public Stime
= timeO;
/// Недопустимое - вызываетфункциюю
public Sscore
= Sieve! 1 *2;
/// Недопустимое - использует Евыражение
}
•?>
Объявление констант
По аналогии с созданием глобальных констант внутри определения функций можно определять константы и внутри классов. Чтобы константы выделялись на общем фоне, обычно для их имен используют буквы верхнего регистра (пример 5.21).
Пример 5.21. Определение констант внутри класса
<?php
Translate;:lookup();
class Translate
{
const ENGLISH = 0 const SPANISH = 1 const FRENCH = 2 const GERMAN = 3 II ...
{
echo self::SPANISH:
}
} ?>
К константам можно обращаться напрямую, с помощью ключевого слова self и оператора двойного двоеточия. Обратите внимание на то, что этот код, в первой строке которого используется оператор двойного двоеточия, вызывает класс напрямую, без предварительного создания его экземпляра. Как и ожидалось, значение, выводимое при запуске этого кода на выполнение, будет равно 1.
Запомните, что константа после ее определения не может быть изменена.
Область видимости свойств и методов в РНР 5
РНР 5 предоставляет три ключевых слова для управления областью видимости свойств и методов.
О publ i с (открытые). Свойства с этой областью видимости получаются по умолчанию при объявлении переменной с помощью ключевых слов var или public или когда переменная объявляется неявно при первом же ее использовании.
Ключевые слова var и public являются взаимозаменяемыми. Хотя сейчас использование var не приветствуется, оно сохранено для совместимости с предыдущими версиями РНР. Методы считаются открытыми по умолчанию.
О protected (защищенные). На свойства и методы с этой областью видимости можно ссылаться только через принадлежащие объектам методы класса и такие же методы любых подклассов.
О private (закрытые). К представителям класса с этой областью видимости можно обращаться через методы этого же класса, но не через методы его подклассов.
Решение о том, какую область видимости применить, принимается на основе следующих положений.
О Открытую (public) область видимости следует применять, когда к представителю класса нужен доступ из внешнего кода и когда расширенные классы должны его наследовать.
О Защищенную (protected) область видимости необходимо использовать, когда к представителю класса не должно быть доступа из внешнего кода, но расширенные классы все же должны его наследовать.
О Закрытую (private) область видимости следует применять, когда к представителю класса не должно быть доступа из внешнего кода и когда расширенные классы не должны его наследовать.
Применение этих ключевых слов показано в примере 5.22.
Пример 5.22. Изменение области видимости свойства и метода
<?php
class Example
var Sname = "Michael": 11 He рекомендуемая форма, аналогичная public public Sage = 23;// Открытое свойство
protected Susercount:// Защищенное свойство
private function adminO // Закрытый метод
{
// Сюда помещается код метода admin
}
}
?>
Статические свойства и методы
Большинство данных и методов применяются в экземплярах класса. Например, в классе User следует установить конкретный пароль пользователя или проверить, когда пользователь был зарегистрирован.
Эти факты и операции имеют особое отношение к каждому конкретному пользователю и поэтому применяют специфические для экземпляра свойства и методы.
Но время от времени возникает потребность обслуживать данные, относящиеся целиком ко всему классу. Например, для отчета о том, сколько пользователей зарегистрировалось, будет храниться переменная, имеющая отношение ко всему классу User. Для таких данных РНР предоставляет статические свойства и методы.
В примере 5.18, приведенном выше, было показано, что объявление представителей класса статическими делает их доступными и без создания экземпляров класса. Свойство, объявленное статическим, не может быть доступно непосредственно из экземпляра класса, но может быть доступно из статического метода.
В примере 5.23 определяется класс по имени Test, в котором содержатся статическое свойство и открытый метод.
Пример 5.23. Определение класса со статическим свойством <?php
Stemp = new TestO;
echo"TestA:"A:Test:: "Static Test;:$static_property"<
echo"Test B:"B:" ■get_sp( . Stemp->get_sp()"<
echo"TestC:"C:" ■static .iroperty$temp->static_pr
class Test
{
static $static_property = "Это статическое свойство":
function get_sp()
{
return selIf: :$static_property:
}
}
?>
Когда код будет запущен на выполнение, он выдаст следующую информацию:
Test А: Это статическое свойство
Test В: Это статическое свойство
Notice: Undefined property: Test: :$static_property Test C:
В этом примере показано, что на свойство Sstatic property можно ссылаться напрямую из самого класса, используя в Test А оператор двойного двоеточия. Test В также может получить его значение путем вызова метода get_sp объекта Stemp, созданного из класса Test. Но Test С терпит неудачу, потому что статическое свойство Sstatic_property недоступно объекту Stemp.
Обратите внимание на то, как метод get sp получает доступ к свойству Sstatic property, используя ключевое слово self. Именно таким способом можно получить непосредственный доступ к статическому свойству или константе внутри класса.
Наследование
Как только класс будет создан, из него можно будет получить подкласс. Это сэкономит массу времени: вместо скрупулезного переписывания кода можно будет взять класс, похожий на тот, который следует создать, распространить его на подкласс и просто внести изменения в те места, которые будут иметь характерные особенности. Это достигается за счет использования инструкции extends.
В примере 5.24 класс Subscriber объявляется подклассом User путем использования инструкции extends.
Пример 5.24. Наследование и распространение класса
<?php
Sobject=new Subscriber;
Sobject->name= "Fred";
$object->password = "pword";
$object->phone= "012I 345 )66789";
Sobject->email = "fred@bloggs.com";
$object->display();
class User
{
public Sname, Spassword;
function save_user()
{
echo "Сюда помещается код. сохраняющий данные пользователя"; }
class Subscriber extends User
{
public $phone, Semail;
function
i
displayO
{
echo
"Name: " . $this->name"<br />
echo
"Pass: " . $this->password . "<br />
echo
"Phone: " . $this->phone"<br />
echo
"Email: " . $this->emai1:
}
?>
У исходного класса User имеются два свойства— Sname и Spassword, а также метод для сохранения данных текущего пользователя в базе данных. Подкласс Subscriber расширяет этот класс за счет добавления еще двух свойств — Sphone и Semai 1 и включения метода, отображающего свойства текущего объекта, который использует переменную $thi s. Данная переменная ссылается на текущее значение объекта, к которому осуществляется доступ. Этот код выведет следующую информацию:
Name: Fred
Pass: pword
Phone: 012 345 6789
Emai1: fred@bloggs.com
Инструкция parent
Когда в подклассе создается метод с именем, которое уже фигурирует в его родительском классе, его инструкции переписывают инструкции из родительского класса. Иногда такое поведение идет вразрез с вашими желаниями и вам нужно получить доступ к родительскому методу. Для этого можно воспользоваться инструкцией parent, которая показана в примере 5.25.
Пример 5.25. Переписывание метода и использование инструкции parent
<?php
Sobject = new Son;
$object->test():
$object->test2():
class Dad
{ function testO { echo "[Class Dad] Я твой отец<Ьг />";
}
}
class Son extends Dad
{
{
echo "[Class Son] Я Лука<Ьг />";
}
function test2()
{ parent::test();
}
}
?>
Этот код создает класс по имени Dad (отец), а затем подкласс по имени Son (сын), который наследует свойства и методы родительского класса, а затем переписывает метод test. Поэтому, когда во второй строке кода вызывается метод test, выполняется новый метод. Единственный способ выполнения переписанного метода test в том варианте, в котором он существует в классе Dad, заключается в использовании инструкции parent, как показано в функции test2 класса Son. Этот код выведет следующую информацию:
[Class Son] Я Лука
[Class Dad] Я твой отец
Если нужно обеспечить вызов метода из текущего класса, можно воспользоваться ключевым словом self:
se lf: :method О ;
Конструкторы подкласса
При распространении класса и объявлении собственного конструктора вы должны знать, что РНР не станет автоматически вызывать метод-конструктор родительского класса. Чтобы обеспечивалось выполнение всего кода инициализации, подкласс, как показано в примере 5.26, всегда должен вызывать родительские конструкторы.
Пример 5.26. Вызов конструктора родительского класса
<?php
Sobject = new TigerO;
echo "У тигров есть.. .<br>";
echo "Мех: " . $object->fur . "<br />";
echo "Полосы: " . $object->stripes:
class Wildcat
{
public $fur; // У диких кошек есть мех
fu nction_constructO
{ $this->fur = "TRUE":
}
{
public Sstripes; 11 У тигров есть полосы
function__constructO
{
parent::__construct(); 11 Первоочередной вызов родительского
11 конструктораконструктора
$this->stripes = "TRUE":
}
}
?>
В этом примере используются обычные преимущества наследования. В классе Wi ldcat (Дикая кошка) создается свойство Sfur (мех), которое хотелось бы использовать многократно, потому мы создаем класс Tiger (Тигр), наследующий свойство $fur, и дополнительно создаем еще одно свойство — $stripes (полосы). Чтобы проверить вызов обоих конструкторов, программа выводит следующую информацию:
У тигров есть...
Мех: TRUE
Полосы: TRUE
Методы Final
При необходимости помешать подклассу переписать метод суперкласса можно воспользоваться ключевым словом final. Как это делается, показано в примере 5.27.
Пример 5.27. Создание метода final
<?php
class User
{
final function copyrightO
{
echo "Этот класс был создан Джо Смитом
}
}
?>
Усвоив содержание этой главы, вы должны приобрести твердое представление о том, что РНР может для вас сделать. Вы сможете без особого труда воспользоваться функциями и при необходимости создать объектно-ориентированный код. В главе 6 мы завершим начальное исследование РНР и рассмотрим работу с массивами.
Проверьте ваши знания
Каково основное преимущество, получаемое при использовании функции?
Сколько значений может вернуть функция?
В чем разница между доступом к переменной по имени и по ссылке?
Что в РНР означает термин «область видимости»?
Как можно включить один файл РНР в другой?
Чем объект отличается от функции?
Как в РНР создаются новые объекты?
Какой синтаксис используется для создания подкласса из существующего класса?
Как можно вызвать инициализирующую часть кода при создании объекта?
0. Почему объявлять свойства внутри класса лучше явным образом?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 5».
Массивы в PHPМассивы в PHP
Массивы в PHP
Массивы в PHP
В главе 3 у нас уже состоялось краткое знакомство с массивами в РНР, позволившее составить первичное представление об их возможностях. В данной главе будет показан большой арсенал приемов работы с массивами, некоторые из них при наличии у вас опыта работы с языками со строгой типизацией, например С, могут удивить своей простотой и изяществом.
Массивы — одна из составляющих популярности РНР Кроме того, что они не дают умереть со скуки при создании кода для работы со сложными структурами данных, они также предоставляют множество невероятно быстрых способов доступа к данным.
Основные подходы к массивам
Массивы уже рассматривались в виде группы склеенных вместе спичечных коробков. Их можно представить также в виде нитки бус, где бусины обозначают переменные, которые могут быть числовыми, строковыми и даже другими массивами. Массивы похожи на нитки бус, потому что каждый элемент имеет собственное место и у каждого элемента (кроме первого и последнего) с обеих сторон есть другие элементы.
Часть массивов использует ссылки по числовым индексам, другая — позволяет работать с буквенно-цифровыми идентификаторами. Встроенные функции дают возможность проводить сортировку, добавлять и удалять отрезки и перебирать элементы для обработки каждого из них, используя специальный вид цикла. А за счет размещения одного или нескольких массивов внутри других массивов можно создавать массивы любой размерности.
Массивы с числовой индексацией
Представим, что перед вами поставлена задача создать простой веб-сайт для компании по поставке товаров для офиса и сейчас вы ведете разработку его раздела, в котором представлены различные сорта бумаги. Как вариант, различные единицы хранения этой категории можно поместить в числовой массив, чтобы получить возможность управлять ими. Простейший способ реализации этого подхода показан в примере 6.1.
Пример 6.1. Добавление элементов в массив
<?php
$рарег[] = "Copier";
$paper[] = "Inkjet";
$paper[] = "Laser";
$paper[] = "Photo";
print_r($paper);
?>
В этом примере при каждом присваивании массиву $рарег значения для хранения последнего используется первое же свободное место, а значение существующего в РНР внутреннего указателя увеличивается на единицу, чтобы указывать на свободное место, готовое для следующей вставки значения. Уже известная нам функция pri nt_r (которая выводит на экран содержимое переменной, массива или объекта) применяется для проверки правильности заполнения массива. Результат ее работы имеет следующий вид:
Array
(
=> Copier
=> Inkjet
=> Laser
=> Photo
)
Предыдущий код может быть написан и так, как показано в примере 6.2, где для каждого элемента указывается точное место в массиве. Но, как видите, такой подход требует набора лишних символов и усложняет обслуживание кода в том случае, если будет необходимо вставлять товары в массив или удалять их оттуда. Поэтому, если не нужно указывать какой-нибудь другой порядок размещения элементов в массиве, лучше все же позволить РНР самостоятельно заниматься их расстановкой.
Пример 6.2. Добавление в массив элементов с конкретным указанием их мест
<?php
$рарег[0] = "Copier";
$paper[l] = "Inkjet";
$paper[2] = "Laser";
$paper[3] = "Photo";
print_r($paper);
?>
Этот пример выведет такую же информацию, как и предыдущий, но в разрабатываемом веб-сайте вы вряд ли будете пользоваться функцией print r, поэтому в примере 6.3 показано, как с помощью цикла можно распечатать сведения о различных типах бумаги, предлагаемых на веб-сайте.
Пример 6.3. Добавление элементов в массив и извлечение их из массива
<?php
$paper[] = "Copier";
$paper[] = "Inkjet";
$рарег[] = "Laser";
$paper[] = "Photo";
for ($j = 0 ; $j < 4 ; ++$j)
echo "$j; $paper[$j]<br>";
?>
Этот пример выведет следующую информацию:
0: Copier
Inkjet
Laser
Photo
Итак, вы увидели два способа добавления элементов к массиву и один из способов ссылки на них, но РНР предлагает и много других способов, на которых я кратко остановлюсь в дальнейшем. Сначала рассмотрим другой тип массива.
Ассоциативные массивы
Конечно, можно отслеживать элементы массива по индексам, но тогда придется помнить, какой именно номер на какой товар ссылается. Кроме того, за вашим кодом трудно будет уследить другим программистам.
Самое время обратиться к ассоциативным массивам. Использование этих массивов позволяет ссылаться на элементы массива по именам, а не по номерам. В примере 6.4 приводится расширенная версия предыдущего кода, где каждому элементу массива дается имя для идентификации и более длинное и информативное строковое значение.
Пример 6.4. Добавление элементов к ассоциативному массиву и извлечение этих элементов
<?php
$paper['copier'] = "Copier &Multipurpose";
$paper['inkjet'] = "Inkjet Printer";
$paper[’laser’] = "Laser Printer";
$paper['photo'] = "Photographic Paper";
echo $paper['laser']: ?>
Теперь у каждого элемента вместо числа (не содержащего никакой полезной информации, кроме позиции элемента в массиве) имеется уникальное имя, по которому на него можно сослаться где-нибудь в другом месте, как в случае с инструкцией echo, которая выводит на экран Laser Pri nter. Имена (copi er, i nkjet и т. д.) называются индексами, или ключами, а присвоенные им элементы (например, Laser Printer) — значениями.
Это весьма мощное свойство РНР часто используется при извлечении информации из кода XML и HTML. Например, HTML-парсер, используемый в поисковой системе, может помещать все элементы веб-страницы в ассоциативный массив, имена которого отображают структуру страницы:
Shtml['title'] = "Моя веб-страница";
Shtml[ 'body’] = "... тело веб-страницы
Вполне вероятно, что программа разобьет все найденные на странице ссылки и поместит их в другой массив, а все заголовки и подзаголовки — еще в один массив. При использовании ассоциативных, а не числовых массивов код, ссылающийся на все эти элементы, проще будет создавать и отлаживать.
Присваивание, использующее ключевое слово array
Мы уже видели, как элементам массива присваиваются значения путем последовательного добавления к этому массиву новых элементов. Но это слишком затянутый процесс, независимо от того, что при этом происходит: вы определяете ключи, числовые идентификаторы или позволяете РНР неявным образом заниматься присваиванием числовых идентификаторов. Есть более краткий и быстрый способ присваивания значений с использованием ключевого слова array. В примере 6.5 показаны оба массива, числовой и ассоциативный, значения которым присваиваются именно этим способом.
Пример 6.5. Добавление элементов к массиву с использованием ключевого слова array
<?php
$pl = агray("Copier", "Inkjet". "Laser". "Photo");
echo "Элемент массива pi: " . $pl[2] . "<br>";
$p2 = array('copier' => "Copier &Multipurpose".
'inkjet' => "Inkjet Printer".
'laser' => "Laser Printer".
'photo' => "Photographic Paper");
P
echo "Элемент массива p2: " . $p2['inkjet'] . "<br>"; ?>
В первой части этого кодового фрагмента массиву $р1 присваивается старое, укороченное описание товара. Здесь используются четыре элемента, поэтому они занимают позиции от 0 до 3. Инструкция echo выводит следующий текст:
Элемент массива pi: Laser
Во второй части кода массиву $р2 присваиваются ассоциативные идентификаторы и сопутствующие им длинные описания товаров. Для этого используется формат индекс => значение. Применение оператора => похоже на использование простого оператора присваивания =, за исключением того, что значение присваивается индексу уа не переменной. После этого индекс приобретает неразрывную связь с этим значением до тех пор, пока ему не будет присвоено другое значение. Поэтому команда echo выводит следующий текст:
Элемент массива р2: Inkjet Printer
В том, что $р1 и $р2 принадлежат к разным типам массивов, можно убедиться, если вставить в код две следующие команды, вызывающие ошибку неопределенного индекса (undefined index) или ошибку неопределенного смещения (undefined offset), поскольку для каждого из массивов используется неподходящий идентификатор:
echo $pl['inkjet’]; // Неопределенный индекс
echo $р2['3']:// Неопределенное смещение
Цикл foreach...as
Создатели РНР постарались сделать этот язык простым в использовании. Поэтому они не остановились на уже имеющихся структурах организации цикла, а добавили еще одну структуру, специально предназначенную для массивов, — цикл foreach. . .as. Используя этот цикл, можно поочередно перебрать все элементы массива и произвести с ними какие-нибудь действия.
Процесс начинается с первого элемента и заканчивается последним, поэтому вам даже не нужно знать, сколько элементов присутствует в массиве. В примере 6.6 показано, как цикл foreach может использоваться для переписывания кода примера 6.3.
Пример 6.6. Последовательный перебор элементов числового массива, использующий цикл foreach.. .as
<?php
Spaper = array("Copier". "Inkjet", "Laser", "Photo");
$j = 0:
foreach ($paper as Si tern)
{
echo "$j: $item<br>";
++$j:
}
?>
Когда PHP встречает инструкцию foreach, он извлекает первый элемент массива и помещает его значение в переменную, указанную после ключевого слова as, и при каждом возвращении управления инструкции foreach в эту переменную помещается значение следующего элемента массива. В данном случае переменной $i tern присваиваются по очереди все четыре значения, хранящиеся в массиве Spaper. Как только будут использованы все значения, выполнение цикла завершается. Этот код выводит точно такую же информацию, что и код примера 6.3.
Теперь посмотрим, как foreach работает с ассоциативным массивом. В примере 6.7 переписана вторая часть примера 6.5.
Пример 6.7. Последовательный перебор элементов ассоциативного массива, использующий цикл foreach...as
<?php
Spaper = array('copier' => "Copier & Multipurpose".
'inkjet' => "Inkjet Printer",
'laser' => "Laser Printer".
'photo' => "Photographic Paper");
foreach (Spaper as Sitem => Sdescription)
echo "Sitem: $description<br>";
?>
Вспомним, что ассоциативным массивам не требуются числовые индексы, поэтому переменная $j в данном примере не используется. Вместо этого каждый элемент массива Spaper вводится в пару «ключ — значение», представленную переменными Sitem и Sdescription, из которых эта пара выводится на экран в следующем виде:
copier: Copier &Multipurpose
inkjet: Inkjet Printer
laser: Laser Printer
photo: Photographic Paper
В качестве альтернативы синтаксису foreach... as можно воспользоваться функцией 1 i st в сочетании с функцией each (пример 6.8).
Пример 6.8. Последовательный перебор элементов ассоциативного массива с помощью функций each и lis t
<?php
Spaper = array( 'copier' => "Copier &Multipurpose".
'inkjet' => "Inkjet Printer",
'laser' => "Laser Printer".
'photo' => "Photographic Paper");
while (list($item. Sdescription) = each(Spaper))
echo "Sitem: Sdescription<br>";
?>
В этом примере организуется цикл whi 1 е, который будет продолжать работу до тех пор, пока функция each не вернет значение FALSE. Функция each ведет себя как foreach: она возвращает из массива Spaper массив, содержащий пару «ключ —значение», а затем перемещает встроенный указатель на следующую пару в исходном массиве. Когда возвращать станет нечего, функция each возвращает значение FALSE.
Функция 1 i st в качестве аргументов принимает массив (в данном случае пару «ключ — значение», возвращенную функцией each), а затем присваивает значения массива переменным, перечисленным внутри круглых скобок.
Лучше понять работу функции 1 i st можно из примера 6.9, где массив создается из двух строк — А1 i се и Bob, а затем передается функции 1 i st, которая присваивает эти строки переменным $а и $Ь.
Пример 6.9. Использование функции lis t
<?php
list(Sa, Sb) = arra ; y('Alice'. 'Bob');
echo "a=$a b=$b";
?>
Этот код выводит следующий текст:
a=Alice b=Bob
Итак, для перебора элементов массива можно использовать различные подходы. Можно воспользоваться конструкцией foreach.. .as для создания цикла, извлекающего значения в переменную, которая следует за as, или воспользоваться функцией each и создать собственную систему циклической обработки.
Многомерные массивы
Простая конструктивная особенность синтаксиса массивов РНР позволяет создавать массивы более чем с одним измерением. Фактически можно создавать массивы какой угодно размерности (хотя приложения редко нуждаются в массивах с размерностью больше трех).
Эта особенность заключается в возможности включать целый массив в состав другого массива, а также делать это снова и снова, как в старом стишке про блох, которых кусают другие блохи поменьше, а тех, в свою очередь, кусают свои блохи, и так до бесконечности.
Рассмотрим, как это работает, для чего возьмем ассоциативный массив из предыдущего примера и расширим его (пример 6.10).
Пример 6.10. Создание многомерного ассоциативного массива
<?php
Sproducts = array(
'paper' => array(
'copier' => "Copier & Multipurpose".
'in kjet' => "Inkjet Printer".
'laser' => "Laser Printer".
’photo' => "Photographic Paper").
'pens' => array(
'ba ll' => "Ball Point".
' hi 1ite ’ => "Highlighters".
'marker' => "Markers").
'misc' => array(
'tape'=>"Sticky Tape".
'glue'=>"Adhesives".
'clip2s'=>"Paperclips") );
echo "<pre>";
foreach ($products as $section => Si terns)
foreach (Siterns as Skey => Svalue)
echo "$section:\t$key\t(Svalue)<br>"; echo "</pre>";
?>
Чтобы упростить понимание начинающего разрастаться кода, я переименовал часть элементов. Например, рассматривавшийся ранее массив Spaper стал всего лишь подразделом более крупного массива, а главный массив теперь называется Sproducts. В этом массиве присутствуют три элемента: бумага — paper, ручки — pens и разные товары —mi sc, и каждый из них содержит другой массив, состоящий из пар «ключ — значение».
При необходимости эти подмассивы могут содержать другие массивы. Например, элемент шариковые ручки — bal 1 может содержать множество типовых и цветовых решений этого товара, имеющихся в интернет-магазине. Но пока я ограничил код глубиной в два измерения.
После присвоения массивам данных для вывода различных значений я воспользовался парой вложенных циклов foreach.. .as. Внешний цикл извлекает из верхнего уровня массива основные разделы, а внутренний цикл извлекает для категорий в каждом разделе пары «ключ — значение».
Если вспомнить, что все уровни массива рабютают одинаково (являясь парой «ключ — значение»), можно без особого труда создать код для доступа к любому элементу на любом уровне.
В инструкции echo используется управляющий символ РНР \t, который выводит знак табуляции.
Хотя знаки табуляции для веб-браузеров, как правило, ничего не значат, я использовал их в разметке, применив теги <рге>.. .</рге>, которые предписывают веб-браузеру форматировать текст с сохранением предварительного формата и фиксированной ширины и не игнорировать неотображаемые символы вроде знаков табуляции и переводов строки. Текст, выводимый этим кодом, будет иметь следующий вид:
paper:
copier
(Copier &Multipurpose)
paper:
inkjet
(Inkjet Printer)
paper:
laser
(Laser Printer)
paper:
photo
(Photographic Paper)
pens:
ball
(Ball Point)
pens:
hilit1e
(Highlighters)
pens:
marker
(Markers)
mi sc:
tape
(Sticky Tape)
misc:
glue
(Adhesives)
mi sc:
clips
(Paperclips)
Непосредственный доступ к конкретному элементу массива можно получить, используя квадратные скобки:
echo Sproducts['misc’]['glue']:
Этот код выводит значение Adhesi ves.
Можно также создать числовой многомерный массив, непосредственный доступ к элементам которого можно будет получать по индексам, а не по буквенно-цифровым идентификаторам. В примере 6.11 создается шахматная доска с фигурами на исходных позициях.
Пример 6.11. Создани
многомерного
числового
массива
<?php
Schessboard = array(
array('r', 'n',
’b\'k\'q'.
'b\ 'n'.
’ r').
array('p'. V -
;p;. 'P'. ;p;.
p . V ,
’P’).
array(’ '.
arrayC '.
array(' ',
arrayC '. '
' '),
array('P'. 'P'.
P", ’ P', P'.
'p*. ■p\
■'P').
arrayCR’. ’N’,
B\ 'K*. 'Q'.
'B*. 'N',
■R'))
echo "<pre>";
foreach (Schessboard
as Srow)
{
foreach (Srow as
Spiece)
echo "Spiece
";
echo ”<br />";
}
echo n</pre>";
?>
В этом примере буквы в нижнем регистре представляют собой черные фигуры, а верхнем регистре — белые. Используются следующие обозначения: г — rook (ладья), n — knight (конь), b — bishop (слон), k — king (король), q — queen (ферзь) и р — pawn (пешка). Для последовательного перебора массива и демонстрации его содержимого снова используется пара вложенных циклов foreach.. .as. Внешний цикл обрабатывает каждую горизонталь и помещает ее в переменную Srow, которая сама по себе является массивом, поскольку для каждой горизонтали массив шахматной доски — Schessboard использует подмассив. В этом цикле используются две инструкции, поэтому они заключены в фигурные скобки.
Внутренний цикл обрабатывает каждую клетку горизонтали, выводя хранящийся в ней символ (Spi есе), за которым следует пробел (чтобы выводимый текст имел форму шахматной доски). У этого цикла одна инструкция, которую не нужно заключать в фигурные скобки. Теги <рге> и </рге> обеспечивают правильную форму выводимого текста:
rnbkqbnr
РРРРРРРР
Р Р Р Р Р Р Р Р
RNBKQBNR
Используя квадратные скобки, можно получить непосредственный доступ к любому элементу этого массива:
echo $chessboard[7][4];
Эта инструкция выведет букву Q в верхнем регистре, которая является значением восьмого вниз по вертикали и пятого по горизонтали элемента (следует помнить, что индексы массива начинаются с нуля, а не с единицы).
Использование функций для работы с массивами
С функциями 1 i st и each вы уже знакомы, но в РНР имеется множество других функций, предназначенных для работы с массивами. Их полный перечень представлен по адресу http://tinyurl.com/phparrayfuncs. Но некоторые из этих функций играют настолько важную роль в программировании на РНР, что мы изучим их подробнее.
is_array
Массивы и переменные используют одно и то же пространство имен. Это означает, что нельзя иметь строковую переменную по имени $fred и массив, который также называется $f red. Если есть сомнения и в коде программы нужно проверить, является ли переменная массивом, можно воспользоваться функцией is_array: echo (is_array($fred)) ? "Это массив" : "Это не массив";
Заметьте, что переменной $fred не присвоено никакого значения, поэтому будет выведено сообщение о неопределенной переменной — Undefined variable.
count
Несмотря на то что функция each и структура организации цикла foreach... as предоставляют отличные способы последовательного перебора всего содержимого массива, иногда нужно точно знать, сколько элементов содержится в вашем массиве, особенно если вы будете обращаться к ним напрямую. Для подсчета всех элементов на верхнем уровне массива используется следующая команда: echo count($fred);
Если нужно узнать, сколько всего элементов содержится в многомерном массиве, можно воспользоваться следующей инструкцией:
echo count($fred. 1);
Второй параметр является необязательным и устанавливает режим использования. Он может иметь либо нулевое значение, чтобы ограничить подсчет только верхним уровнем, либо единичное — для принудительного включения рекурсивного подсчета еще и всех элементов, содержащихся в подмассивах.
sort
Сортировка является настолько распространенной операцией, что РНР предоставляет для нее встроенную функцию. В наипростейшей форме ее можно использовать следующим образом:
sor I t($fred):
В отличие от некоторых других функций, сортировка будет работать непосредственно с предоставленным ей массивом, а не возвращать новый массив с отсортированными элементами. Вместо этого она вернет значение TRUE при успешном выполнении сортировки и FALSE — в случае возникновения ошибки. Эта функция поддерживает также несколько флагов. Основные два, которые вам могут пригодиться, предписывают проведение либо числовой, либо строковой сортировки:
sort($fred. SORT_NUMERIC);
sort($fred. SORT_STRING);
Массив можно также отсортировать в обратном порядке, воспользовавшись функцией rsort:
rsort($fred, SORT_NUMERIC);
rsort($fred. SORT_STRING);
shuffle
Иногда, например при создании игры или при игре в карты, требуется, чтобы элементы массива располагались в случайном порядке:
shuffle(Scards);
Как и функция sort, функция shuff1 е работает непосредственно с предоставленным ей массивом и возвращает значение TRUE в случае успешного завершения работы и FALSE — при возникновении ошибки.
explode
Это очень полезная функция, позволяющая взять строку, содержащую несколько элементов, отделенных друг от друга одиночным символом (или строкой символов), а затем поместить каждый из этих элементов в массив. В примере 6.12 показан один из случаев полезного применения этой функции, который заключается в разбиении предложения на слова и помещении всех слов, из которого оно состоит, в массив.
Пример 6.12. Извлечение слов из строки в массив с использованием пробелов
<?php
Stemp = explodeC '. "Это предложение из пяти слов"); print_r($temp);
?>
Этот пример выводит следующую информацию (которая при просмотре в браузере будет отображена в одной строке):
Array
(
=> Это
=> предложение
=> из
=> пяти
=> слов
)
Первый параметр, разделитель, не обязательно должен быть пробелом или даже одиночным символом. В примере 6.13 показан этот же код в несколько измененном виде.
Пример 6.13. Извлечение слов, разделенных символами ***, из строки в массив
<?php
Stemp = explode('***'. "Это***предложение***со***звездочками"); print_r($temp);
?>
Код примера 6.13 выводит следующую информацию:
Array
(
[0] => Это
=> предложение
=> со
=> звездочками
)
extract
Иногда бывает удобно превратить пары «ключ — значение» из массива в переменные РНР. Один из таких случаев — это обработка переменных $_GET или S P0ST, отправленных формой сценарию РНР
Когда форма передается через Интернет, веб-сервер распаковывает переменные и помещает их в глобальный массив, предназначенный для сценария РНР. Если переменные были отправлены методом GET, они будут помещены в ассоциативный массив $_GET, а при отправке методом POST они будут помещены в ассоциативный массив $_P0ST.
Разумеется, можно перебрать все элементы этих ассоциативных массивов, воспользовавшись уже рассмотренными в этой главе способами. Но иногда нужно лишь сохранить отправленные значения в переменных для дальнейшего использования. В таком случае можно заставить РНР проделать эту работу за вас в автоматическом режиме:
extract ($_GET);
Таким образом, к примеру, если параметр строки запроса q отправлен сценарию РНР наряду со связанным с ним значением Hi there, будет создана новая переменная по имени $q, которой будет присвоено это значение.
Но к этому подходу нужно относиться осторожно, поскольку если какие-нибудь извлекаемые переменные конфликтуют с уже определенными переменными, то существующие переменные будут переписаны. Чтобы избежать этого, можно воспользоваться одним из многих дополнительных параметров, доступных в данной функции:
extract($_GET. EXTR_PREFIX_ALL. Mtomget');
В этом случае имена всех новых переменных будут начинаться с заданного строкового префикса, за которым следует символ подчеркивания, в результате чего $q превратится в $f romget q. Я настоятельно рекомендую при обработке массивов $_GET и $_P0ST или любого другого массива, ключи которого могут контролироваться пользователем, использовать именно эту версию функцииЛ1оскольку злоумышленники могут отправлять ключи, специально подобранные для того, чтобы переписать переменные с часто используемыми именами и таким образом угрожать вашему веб-сайту.
compact
Иногда нужно воспользоваться функцией compact, которая является противоположностью функции extract, чтобы создать массив из переменных и их значений. Применение этой функции показано в примере 6.14.
Пример 6.14. Использование функции compact
<?php
$fname="Elizabeth";
Ssname="Windsor";
Saddress="Buckingham Palace";
Scity="London";
Scountry = "United Kingdom";
Scontact = compact('fname'. 'sname', ‘address’ , 'city', 'country'); print_r($contact);
?>
В результате запуска кода из примера 6.14 будет выведена следующая информация:
Array
(
[fname] => Elizabeth
[sname] => Windsor
[address] => Buckingham Palace
[city] => London
[country] => United Kingdom
)
Обратите внимание на то, что функции compact нужны имена переменных, стоящие в кавычках и не содержащие начального символа $. Причина заключается в том, что функция compact определяет массив с именами переменных.
Эту функцию можно использовать также для отладки, когда нужно быстро просмотреть несколько переменных вместе с их значениями, как в примере 6.15.
Пример 6.15. Использование функции compact для отладки программы
<?php
$j=23;
Stemp= "Hello";
Saddress = "1 Old Street":
$аде = 61;
print_r (compact (explode (' ' .'j temp address age'))); ?>
Работа примера основана на использовании функции expl ode для извлечения всех слов из строки в массив, который затем передается функции compact, а она, в свою очередь, возвращает массив функции print r, показывающей его содержимое.
Если скопировать и вставить строку кода, содержащую вызов функции pri nt_r, то в ней нужно будет лишь изменить имена переменных, чтобы быстро вывести группу их значений. В этом примере выводимая информация будет иметь следующий вид:
Array
(
[j] => 23
[temp] => Hello
[address] => 1 Old Street [age] => 61
)
reset
Когда с помощью конструкции foreach.. .as или функции each осуществляется последовательный перебор элементов массива, они перемещают внутренний указатель РНР, который показывает, какой из элементов массива следует извлечь в следующий раз. Если коду программы понадобится вернуться к началу массива, то можно воспользоваться функцией reset, которая к тому же вернет значение элемента, на котором остановился указатель. Эта функция может быть использована следующим образом:
reset(Sfred);// Отбрасывание возвращаемого значения
Sitem = reset(Sfred): // Сохранение первого элемента массива // в переменной Sitem
end
Можно также переместить внутренний указатель элемента массива РНР на самый последний элемент, воспользовавшись для этого функцией end, которая, кроме этого, возвращает значение элемента и может быть использована следующим образом:
end($fred);
Sitem = end(Sfred);
В этой главе завершается введение в основы РНР. Теперь, используя приобретенные навыки, вы должны справиться с написанием довольно сложных программ. В следующей главе будет рассмотрено применение РНР для решения наиболее распространенных практических задач.
Проверьте ваши знания
В чем разница между числовым и ассоциативным массивом?
Каковы основные преимущества использования ключевого слова array?
В чем разница между foreach и each?
Как создается многомерный массив?
Как определить количество элементов в массиве?
Каково назначение функции expl ode?
Как вернуть внутренний указатель элемента массива РНР на его первый элемент?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 6».
7
Практикум
по программированию на РНР
В предыдущих главах рассматривались элементы языка РНР. А эта глава предназначена для приобретения навыков программирования в процессе решения типовых, но тем не менее важных практических задач.
Здесь будут представлены лучшие способы обработки строк, позволяющие получить вполне понятный и лаконичный код, включающий усовершенствованное управление отображением даты и времени, демонстрируемый веб-браузерами в точном соответствии с вашими желаниями. Вы также узнаете о создании и разнообразных способах изменения файлов, включая файлы, выложенные на сайт пользователями.
В этой главе будет также предоставлено полноценное введение в XHTML, язык разметки, похожий на HTML, и предназначенный для его замены (согласующийся с синтаксисом XML, который используется для хранения данных, например RSS- потоков). В совокупности все это расширит вашу осведомленность как в области практического программирования на РНР, так и в сфере развития международных веб-стандартов.
Использование функции printf
Ранее нам уже встречались функции print и echo, которые использовались для простого вывода текста в браузер. Но существует намного более мощная функция pri ntf, управляющая форматом выводимых данных путем вставки в строку специальных форматирующих символов. Функция pri ntf ожидает, что для каждого форматирующего символа будет предоставлен аргумент, который будет отображаться с использованием заданного формата. Например, в следующем фрагменте применяется спецификатор преобразования Md, чтобы значение 3 отображалось в виде десятичного числа:
pr^ntf("В вашей корзине находится Md покупки”. 3):
Если заменить Md на Mb, значение 3 будет отображено в виде двоичного числа (11).
В табл. 7.1 показаны поддерживаемые функцией спецификаторы преобразования.
Таблица 7.1. Спецификаторы преобразования, используемые в функции printf
Спецификатор
Преобразование, осуществляемое с аргументом arg
Пример (для arg, имеющего значение 123)
%
Отображение символа % (аргументы не требуются)
%
Ь
Отображение arg в виде двоичного целого числа
1111011
с
Отображение ASCII-символа с кодом, содержащимся в arg
{
d
Отображение arg в виде целого десятичного числа со знаком
123
е
Отображение arg с использованием научной формы записи
1.23000е+2
f
Отображение arg в виде числа с плавающей точкой
123.000000
о
Отображение arg в виде восьмеричного целого числа
173
S
Отображение arg в виде строки
123
U
Отображение arg в виде беззнакового десятичного числа
123
X
Отображение arg в виде шестнадцатеричного числа с символами в нижнем регистре
7Ь
X
Отображение arg в виде шестнадцатеричного числа с символами в верхнем регистре
7В
В функции pri ntf можно использовать любое количество спецификаторов, если им передается соответствующее количество аргументов и если каждый спецификатор предваряется символом %. Поэтому следующий код имеет вполне допустимую форму и выводит предложение: «Меня зовут Симон. Мне 33 года, то есть 21 в шестнадцатеричном представлении»:
pr1nt"f("Меня зовут ^5. Мне 2d года, то есть И в шестнадцатеричном представлении", 'Simon', 33, 33):
Если пропустить какой-нибудь аргумент, то будет получена ошибка синтаксического разбора, информирующая о том, что правая круглая скобка,), была встречена в неожиданном месте.
Более полезный с практической точки зрения пример использования функции printf устанавливает цвета в коде HTML, используя для этого десятичные числа. Предположим, к примеру, что вам нужен цвет, составленный из трех значений: 65 для красного, 127 для зеленого и 245 для синего цвета, но вам не хочется самостоятельно переводить эти числа в шестнадцатеричный формат. Для этого есть более простое решение:
printf("<font color='#TOX%X’>npHBeT</font>". 65. 127. 245):
Тщательно разберитесь с цветовой спецификацией, которая заключена в апострофы ( ’' Сначала ставится знак решетки (#), ожидаемый в цветовой спецификации. Затем следуют три форматирующие спецификации %, по одной для каждого из ваших чисел. В результате эта команда выдаст следующий текст:
<font color='#417FF5'>Привет</font>
Обычно представляется удобным в качестве аргументов printf использовать переменные или выражения. Например, если значения для цветового решения хранятся в трех переменных — $r, $д и $Ь, то более темный оттенок можно получить с помощью выражения:
printf("<font color='#^X%X^X,>npnBeT</font>". $r-20. $д-20. $b-20);
Настройка представления данных
Можно указать не только тип преобразования, но и точность отображаемого результата. Например, суммы в валюте отображаются, как правило, с точностью до двух цифр. Но после вычисления значение может иметь более высокую точность (например, если разделить $123,42 на 12, получится $10,285). Чтобы обеспечить правильное внутреннее хранение таких значений, но при этом организовать их отображение с точностью только до двух цифр, можно между символом %и спецификатором преобразования вставить строку . 2:
рг^-ГСРезультат: $*.2f". 123.42 / 12);
Эта команда выводит следующий текст:
Результат: $10.29
Но доступные средства управления на этом не заканчиваются, потому что можно также указать, где и чем — нулями или пробелами — дополнить выводимый текст, поставив перед спецификатором соответствующие значения. В примере 7.1 показаны пять возможных комбинаций.
Пример 7.1. Настройка преставления данных точности
<?php
echo "<pre>"; // Тег. позволяющий отображать все пустые пространства
// Дополнение пробелами до 15 знако-мест
printtf("Результат равен $%15f\n". 123.42 / 12):
// Дополнение нулями до 15 знако-мест
printtf("Результат равен $iШL5Ап". 123.42 / 12);
// Дополнение пробелами до 15 знако-мест и вывод с точностью до двух
// десятичных знаков
pri nttf("Результат равен $*15.2f\n". 123.42 / 12);
// Дополнение нулями до 15 знако-мест и вывод с точностью до двух
// десятичных знаков
pri nttf("Результат равен $£015.2f\n". 123.42 / 12);
// Дополнение символами # до 15 знако-мест и вывод с точностью до двух десятичных знаков
pri ntf ("Результат равен $r# 15.2f\n". 123.42 / 12);
?>
Этот пример выводит следующий текст:
Результат равен $10.285000
Результат равен $00000010.285000
Результат равен $10.29
Результат равен $000000000010.29
Результат равен $########10.29
Проследить работу спецификатора проще, если изучать его слева направо (табл. 7.2). Обратите внимание на следующие моменты.
О Самым правым символом спецификатора преобразования в данном случае является f, означающий преобразование в число с плавающей точкой.
О Если сразу же перед спецификатором преобразования стоит сочетание точки и числа, значит, этим числом указана точность выводимой информации.
О Независимо от присутствия спецификатора точности, если в общем спецификаторе есть число, то оно представляет собой количество знако-мест, выделяемых под выводимую информацию. В предыдущем примере это количество равно 15. Если выводимая информация уже равна количеству выделяемых знако-мест или превышает его, данный аргумент игнорируется.
О Перед самым левым символом % разрешается поставить символ 0, который игнорируется, если не указано количество выделяемых знако-мест. Если это количество указано, то вместо пробелов дополнение производится нулями. Если нужно, чтобы пустующие знако-места заполнялись не нулями или пробелами, а каким-нибудь другим символом, то можно выбрать любой символ, поставив перед ним одинарную кавычку: '#.
О В левой части спецификатора ставится символ %, с позиции которого и начинается преобразование.
Таблица 7.2. Компоненты спецификатора преобразования
Начало преобразования
Дополняющий символ
Количество дополняющих символов
Точность отображения
Спецификатор преобразования
Примеры
%
15
f
10.285000
%
0
15
.4
f
000000000010.29
%
'#
15
.2
f
########10.2850
Дополнение строк
Дополнить до требуемой длины можно не только числа, но и строки, выбирая для этого различные дополняющие символы и даже левую или правую границы выравнивания. Возможные варианты показаны в примере 7.2.
Пример 7.2. Дополнение строк
<?php
echo "<рге>": // Тег. позволяющий отображать все пустые пространства
$h = ’House';
prin tf( [*s]\n\
Sh)
//
Стандартный вывод строки
prin tf( [*10s]\n\
Sh)
//
Выравнивание пробелами по правому краю
printf( C%-10s]\n".
Sh)
//
Выравнивание пробелами по левому краю
printf( [*010s]\n".
Sh)
//
Дополнение нулями
printf( [*'#10s]\n\n"
Sh)
// //
Использование специально выбранного символа дополнения '#’
Sd = 'Doctor House';
printf( "[£10.8s]\n ",
Sd);
// //
Выравнивание по правому краю с усечением до 8 символов
printf("[X-10.6s]\n",
Sd);
// //
Выравнивание по левому краю с усечением до 6 символов
printf("[£-’@10.6s]\n".
Sd);
// //
Выравнивание по левому краю, дополнение символом ■ @, усечение до 6 символов
?>
Обратите внимание на то, что для получения нужной разметки на веб-странице я воспользовался HTML-тегом <рге>, который оставляет нетронутыми все пустые пространства и после каждой отображаемой строки выводит на экран символ новой строки \п. В этом примере выводится следующий текст:
[House]
[ House]
[House ]
[OOOOOHouse]
[#####House]
[ Doctor H]
[Doctor ]
[Doctor@@@@]
Если при указании количества знако-мест длина строки уже равна этому количеству или превышает его, это указание будет проигнорировано, если только заданное количество символов, до которого нужно усечь строку, не будет меньше указанного количества знако-мест.
В табл. 7.3 показаны спецификаторы преобразования строки, разложенные на компоненты.
Таблица 7.3. Компоненты спецификаторов преобразования строки
Начало преобразования
Выравнивание по левому или по правому краю
Дополняющий символ
Количество дополняющих символов
Усечение
Спецификатор преобразования
Примеры
%
S
[House]
%
-
10
S
[House ]
%
'#
8
.4
S
[####Hous]
Использование функции sprintf
Зачастую результат преобразования нужно не выводить на экран, а использовать в самом коде программы. Для этого предназначена функция spri ntf. Она позволяет не отправлять выходную информацию браузеру, а присваивать ее какой-нибудь переменной.
Функции spri ntf можно использовать просто для преобразования, возвращающего шестнадцатеричное строковое значение для цветового сочетания RGB 65,127,245, которое присваивается переменной Shexstri ng:
Shexstring = sprintfC'&XTOX", 65, 127, 245);
Или же она может пригодиться для сохранения выходной информации, которую нужно будет вывести на экран чуть позже:
$out = sprintf("Результат: $Z.2f", 123.42 / 12);
echo $out;
Функции даты и времени
Для отслеживания даты и времени в РНР используются стандартные отметки времени Unix, представляющие собой простое количество секунд, прошедших с начала отсчета — 1 января 1970 года. Для определения текущей отметки времени можно воспользоваться функцией time:
echo timeO;
Поскольку значение хранится в секундах, для получения метки времени ровно через неделю можно воспользоваться следующим выражением, в котором к возвращаемому значению прибавляется 7 дней х 24 часа х 60 минут х 60 секунд:
echo timeO + 7 * 24 * 60 * 60;
Если нужно получить отметку времени для заданной даты, можно воспользоваться функцией mkti me. Она выводит отметку времени 946684800 для первой секунды первой минуты первого часа первого дня 2000 года:
echo mktime(0, 0. 0. 1, 1. 2000);
Этой функции передаются следующие параметры (слева направо):
О количество часов (0-23);
О количество минут (0-59);
О количество секунд (0-59);
О номер месяца (1-12);
О номер дня (1-31);
О год (1970-2038 или 1901-2038 при использовании РНР 5.1.0 + 32-разрядной системы со знаком числа).
Вы можете спросить, а почему годы ограничены отрезком с 1970-го по 2038-й? Причина в том, что разработчики первой версии Unix выбрали 1970 год в качестве начала отсчета времени, J опускаться ниже которого не понадобится ни одному программисту. К счастью, благодаря тому, что РНР, начиная с версии 5.1.0, поддерживает системы, использующие 32-разрядные целые числа со знаком, в нем разрешается применение дат с 1901 до 2038 года. Но второе ограничение еще хуже первого и обусловлено тем, что разработчики Unix также решили, что по прошествии 70 лет никто уже не будет пользоваться их системой, и поэтому они были уверены, что для хранения отметки времени им вполне хватит 32-разрядного значения, которое будет вмещать даты только до 19 января 2038 года. Это ограничение вызовет сбой, известный как Y2K38 (очень похожий на проблему 2000 года, которая была вызвана тем, что года хранились в виде значений из двух цифр, и также требовала своего решения). Остается только надеяться на то, что ближе к этой дате будет найден приемлемый выход из положения.
Для отображения даты используется функция date, поддерживающая множество настроек форматирования, которые позволяют выводить дату любым желаемым способом. Эта функция имеет следующий синтаксис:
date($format. Stimestamp):
Параметр Sformat должен быть строкой, в которой содержатся спецификаторы форматирования, подробно описанные в табл. 7.4, а параметр $timestamp должен быть отметкой времени в стандарте Unix. Полный перечень спецификаторов приведен по адресу http://tinyurl.com/phpdatefuncs. Следующая команда выведет текущее время и дату в формате «Thursday April 15th, 2010 - 1:38pm»:
echo dateC'l F jS. Y - g:ia", timeO):
Таблица 7.4. Основные спецификаторы формата, использующиеся в функции date
Формат
Описание1
Возвращаемое значение
Спецификаторы дня
6
День месяца, две цифры с лидирующими нулями
От 01-до 31
D
День недели, составленный из трех букв
От Моп до Sun
j
День месяца без лидирующих нулей
От 1 до 31
1
День недели полностью
От Sunday до Saturday
N
День недели, число, от понедельника до воскресенья
От 1 до 7
S
Суффикс для дня месяца (пригодится в сочетании со спецификатором j)
st, nd, rd или th
W
День недели, число, от воскресенья до субботы
От 0 до 6
Z
День года
От 0 до 365
Спецификатор недели
W
Номер недели в году
От 01 до 52
Спецификаторы месяца
F
Название месяца
От January до December
m
Номер месяца с лидирующими нулями
От 01 до 12
М
Название месяца, составленное из трех букв
От Jan до Dec
п
Номер месяца без лидирующих нулей
От 1 до 12
t
Количество дней в заданном месяце
28, 29, 30 или 31
Формат1
Описание
Возвращаемое значение
Спецификаторы года
L
Високосный ГОД
1 - Да, 0 - Нет
Y
Год, четыре цифры
От 0000 до 9999
У
Год, две цифры
От 00 до 99
Спецификаторы времени
а
До или после полудня, в нижнем регистре
am или pm
А
До или после полудня, в верхнем регистре
AM или РМ
9
Час суток, 12-часовой формат без лидирующих нулей
От 1 до 12
G
Час суток, 24-часовой формат без лидирующих нулей
От 1 до 24
h
Час суток, 12-часовой формат с лидирующими нулями
От 01 до 12
Н
Час суток, 24-часовой формат с лидирующими нулями
От 01 до 24
i
Минуты с лидирующими нулями
От 00 до 59
S
Секунды с лидирующими нулями
От 00 до 59
Константы, связанные с датами
Существуют полезные константы, которые можно использовать с командами, связанными с датами, для того чтобы они вернули дату в определенном формате. Например, date(DATE RSS) возвращает текущую дату и время в формате, который используется в RSS-потоке. Наиболее часто используются следующие константы.
О DATE AT0M —формат для потоков Atom. PHP-формат имеет вид «Y-m-d\TH:i:sP», а выводимая информация —«2012-08-16Т12:00:00+0000».
О DATE COOKIЕ — формат для cookie, устанавливаемый веб-сервером или JavaScript. PHP-формат имеет вид «1, d-M-y H:i:s Т», а выводимая информация — «Thu, 16-Aug-2012 12:00:00 UTC».
О DATE RSS — формат для потоков RSS. PHP-формат имеет вид «D, d М Y H:i:s Т», а выводимая информация — «Thu, 16 Aug 2012 12:00:00 UTC».
О DATE_W3C —формат для консорциума Всемирной паутины, World Wide Web Consortium. PHP-формат имеет вид «Y-m-d\TH:i:sP», а выводимая информация - «2012-08-16Т12:00:00+0000».
Полный перечень приведен по адресу http://tinyurl.com/phpdates.
Использование функции checkdate
Как отобразить допустимую дату в различных форматах, вы уже видели, а как проверить, что пользователь передал такую дату вашей программе? Нужно передать месяц, день и год функции checkdate, которая вернет значение TRUE, если ей передана допустимая дата, или значение FALSE — в противном случае.
Например, если введена дата 30 февраля любого года, то она в любом случае будет недопустимой. В примере 7.3 показан код, который можно использовать для этой цели. В данных условиях он признает заданную дату недопустимой.
Пример 7.3. Проверка допустимости даты
<?php
$month = 9;// Сентябрьь ( (в нкотором 1только 30 I дней)
$day =31;// 31-е
$уеаг = 2012;// 20122
if (checkdate(Smonth, $day, Syear)) echo "Допустимая дата";
else echo "Недопустимая дата";
?>
Работа с файлами
При всех своих достоинствах MySQL не является единственным (или самым лучшим) способом хранения всех данных на веб-сервере. Иногда бывает быстрее и удобнее обращаться непосредственно к файлам, хранящимся на диске. Это может потребоваться при изменении изображений, например выложенных пользователями аватаров, или файлов регистрационных журналов, требующих обработки.
Прежде всего следует упомянуть об именах файлов. Если создается код, который может использоваться на различных установках РНР, то узнать о том, чувствительна система к регистру букв или нет, практически невозможно. Например, имена файлов в Windows и Mac OS X нечувствительны к регистру, а в Linux и Unix — чувствительны. Поэтому нужно принять за основу то, что система чувствительна к регистру, и придерживаться соглашения о присваивании файлам имен в нижнем регистре.
Проверка существования файла
Чтобы проверить факт существования файла, можно воспользоваться функцией file exists, которая возвращает либо TRUE, либо FALSE и используется следующим образом:
if (fi 1 e exists("te stfile .txt")) echo "Файл существует";
Создание файла
В данный момент файла testfi 1 е. txt не существует, поэтому создадим его и запишем в него несколько строк. Наберите код, показанный в примере 7.4, и сохраните его под именем testfi 1 е . php.
Пример 7.4. Создание простого текстового файла
<?php // te sItfi1е .php
$fh = fopen ("te stfile .txIt". V)) or die("Co3flaTb файл не удалось");
$text = «< END
Строка 1
Строка 2
Строка 3
JND;
fwrite($fh. $text) or d iе("Сбой записи файла"); fclose($fh);
echo "Файл 'tes I tfile .txt' записан успешно "; ?>
Если этот код будет запущен через браузер, то при его успешном выполнении будет получено следующее сообщение: «Файл 'testfile.txt' записан успешно». Если будет выведено сообщение об ошибке, значит, на диске недостаточно свободного места или, что более вероятно, отсутствует разрешение на создание файла или на запись в этот файл. В таком случае нужно изменить атрибуты папки назначения в соответствии с требованиями вашей операционной системы. Если все обойдется без ошибки, то файл testfi 1 е. txt попадет в ту же папку, где был сохранен программный файл testfi 1 е . php. Если открыть файл в текстовом или программном редакторе, в нем будет следующее содержимое:
Строка 1
Строка 2
Строка 3
В этом простом примере показана последовательность работы со всеми файлами.
Все начинается с открытия файла с помощью вызова функции fopen.
После этого можно вызывать другие функции. В данном случае в файл велась запись (fwrite), но можно также читать данные из уже существующего файла (fread или fgets) и осуществлять с ним другие действия.
Работа завершается закрытием файла (fcl ose). Хотя программа перед завершением своей работы делает это за вас, но все же вы должны удостовериться в том, что по окончании работы с файлом он будет закрыт.
Каждому открытому файлу требуется файловый ресурс, чтобы РНР-программа могла к нему обращаться и им управлять. В предыдущем примере переменной $fh (которую я выбрал в качестве описателя файла) присваивается значение, возвращаемое функцией fopen. После этого каждой функции обработки файла, которая получает к нему доступ, например fwrite или fcl ose, в качестве параметра должна быть передана переменная $fh, чтобы идентифицировать обрабатываемый файл. Интересоваться содержимым переменной Sfh не стоит, это всего лишь номер, используемый РНР для ссылки на внутреннюю информацию о файле. Данная переменная используется только для передачи другим функциям.
В случае сбоя функция fopen возвращает значение FALSE. В предыдущем примере показан простой способ перехвата управления и реакции на сбой: в нем вызывается функция di е, которая завершает программу и выдает пользователю сообщение об ошибке. Это упрощенный способ выхода подходит лишь для наших учебных программ, а выходить с его помощью из веб-приложения не следует ни в коем случае (вместо этого нужно создать веб-страницу с сообщением об ошибке).
Обратите внимание на второй параметр, используемый в вызове функции fopen. Это символ w, предписывающий функции открыть файл для записи. Если такого файла нет, то он будет создан. Применять эту функцию следует с оглядкой: если файл уже существует, параметр режима работы w заставит функцию fopen удалить все его прежнее содержимое (даже если в него не будет записано ничего нового!).
В табл. 7.5 перечислены различные параметры режима работы, которые могут быть использованы при вызове этой функции.
Таблица 7.5. Режимы работы, поддерживаемые функцией fopen
Режим
Действие
Описание
У
Чтение с начала файла
Открытие файла только для чтения; установка указателя файла на его начало. Возвращение FALSE, если файла не существует
'г+'
Чтение с начала файла с возможностью записи
Открытие файла для чтения и записи; установка указателя файла на его начало. Возвращение FALSE, если файла не существует
W
Запись с начала файла с усечением его размера
Открытие файла только для записи; установка указателя файла на его начало и сокращение размера файла до нуля. Если файла не существует, попытка его создания
'w+'
Запись с начала файла с усечением его размера и возможностью чтения
Открытие файла для чтения и записи; установка указателя файла на его начало и сокращение его размера до нуля. Если файла не существует, попытка его создания
'а'
Добавление к концу файла
Открытие файла только для записи; установка указателя файла на его конец. Если файла не существует, попытка его создания
'а+'
Добавление к концу файла с возможностью чтения
Открытие файла для чтения и записи; установка указателя файла на его конец. Если файла не существует, попытка его создания
Чтение из файлов
Проще всего прочитать текстовый файл, извлекая из него всю строку целиком, для чего, как в примере 7.5, используется функция fgets (последняя буква s в названии функции означает string —«строка»).
Пример 7.5. Чтение файла с помощью функции fgets
<?php
$fh = fopen Ctestfile .txtt". 'г') or
сИеСФайл не существует, или вы не обладаете правами на его открытие"):
Sline = fgets($fh):
fclose($fh):
echo Sline: ?>
Если используется файл, созданный кодом из примера 7.4, будет получена первая строка:
Строка 1
Можно также извлечь из файла сразу несколько строк или фрагменты строк, воспользовавшись функцией fread, как показано в примере 7.6.
Пример 7.6. Чтение файла с помощью функции fread
<?php
$fh = fopen("testfile .txt". 'r') or
die("Фaйл не существует, или вы не обладаете правами на его открытие"):
Stext = fread(Sfh. 3):
fclose(Sfh) :
echo Stext;
?>
При вызове функции fread было запрошено чтение трех символов, поэтому программа отобразит следующий текст:
Стр
Функция fread обычно попользуется для чтения двоичных данных. Но если она используется для чтения текстовых данных объемом более одной строки, следует брать в расчет с имволы новой строки.
Копирование файлов
Попробуем создать клон нашего файла testfi le .txt, воспользовавшись РНР-функ- uneii сору. Наберите текст примера 7.7 и сохраните его в файле copyfi 1 е. php, а затем вызовите программу через браузер.
Пример 7.7. Копирование файла
<?php // copyfi1е .php
copy('testfi 1e.txt’ . ’testtfile2.txtt’ ) or d ie("Копирование невозможно"):
echo "Файл успешно скопирован в 'te sttfile2.txtt'": ?>
Если еще раз проверить содержимое вашей папки, в ней окажется новый файл testfi 1 е2. txt. Кстати, если вам не нужно, чтобы программа завершала свою работу после неудачной попытки копирования, можно воспользоваться другим вариантом синтаксиса, который показан в примере 7.8.
Пример 7.8. Альтернативный синтаксис для копирования файла
<?php // copyfi1е2.php
if (!copy('te s1tfile . tx t'testfile2.txt')))) echo " Копирование невозможно";
else echo "Файл успешно скопирован в ’te sttfi1е2.t x t ;
?>
Перемещение файла
Для перемещения файла его следует переименовать, как показано в примере 7.9.
Пример 7.9. Перемещение файла
<?php // movefile.php
if ( !rename(’testtfile2 .txt'. 'testfile2.new'))
echo "Переименование невозможно";
else echo "Файл успешно переименован в 'te sttfi 1е2.new'"; ?>
Функцию переименования можно применять и к каталогам. Чтобы избежать предупреждений при отсутствии исходных файлов, сначала для проверки факта их существования можно вызвать функцию file exists.
Удаление файла
Для удаления файла из файловой системы достаточно, как показано в примере 7.10, воспользоваться функцией uni i nk, позволяющей сделать это.
Пример 7.10. Удаление файла
<?php // deletefi1e.php
if (!unlink(’testtfi 1e2.new’)) echo "Удаление невозможно ";
else echo "Файл 'testfile2.new' удален успешно";
?>
При непосредственном доступе к файлам на жестком диске нужна гарантия того, что ваша файловая система не будет поставлена под угрозу. Например, при удалении файла на основе введенной пользователем информации нужно быть абсолютно уверенными в том, что этот файл может быть удален без ущерба безопасности системы и что пользователю разрешено удалять его.
В данном случае, как и при операции перемещения, если файла с таким именем не существует, будет выведено предупреждение, появления которого можно избежать, если использовать функцию fi 1e_exi sts для проверки его существования перед вызовом функции uni i nk.
Обновление файлов
Довольно часто возникает потребность добавлять к сохраненному файлу дополнительные данные, для чего существует множество способов. Можно воспользоваться одним из режимов добавления данных (см. табл. 7.5) или же задействовать режим, поддерживающий запись, и просто открыть файл для чтения и записи и переместить указатель файла в то место, с которого необходимо вести запись.
Указатель файла — это позиция внутри файла, с которой будет осуществлен очередной доступ к файлу при чтении или записи. Его не следует путать с описателем файла (который в примере 7.4 хранился в переменной Sfh), содержащим сведения о том файле, к которому осуществляется доступ.
Если набрать код, показанный в примере 7.11, сохранить его в файле update. php, а затем вызвать его из своего браузера, то можно увидеть работу указателя.
Пример 7.11. Обновление файла
<?php // update.php
$fh = fopen("testfile .txt", 'r+') or die("Cбoй открытия файла");
Stext = fgets($fh);
fseek($fh, 0. SEEKJND):
fwrite($fh, "$text") or сИеС'Сбой записи в файл");
fclose($fh);
echo "Файл 'te s tfile .txtt‘ успешно обновлен";
?>
Эта программа открывает файл testfi 1 е. txt для чтения и записи, для чего указывается режим работы ' +г', в котором указатель устанавливается в самое начало файла. Затем используется функция fgets, с помощью которой из файла считывается одна строка (до встречи первого символа перевода строки). После этого вызывается функция fseek, чтобы переместить указатель файла в самый конец, куда затем добавляется строка, которая была извлечена из начала файла (и сохранена в переменной Stext), после чего файл закрывается. Получившийся в итоге файл имеет следующий вид:
Строка 1
Строка 2
Строка 3
Строка 1
Первая строка была успешно скопирована, а затем добавлена в конец файла.
В данном примере функции fseek, кроме описателя файла $fh, были переданы еще два параметра, 0 и SEEK_END. Параметр SEEK END предписывает функции переместить указатель файла в его конец, а параметр 0 показывает, на сколько позиций нужно вернуться назад из этой позиции. В примере 7.11 используется значение 0, потому что указатель должен оставаться в конце файла.
С функцией fseek можно использовать еще два режима установки указателя: SEEK_SET и SEEK_CUR. Режим SEEK_SET предписывает функции установку указателя файла на конкретную позицию, заданную предыдущим параметром. Поэтому в следующем примере указатель файла перемещается на позицию 18:
fseek($fh, 18. SEEK_SET);
Режим SEEK_CUR приводит к установке указателя файла на позицию, которая смещена от текущей позиции за заданное значение. Если в данный момент указатель файла находится на позиции 18, то следующий вызов функции переместит его на позицию 23:
fseek($fh, 5, SEEK_CUR);
Делать это без особой надобности не рекомендуется, но таким образом даже текстовые файлы (с фиксированной длиной строк) можно использовать в качестве простых неструктурированных баз данных. В этом случае ваша программа может использовать функцию fseek для перемещения в обе стороны по такому файлу для извлечения, обновления существующих и добавления новых записей. Записи также могут удаляться путем их перезаписи нулевыми символами и т. д.
Блокирование файлов при коллективном доступе
Веб-программы довольно часто вызываются многими пользователями в одно и то же время. Когда одновременно предпринимается попытка записи в файл более чем
одним пользователем, файл может быть поврежден. А когда один пользователь ведет в него запись, а другой считывает из него данные, с файлом ничего не случится, но читающий может получить весьма странные результаты.
Чтобы обслужить сразу несколько одновременно обращающихся к файлу пользователей, нужно воспользоваться функцией блокировки файла f1 ock. Эта функция ставит в очередь все другие запросы на доступ к файлу до тех пор, пока ваша программа не снимет блокировку. Когда ваши программы обращаются к файлу, который может быть одновременно доступен нескольким пользователям, с намерением произвести в него запись, к коду нужно также добавлять задание на блокировку файла, как в примере 7.12, который является обновленной версией примера 7.11.
Пример 7.12. Обновление файла с использованием блокировки
<?php
Sfh = fopen(”te stfile .txtt". 'r+') or die("Сбой открытия файла"):
Stext = fgets(Sfh):
if (flock(Sfh, LOCKJX))
{
fseek(Sfh, 0. SEEKJND);
fwrite (Sfh. "Stext") or сИеС'Сбой записи в файл"): flock(Sfh, LOCKJJN);
fclose(Sfh):
echo "Файл 'tesItfile .txt' успешно обновлен":
При блокировке файла для посетителей вашего веб-сайта нужно добиться наименьшего времени отклика: блокировку нужно ставить непосредственно перед внесением изменений в файл и снимать ее сразу же после их внесения. Блокировка файла на более длительный период приведет к неоправданному замедлению работы приложения. Поэтому в примере 7.12 функция f 1 ock вызывается непосредственно до и после вызова функции fwr i te.
При первом вызове f1 ock с помощью параметра L0CK_EX устанавливается эксклюзивная блокировка того файла, ссылка на который содержится в переменной Sfh:
flock (Sfh, LOCKJX):
С этого момента и далее никакой другой процесс не может осуществлять не только запись, но даже чтение файла до тех пор, пока блокировка не будет снята с помощью передачи функции параметра LOCKJJN:
flock(Sfh, LOCKJJN):
Как только блокировка будет снята, другие процессы снова получат возможность доступа к файлу. Это одна из причин, по которой необходимо заново обращаться к нужному месту в файле при каждом чтении или записи данных: со времени последнего обращения к нему другой процесс мог внести в этот файл изменения.
Кстати, вы заметили, что вызов с требованием эксклюзивной блокировки вложен в структуру инструкции i f? Дело в том, что f1 ock поддерживается не на всех систе-
мах, и поэтому перед внесением изменений есть смысл проверить успешность установки блокировки, так как известно, что некоторые системы на это не способны.
Следует также принять во внимание, что действия функции flock относятся к так называемой рекомендательной блокировке. Это означает, что блокируются только те процессы, которые вызывают эту функцию. Если есть код, который действует напрямую и изменяет файлы, не блокируя их с помощью flock, он всегда сможет обойти блокировку и внести хаос в ваши файлы.
Если в каком-то кодовом фрагменте заблокировать файл, а затем по рассеянности забыть его разблокировать, это может привести к ошибке, которую будет очень трудно обнаружить.
Функция flock не будет работать в сетевой файловой системе NFS и во многих других файловых системах, основанных на применении сетей. Не стоит полагаться на flock и при использовании многопоточных серверов типа ISAPI, потому что она не защитит файлы от доступа из кода PHP-сценариев, запущенных в параллельных потоках на том же физическом сервере. Кроме того, flock не поддерживается на любых системах, использующих устаревшую файловую систему FAT, например на устаревших версиях Windows.
Чтение всего файла целиком
Для чтения целиком всего файла без использования описателей файлов можно воспользоваться очень удобной функцией fi le_get_contents. Она очень проста в применении, о чем свидетельствует код примера 7.13.
Пример 7.13. Использование функции file_get_contents
<?php
echo ”<pre>"; // Тег. позволяющий отображать переводы строк
echo fi 1e_get_contents("testfi 1 e.txt"):
echo "</pre>": // Прекращение действия тега рге ?>
Но эту функцию можно использовать и с большей пользой. С ее помощью можно извлечь файл с сервера через Интернет. В примере 7.14 показан запрос кода HTML с главной страницы веб-сайта O’Reilly с последующим ее отображением, как при обычном переходе на саму веб-страницу. Полученный результат будет похож на копию страницы, приведенную на рис. 7.1.
Пример 7.14. Захват главной страницы веб-сайта O 'Reilly
<?php
echo f i 1e_get_contents("http ://orei1ly .com"); ?>
Загрузка файлов на веб-сервер
Загрузка файлов на веб-сервер вызывает затруднения у многих пользователей, но сделать этот процесс еще проще, чем он есть на самом деле, не представляется возможным. Для загрузки файла из формы нужно лишь выбрать специальный тип кодировки, который называется multipart/form-data, а все остальное сделает ваш
веб-браузер. Чтобы увидеть этот процесс в работе, наберите программу, представленную в примере 7.15, и сохраните ее в файле под именем upload.php. Когда этот файл будет запущен, в браузере появится форма, позволяющая загружать на сервер любой выбранный файл.веб-браузер. Чтобы увидеть этот процесс в работе, наберите программу, представленную в примере 7.15, и сохраните ее в файле под именем upload.php. Когда этот файл будет запущен, в браузере появится форма, позволяющая загружать на сервер любой выбранный файл.0© ■httpy'/lo«lhost/«amp!e7-14.php0© ■httpy'/lo«lhost/«amp!e7-14.phpO'Reilly MediaO'Reilly Mediaft&®ft&®file£drit yiew Favorites look fcjelpfile£drit yiew Favorites look fcjelpO’REILLYSpreading the knowledge of innovators.O’REILLYSpreading the knowledge of innovators.Your AccountYour Account.TSj Shopping Cart.TSj Shopping CartHome Shop Radar News & Commentary Answers Safari Books Online Conferences Training School of TechnologyHome Shop Radar News & Commentary Answers Safari Books Online Conferences Training School of TechnologyCommunityCommunityPopular Topics: Programming JavaScript IPhone Android Python HTML5 & CSS Microsoft Java Perl Linux DataPopular Topics: Programming JavaScript IPhone Android Python HTML5 & CSS Microsoft Java Perl Linux Data&&С О Si №С О Si №; Search; SearchSearchSearchBuy 2, Get the 3r FREEBuy 2, Get the 3r FREEAll order s over $29.' qualify for FREE SHIPPING within the USAll order s over $29.' qualify for FREE SHIPPING within the USNew & Top Python Titles
веб-браузер. Чтобы увидеть этот процесс в работе, наберите программу, представленную в примере 7.15, и сохраните ее в файле под именем upload.php. Когда этот файл будет запущен, в браузере появится форма, позволяющая загружать на сервер любой выбранный файл.
веб-браузер. Чтобы увидеть этот процесс в работе, наберите программу, представленную в примере 7.15, и сохраните ее в файле под именем upload.php. Когда этот файл будет запущен, в браузере появится форма, позволяющая загружать на сервер любой выбранный файл.
0© ■httpy'/lo«lhost/«amp!e7-14.php
0© ■httpy'/lo«lhost/«amp!e7-14.php
O'Reilly Media
O'Reilly Media
ft&®
ft&®
file£drit yiew Favorites look fcjelp
file£drit yiew Favorites look fcjelp
O’REILLY
Spreading the knowledge of innovators.
O’REILLY
Spreading the knowledge of innovators.
Your Account
Your Account
.TSj Shopping Cart
.TSj Shopping Cart
Home Shop Radar News & Commentary Answers Safari Books Online Conferences Training School of Technology
Home Shop Radar News & Commentary Answers Safari Books Online Conferences Training School of Technology
Community
Community
Popular Topics: Programming JavaScript IPhone Android Python HTML5 & CSS Microsoft Java Perl Linux Data
Popular Topics: Programming JavaScript IPhone Android Python HTML5 & CSS Microsoft Java Perl Linux Data
&
&
С О Si №
С О Si №
; Search
; Search
Search
Search
Buy 2, Get the 3r FREE
Buy 2, Get the 3r FREE
All order s over $29.' qualify for FREE SHIPPING within the US
All order s over $29.' qualify for FREE SHIPPING within the US
Save 50% - One Week Only
Ruthlessly Efficient. Infinitely Practical.
Python has it all: elegant yet pragmatic, simple yet powerful, high-level but it doesn't get bet ween you and your code. It's tht naturai choice of programmers who put productivity first. This week only save 50% on the top Python titles from O'Reilly.
В Н Ш З В 2 Days Left to Save
Shop Our Wide Selection of Books & VideosShop Our Wide Selection of Books & VideosProgramming Entity FrameworVDbContextEbook -S7.49(Save 50%)Programming Entity FrameworVDbContextEbook -S7.49(Save 50%)^100%
Shop Our Wide Selection of Books & Videos
Shop Our Wide Selection of Books & Videos
Programming Entity FrameworV
DbContext
Ebook -S7.49
(Save 50%)
Programming Entity FrameworV
DbContext
Ebook -S7.49
(Save 50%)
Рис. 7.1. Главная страница веб-сайта O'Reilly, захваченная с помощью функции fiie_get_contents
Пример 7.15. Программа для загрузки изображений, хранящаяся в файле upload.php
<?php // upload.php echo <«_END
<html><head><title>PHP-$opMa для загрузки файлов на cepBep</title></head><body>
<form method^post’ action=’upload.php’ enctype='multi part/form-data'>
Выберите файл: <input type= 'file' name='filename' size=’ 10' />
<input type='submit' value='Загрузить' />
</form>
_END:
i f ($_FILES)
{
Sname = $_FILES[,filenam e'][,name']:
move_uploaded_fi1e($_FILES[' f i 1ename’] [ 'tmp_name'] . $name):
echo "Загружаемое изображение '$name'<br /ximg src='$name' />” : }
echo "</body></html>"; ?>
Проанализируем программу по блокам. В первой строке многострочной инструкции echo задается начало HTML-документа, отображается заголовок, а затем начинается тело документа.
Далее идет форма, для передачи содержимого которой выбран метод POST, задается предназначение всех отправляемых программе upload.php (то есть самой нашей программе) данных и указывается веб-браузеру на то, что отправляемые данные должны быть закодированы с использованием типа содержимого multipart/ form-data.
Для подготовки формы в следующих строках задается отображение приглашения Выберите файл, а затем дважды запрашивается пользовательский ввод. Сначала от пользователя требуется указать файл. В параметрах ввода задаются тип вводимой информации — i nput type, в качестве которого указан файл — fi 1 е, имя — name, в качестве которого определено имя файла — fi 1 ename, а также размер поля ввода — size, в качестве которого указана ширина поля, составляющая десять символов.
Затем от пользователя требуется ввести команду на отправку данных формы, для чего служит кнопка с надписью Загрузить (эта надпись заменяет текст, используемый по умолчанию, — Submit Query, что означает «Отправить запрос»). После этого форма закрывается.
В этой небольшой программе показана весьма распространенная технология веб-программирования, в которой одна и та же программа вызывается дважды: один раз при первом посещении страницы, а второй — когда пользователь щелкает на кнопке отправки формы.
PHP-код, предназначенный для приема загружаемых данных, предельно прост, поскольку все загружаемые на сервер файлы помещаются в ассоциативный системный массив $_FILES. Поэтому для установки факта отправки пользователем файла достаточно проверить, есть ли у массива S FILES хоть какое- нибудь содержимое. Эта проверка осуществляется с помощью инструкции i f ($_FILES).
При первом посещении страницы пользователем, которое происходит еще до загрузки файла, массив $_FI LES пуст, поэтому программа пропускает этот блок кода. Когда пользователь загружает файл, программа запускается еще раз и обнаруживает присутствие элемента в массиве $_FI LES.
Когда программа обнаружит, что файл был загружен, его имя, каким оно было прочитано из компьютера, занимавшегося загрузкой, извлекается и помещается в переменную $name. Теперь нужно только переместить файл из временного места, где РНР хранит загруженные файлы, в постоянное место хранения. Это делается с помощью функции move_upl oaded_f11 е, которой передается исходное имя файла, сохраняемого в текущем каталоге.
И наконец, загруженное на сервер изображение отображается путем помещения его имени в тег <IMG>. Возможный результат показан на рис. 7.2.
Если при запуске программы в ответ на вызов функции move_uploaded_file будет получено предупреждение об отсутствии прав доступа — Permission denied, значит, у вас нет права на доступ к папке, из которой запущена программа.
Рис. 7.2. Загрузка изображения с помощью формы данных
Использование массива $_FILES
При загрузке файла на сервер в массиве $_FILES сохраняются пять элементов, показанных в табл. 7.6 (где используется загружаемый файл, имя которого предоставляется отправляемой серверу формой).
Таблица 7.6. Содержимое массива $_FILES
Элемент массива
Содержимое
$_FILES['file’]['name']
Имя загруженного файла (например, smiley.jpg)
$_FILES[,file'][’type']
Тип содержимого файла (например, image/jpeg)
$_FILES[,file'] ['size']
Размер файла в байтах
$_FILES[,file,][,tmp_name']
Имя временного файла, сохраненного на сервере
$_FILES['filel] ['error']
Код ошибки, получаемый после загрузки файла
Типы содержимого обычно называли MIME-типами (Multipurpose Internet Mail Extension —многоцелевые почтовые расширения в Интернете). Но поскольку позже они были распространены на все виды передаваемой через Интернет информации, то теперь их часто называют типами информации, используемой в Интернете (Internet media types). В табл. 7.7 показаны некоторые из наиболее часто используемых типов, которые появляются в элементе массива $_FILES['fi 1е'] ['type'].
Таблица 7.7. Некоторые наиболее распространенные типы информации, используемой в Интернете
application/pdf
image/gif
multipart/form-data
text/xml
application/zip
image/jpeg
text/css
video/mpeg
audio/mpeg
image/png
text/html
video/mp4
audio/x-wav
image/tiff
text/plain
video/quicktime
Проверка допустимости
Надеюсь, что не нужно говорить (хотя я все равно это сделаю) о крайней важности проверки допустимости присланных формой данных, обусловленной существующей для пользователей возможностью взломать ваш сервер.
Помимо проверки вредоносности введенных данных, нужно также проверить, был ли файл получен, и если он получен, то был ли отправлен правильный тип данных.
С учетом всего этого профамма upl oad. php была превращена в программу upl oad2. php, показанную в примере 7.16.
Пример 7.16. Более безопасная версия upload.php
<?php // upload2.php
echo <«_END
<html><head><title>PHP-(j)opMa для загрузки файлов на cepeep</title></head><body>
<form method='post' action='upload2.php' enctype='multipart/form-data’>
Выберите файл с расширением JPG. GIF. PNG или TIF:
<input type='fi1e ’ name=’filename’ size='10' />
<input type='submit1 уа!ие='3агрузить' /></form> _END;
i f ($_FILES) {
Sname = S_FILES['filename']['name'];
swi tch($_FILES[’fi 1ename'] [ 'type']) {
case'image/jpeg';$ext='jpg';break;
case'image/gif':$ext='g1if';break;
case'image/png’ :$ext='png';break;
case'image/itiff':$ext='ti f';break;
default;$ext='';break;
}
if ($ext)
{
$n = "image.$ext":
move_uploaded_fi1e($_FILES['f i 1ename'] ['tmp_name'], $n):
echo "Загружено изображение 'Sname' под именем '$n':<br />";
echo "<img src='Sn’ />";
}
else echo "'$name' - неприемлемый файл изображения";
}
else echo "Загрузки изображения не произошло";
echo "</body></html>";
?>
Блок, не содержащий HTML-кода, был расширен, и теперь вместо шести строк примера 7.15 в нем содержится 20 строк, начиная с i f ($_FILES).
Как и в предыдущей версии, в этой строке i f выполняется проверка факта отправки данных, но теперь у этой инструкции ближе к концу программы есть
и соответствующая ей инструкция el se, которая выводит на экран сообщение о том, что загрузки изображения не произошло.
В теле инструкции i f переменной Sname присваивается значение имени файла, полученное (как и прежде) от загружающего компьютера, но на этот раз мы не полагаемся на то, что пользователь отправил нам приемлемые данные. Вместо этого используется инструкция swi tch, предназначенная для проверки соответствия типа загружаемого контекста четырем типам изображений, которые поддерживаются этой программой. При обнаружении соответствия переменной $ext присваивается трехсимвольное расширение имени файла, относящееся к этому типу. Если соответствие не обнаружится, значит, загруженный файл не относится к приемлемому типу и переменной Sext будет присвоена пустая строка "".
В следующем блоке кода проверяется, содержит ли переменная $ext строку, и в случае положительного ответа в переменной $п создается новое имя файла, составленное из основы image и расширения, сохраненного в переменной $ext. Это означает, что программа полностью контролирует имя создаваемого файла и этим именем может быть только одно из следующих: image, jpg, image.gif, image.png или image.tif.
Поскольку программе больше ничего не угрожает, остальной PHP-код похож на код предыдущей версии. Он перемещает загруженное временное изображение на его новое место, затем выводит его на экран, а вместе с ним отображает старое и новое имена изображения.
Об удалении временного файла, созданного РНР в процессе загрузки, беспокоиться не стоит, поскольку, если файл не был перемещен или переименован, он будет удален автоматически, как только программа завершит свою работу.
Когда по условию инструкции if произойдет переход к инструкции else, которая выполняется только в том случае, если загружен неподдерживаемый тип изображения, программа выводит сообщение об ошибке.
Я настоятельно рекомендую применить такой же подход и использовать заранее подобранные имена и места для загружаемых файлов, когда вы будете создавать собственную программу загрузки. Тогда будут исключены любые попытки добавления к используемым переменным каких-нибудь других путевых имен и других данных, способных нанести вред. Если подразумевается, что несколько пользователей могут загружать файл с одним и тем же именем, то такие файлы можно снабжать префиксами, представляющими собой имена пользователей, или сохранять их в отдельных папках, созданных для каждого пользователя.
Но если нужно использовать предоставленное имя файла, его следует обезвредить, разрешив применение только буквенно-цифровых символов и точки, что можно сделать с помощью следующей команды, использующей регулярное выражение (см. главу 17) для осуществления поиска и замены символов в значении переменной Sname:
Sname = ereg_replace("[*A-Za-zO-9.]"f "". Sname):
Эта команда оставляет в строковой переменной Sname только символы A-Z, a-z, 0-9 и точку, а прочие символы удаляет.
Для обеспечения работы своей программы на всех системах, независимо от их чувствительности к регистру букв, стоит воспользоваться другой командой, которая одновременно с предыдущими действиями переводит все символы верхнего регистра в нижний:
Sname = strtolower(ereg_replace("[^A-Za-zO-9.]". Sname));
Иногда можно встретить тип содержимого image/pjpeg, который служит признаком прогрессивного JPEG-формата. Этот тип можно без лишних опасений добавить к вашему коду в качестве альтернативы для image/jpeg:
case 'image/pjpeg':
case 'image/jpeg': $ext = 'jpg'; break;
Системные вызовы
Иногда функцию для осуществления конкретного действия можно найти не в РНР, а в операционной системе, под управлением которой запущен этот язык. В таком случае для выполнения задачи можно применить системный вызов ехес.
Например, для быстрого просмотра содержимого текущего каталога можно воспользоваться программой, показанной в примере 7.17. В процессе работы в системе Windows она не потребует изменений и задействует Windows-команду dir. В Linux, Unix или Mac OS X нужно будет закомментировать или удалить первую строку и убрать символы комментария из второй строки, чтобы применить системную команду 1 s. При желании можете набрать текст этой программы, сохранить его как exec. php и вызвать из своего браузера.
Пример 7.17. Выполнение системной команды
<?php // exec.php
Scmd = "dir"; // Windows
// Scmd = "Is"; // Linux, Unix &Mac
exec(escapeshellcmd($cmd), Soutput, Sstatus);
if (Sstatus) echo "Команда exec не выполнена"; else
{
echo "<pre>";
foreach(Soutput as Sline) echo "$line\n";
} ?>
В зависимости от используемой системы в результате запуска этой программы будет выведена следующая информация (полученная при использовании Windows- команды di г):
Volume in drive С is HP
Volume Serial Number is E67F-EE11
Directory of C:\web
20/01/2011 10:34
19/01/201120/01/201120/01/201119/01/201119/01/201120/01/201119/01/201120/01/201120/01/201119/01/201119/01/201120/01/201120/01/201110:34
19/01/2011
20/01/2011
20/01/2011
19/01/2011
19/01/2011
20/01/2011
19/01/2011
20/01/2011
20/01/2011
19/01/2011
19/01/2011
20/01/2011
16:: 26236maketest.php
10::47198exec.php
08:: 0413.741smiley.jpg
18:: 0154test.php
16::5935testfile.tIxt
09:: 35886upload.php
6 File(s)15.150bytes
2 Dir(s) 382.907.748.352 bytes free
Функция exec воспринимает три аргумента.
Саму команду (в предыдущем случае это $cmd).
Массив, в который система поместит информацию, получаемую в результате выполнения команды (в предыдущем случае это $output).
Переменную для хранения возвращаемого статуса вызова (в предыдущем случае это Sstatus).
При желании параметры Soutput и Sstatus можно опустить, но тогда ничего не будет известно ни о выходной информации, созданной в результате вызова, ни даже о том, насколько успешен был сам вызов.
Обратите внимание также на применение функции escapeshel 1 cmd. Желательно выработать привычку постоянно использовать эту функцию при вызове функции ехес, поскольку она обезвреживает содержимое командной строки, предотвращая выполнение случайных команд в том случае, если пользователю предоставляется возможность их ввода.
Как правило, функции системных вызовов на веб-хостах общего пользования запрещены как представляющие угрозу системе безопасности. По возможности все задачи нужно стараться решить средствами РНР и обращаться к системе напрямую только в случае крайней необходимости. Кроме того, вы должны знать, что обращение к системе выполняется довольно медленно, и если приложение рассчитано на запуск как в Windows, так и в Linux/Unix, для него следует создавать две реализации вызова.
XHTML
Некоторые элементы XHTML (extensible Hypertext Markup Language — расширяемый язык гипертекстовой разметки) уже встречались в этой книге, хотя вы могли этого и не заметить. Например, вместо простого HTML-тега <br> я использовал его XHTML-версию <br /> В чем же разница между этими двумя языками разметки?
На первый взгляд, они не слишком отличаются друг от друга, но XHTML облагораживает HTML, проясняя множество мелких непоследовательных моментов, усложняющих обработку. Для HTML требуется очень сложный и весьма лояльный синтаксический анализатор (парсер), а для XHTML, в котором используется стан-
дартный синтаксис, больше напоминающий XML (extensible Markup Language — расширяемый язык разметки), нужна совсем несложная обработка с использованием очень простого парсера, то есть той части кода, которая обрабатывает теги и команды и определяет их предназначение.
Преимущества XHTML
Документы XHTML могут довольно быстро обрабатываться любой программой, способной работать с XML-файлами. При неизменном росте количества устройств, приобретающих способность обработки веб-контента (примером которых могут послужить iPhone и Blackberry, а также телефоны, работающие под управлением Android и Windows, не говоря уже о множестве новых планшетных устройств), обеспечение пристойного внешнего вида контента на этих устройствах, как, впрочем, и в веб-браузерах стационарных компьютеров и ноутбуков, приобретает все большее значение. Более компактный синтаксис, требующийся для XHTML, является весьма весомым фактором, содействующим такой кросс-платформенной совместимости.
Уже настали времена, когда разработчики браузеров, чтобы получить возможность поставлять все более быстрые и мощные программы, стараются подтолкнуть веб-разработчиков к использованию XHTML. И в итоге случится такое, когда на смену HTML придет XHTML, поэтому его использование лучше не откладывать в долгий ящик.
Версии XHTML
Стандарт XHTML постоянно развивается и использовались сразу несколько версий этого языка, но по тем или иным причинам единственной версией, с которой вам нужно разобраться, осталась XHTML 1.0.
Хотя были и другие версии XHTML (например, 1.1, 1.2 и 2.0), которые дошли до стадии рекомендованных и даже начали использоваться, ни одна из них не нашла достаточной поддержки у веб-разработчиков, что еще больше упрощает нашу с вами задачу, сводя ее к освоению только одной версии.
Отличие XHTML от HTML
Отличия XHTML от HTML описываются следующими правилами.
О Каждый тег должны быть закрыт другим тегом. В случае отсутствия соответствующего закрывающего тега тег должен закрывать сам себя, используя пробел, за которым следуют символы / и >. Так, к примеру, тег <input type='submtit> необходимо изменить, чтобы получился Ter<input type=' submit' />. Кроме того, все открывающие теги <р> теперь требуют закрывающих тегов </р>, которые нельзя заменить тегами <р />.
О Все теги должны быть правильно вложены. Поэтому строка <Ь>Меня зовут <i >Робин </ Ь></ i > недопустима, поскольку открытый тег <Ь> закрывается до закрытия тега <i>. Правильная версия строки — <Ь>Меня зовут <i >Робин</i></b>.
О Все атрибуты тегов должны быть взяты в кавычки. Вместо использования таких тегов, как <form method=post action=post .php>, нужно применять теги <form method=' post' acti on=' post. php' >. Можно также задействовать двойные кавычки: <formmethod="post" action="post.php">.
О Символ амперсанда (&) не может использоваться сам по себе. Например, строка Batpian & Robin должна быть заменена строкой Batman & Robin. Это означает, что URL-адреса также требуют модификации: HTML-синтаксис <а tiref="index.php? page=12&item=15"> необходимо заменить на <а href="index.php?
v page=12&item=15">.
О Теги XHTML чувствительны к регистру, и для них нужно использовать символы нижнего регистра. Поэтому такой HTML-код, как <B0DY><DIV ID="heading">, должен быть заменен следующим синтаксисом: <body><di v id="heading">.
О Теперь атрибуты не могут быть минимизированы, поэтому такие теги, как <option name="bill" selected>, должны быть заменены присваиваемым значением: <option name="bil1" selected="selected">. Все остальные атрибуты, например checked и disabled, также должны быть заменены на checked="checked", disabled="disabled" и т. д.
О Документы XHTML должны начинаться с помещаемого в самую первую строку XML-объявления. Эти объявления должны быть похожи на следующее: <?xml version=" 1.0" encoding="UTF-8"?>.
О Изменилось объявление DOCTYPE.
О Теперь тег <html > требует использования атрибута xml ns.
Рассмотрим документ, приведенный в примере 7.18, который оформлен в стандарте XHTML 1.0.
Пример 7.18. Документ XML
<?xml version*"1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml 1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equ i v="Content-Type" content="text/html; charset=utf-8" />
<Ш1е>Документ XHTML 1.0 </title>
</head>
<body>
<р>Это пример документа XHTML 1.0 </p>
<hl>3T0 заголовок</1п1>
<р>Это просто текст</р>
</body>
</html>
Как уже упоминалось, документ* начинается с XML-объявления, за которым следуют объявление DOCTYPE и тег <html > с атрибутом xml ns. Далее все выглядит как обычный код HTML, за исключением того, что тег meta закрыт, как и полагается, символами />.
Типы документов HTML 4.01
Чтобы дать браузеру точную информацию о том, как необходимо обрабатывать документ, используется объявление D0CTYPE, которое определяет допустимый синтаксис. Как следует из приводимых далее примеров, HTML 4.01 поддерживает три определения типа документа (Document Type Declarations (DTD)). Строгий DTD в примере 7.19 требует полного соблюдения синтаксиса HTML 4.01.
Пример 7.19. Строгий DTD в HTML 4.01
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
Нестрогий DTD в примере 7.20 разрешает использование некоторых устаревших элементов и не рекомендуемых атрибутов. (В стандартах, размещенных по адресу http://w3.org/TR/xhtmll, объясняется, какие элементы не рекомендуется использовать.)
Пример 7.20. Промежуточный DTD в HTML 4.01
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/1oose.dtd">
И наконец, в примере 7.21 обозначен документ HTML 4.01, содержащий фреймы.
Пример 7.21. Фреймовый DTD в HTML 4.01
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
"http://www.w3.org/TR/html4/frameset.dtd">
Типы документов HTML5
Использование типов документов в HTML5 существенно упростилось, поскольку теперь остался только один из них:
<!DOCTYPE html>
Одного простого слова html достаточно, чтобы сказать браузеру о том, что ваша веб-страница разработана для HTML5. Более того, поскольку все самые последние версии наиболее популярных браузеров начиная примерно с 2011 года поддерживают большинство спецификаций HTML5, растет вероятность того, что этот тип документа будет единственным, который вам понадобится, если только вы не выберете в качестве своей задачи обслуживание устаревших браузеров.
Типы документов XHTML 1.0
Возможно, прежде вам уже встречались HTML-документы одного или нескольких типов. Но с появлением XHTML 1.0 синтаксис претерпел небольшие изменения, показанные в следующих примерах.
Строгий DTD в примере 7.22 исключает применение нерекомендуемых атрибутов и требует использования только правильного кода.
Пример 7.22. Строгий DTD в XHTML 1.0
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtnil 1/DTD/xhtml l-strict.dtd">
Промежуточный DTD в XHTML 1.0, показанный в примере 7.23, позволяет использовать нерекомендуемые атрибуты, и это самый востребованный DTD.
Пример 7.23. Промежуточный DTD в XHTML 1.0
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtmll/DTD/xhtmll-transitional.dtd">
В примере 7.24 показан единственный XHTML 1.0 DTD, поддерживающий фреймы.
Пример 7.24. Фреймовый DTD в XHTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml 1- frameset.dtd">
Проверка правильности XHTML
Чтобы проверить код XHTML, следует посетить сайт проверки правильности W3C по адресу http://validator.w3.org, с помощью которого можно проверить документ по URL-адресу путем загрузки формы или путем его набора или копирования и вставки в веб-форму. Перед разработкой кода РНР, предназначенного для создания вебстраницы, передайте выборку выходной информации, которую хотите создавать, на сайт проверки правильности. Независимо от того, насколько тщательно вы создавали свой XHTML-код, вы будете удивлены обилию пропущенных ошибок. 4
Если документ не полностью совместим с XHTML, вы получите сообщение, объясняющее, как это можно исправить. На рис. 7.3 показано, что документ из примера 7.18 успешно прошел проверку на соответствие строгому XHTML 1.0.
3 http://validator.w3.org/check7unrhttp%3A%2F1* Р ” § С X 3 [Valid] Markup Validation о. х La file £dit ¥iew Favorites loots fcjdp
This document was successfully checked as XHTML 1,0 Strict!
Result: i Passed
Address : ; jhtfcp://validator.w3.org/
Encoding : i utf-8I (detect automaticatly)
Doctype: j XHTML 1.0 Strict| (detect automatically)
Root Elementhtml
Root Namespace:http://www.w3.orq/1999/xhtml
The W3C validators are hosted on server technology donated by HP, andsupported by community donations.
Donate and help us build better tools for a better web.
^ 100% -
Рис. 7.3. Документ из примера 7.18, прошедший проверку правильности
#4 Оказывается, документы XHTML 1.0 настолько близки к HTML, что даже при вызове в брау- ' I зере, который не знаком с XHTML, они будут отображаться правильно. Но есть одна по- *х J тенциальная проблема, связанная с тегом <script>. Для обеспечения совместимости сле- ilft дует избегать использования синтаксиса <script src="script.src" />, заменяя его синтаксисом <script src=,,script.src"></script>.
В этой главе мы проделали еще одно длинное путешествие на пути овладения языком РНР. Теперь у вас появилось представление о форматировании текста, работе с файлами, XHTML и т. д.
Проверьте ваши знания
Какой спецификатор преобразования следует использовать в функции pri ntf для отображения числа с плавающей точкой?
Какая инструкция pri ntf может быть использована для приема строки Happy Bi rthday и вывода строки **Нарру?
Какой альтернативной функцией следует воспользоваться для выдачи информации из pri ntf не в браузер, а в переменную?
Как создать отметку времени Unix для времени и даты, представленных в виде «7:11аш May 2nd, 2016»?
Какой режим доступа к файлу следует использовать в функции fopen, чтобы открыть файл в режиме чтения и записи с усечением его размера и установкой указателя на начало файла?
Какую PHP-команду следует применить для удаления файла fi 1 е. txt?
Какая PHP-функция используется для чтения целиком всего файла и даже для извлечения его из Всемирной паутины?
В какой системной переменной РНР содержатся сведения о загруженных на сервер файлах?
Какая PHP-функция позволяет запускать системные команды?
Чтооне£соответствуетТправиламИввследующемитегеЕXHTMLL1.0: <inputttype=file е name=file size=10>?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 7».
О Введение в MySQL
Более чем 10 млн установленных на компьютерах копий MySQL позволяют, наверное, считать ее наиболее популярной системой управления базами данных для веб-серверов. Она была разработана в середине 1990-х годов и теперь превратилась в полноценную технологию, входящую в состав многих современных, наиболее посещаемых интернет-ресурсов.
Одна из причин такого успеха — это то, что она, как и РНР, является продуктом свободного пользования. Кроме того, это очень мощная и исключительно быстрая система, способная работать даже на самом скромном оборудовании, не отнимая слишком много системных ресурсов.
Система MySQL обладает также хорошей масштабируемостью, то есть база данных может увеличиваться в объеме вместе с вашим веб-сайтом. Сравнительный анализ нескольких баз данных, проведенный журналом eWEEK, показал, что MySQL и Oracle получили равное количество голосов за лучшую производительность и самый высокий показатель масштабируемости.
Основные характеристики MySQL
База данных — это структурированная коллекция записей или данных, хранящихся в компьютерной системе и организованных так, что можно осуществлять быстрый поиск и извлечение нужной информации.
В названии MySQL составляющая SQL означает Structured Query Language — «язык структурированных запросов». Если характеризовать его в общих чертах, то это язык, основанный на словах английского языка и используемый также в других системах управления базами данных, например Oracle и Microsoft SQL Server. Он разработан для предоставления возможности создания простых запросов к базе данных посредством команд следующего вида:
SELECT title FROM publications WHERE author = 'Charles Dickens':
В базе данных MySQL имеются одна или несколько таблиц, каждая из которых состоит из записей, или строк. Внутри строк находятся разные столбцы, или поля, в которых и содержатся данные. В табл. 8.1 показана учебная база данных, в которой присутствует информация о пяти книгах, структурированная по авторам, названиям, категориям и годам издания.
Таблица 8.1. Пример простой базы данных
Author (автор)
Title (название)
Type (категория)
Year(год)
Mark Twain (Марк Твен)
The Adventures of Tom Sawyer (Приключения Тома Сойера)
Fiction
(Художественная)
1876
Jane Austen (Джейн Остин)
Pride and Prejudice (Гордость и предубеждение)
Fiction
(Художественная)
1811
Charles Darwin (Чарльз Дарвин)
The Origin of Species (Происхождение видов)
Non-Fiction (Научная)
1856
Charles Dickens (Чарльз Диккенс)
The Old Curiosity Shop (Лавка древностей)
Fiction
(Художественная)
1841
William Shakespeare (Вильям Шекспир)
Romeo and Juliet
(Ромео и Джульетта)
Play (Пьеса)
1594
Каждая строка таблицы подобна строке в таблице MySQL, и каждый элемент в этой строке подобен полю MySQL.
Чтобы однозначно идентифицировать эту базу данных, в последующих примерах я буду ссылаться на нее как на базу данных publicati I ons (издания). Как вы уже заметили, все эти издания относятся к классической литературе, поэтому таблицу в базе данных, содержащую сведения о них, я буду называть classics.
Сводка понятий, используемых в базах данных
Основными понятиями, с которыми следует ознакомиться на данном этапе, являются:
О база данных — контейнер для всей коллекции данных MySQL;
О таблица — вложенный в базу данных контейнер, в котором хранятся сами данные;
О строка — отдельная запись, в которой могут содержаться несколько полей;
О столбец — имя поля внутри строки.
Следует заметить, что я не пытаюсь воспроизвести точную терминологию, используемую в учебной литературе по реляционным базам данных, а хочу лишь дать простые, обычные определения, помогающие быстро усвоить основные понятия и приступить к работе с базой данных.
Доступ к MySQL из командной строки
Работать с MySQL можно тремя основными способами: используя командную строку, применяя веб-интерфейс наподобие phpMyAdmin и задействуя такой язык программирования, как РНР. Третий из перечисленных способов будет рассмотрен в главе 10, а сейчас изучим первые два способа.
Начало работы с интерфейсом командной строки
В следующих разделах даны соответствующие инструкции для операционных систем Windows, Mac OS X и Linux.
Для пользователей Windows
Если у вас в соответствии с инструкциями, изложенными в главе 2, установлена программа Zend Server СЕ WAMP, то доступ к исполняемой программе MvSQL можно получить из следующих каталогов (первый из которых относится к 32-раз- рядным, а второй —к 64-разрядным компьютерам):
С:\Program Fi1es\Zend\MySQL51\bin
C:\Program Files (x86)\Zend\MySQL51\bin
Если Zend Server СЕ установлена не в каталоге \Program Files (или \Program Files (х86)), то нужно воспользоваться тем каталогом, в котором она установлена.
По умолчанию начальным для MySQL будет пользователь по имени root, у которого не установлен пароль. Учитывая то, что это разработочный сервер, доступ к которому можете получить только вы, мы не станем устанавливать пароль.
Чтобы войти в интерфейс командной строки MySQL, следует выбрать команду Пуск ► Выполнить и в окне запуска ввести команду CMD, после чего нажать клавишу Enter. В результате будет вызвано командное окно Windows. Находясь в этом окне, следует ввести следующую команду (внося в нее соответствующие коррективы):
"C:\Program Files\Zend\MySQL51\bin\mysql" -u root
"C:\Program Files (x86)\Zend\MySQL51\bin\mysql" -и root
tit ч
1 :
#% Обратите внимание на кавычки, в которые заключены основной путь и имя файла. Их приму А « сутствие обусловлено наличием пробелов, которые неправильно интерпретируются коман- дной строкой, а кавычки группируют составные частиЕимени файла в единую строку, понят- понятную программе Command.
Эта команда предписывает MySQL зарегистрировать вас как пользователя root без пароля. Теперь вы должны оказаться в среде MySQL и сможете приступить к вводу команд. Чтобы убедиться в том, что все работает должным образом, введите следующую команду, результат выполнения которой должен быть похож на показанный на рис. 8.1:
SHOW databases;
Если это не приведет к желаемому результату и будет выдано сообщение об ошибке, убедитесь в том, что у вас система MySQL правильно установлена наряду с Zend Server СЕ (как описано в главе 2). Если же все прошло успешно, переходите к подразделу «Использование интерфейса командной строки».
В | C:\Windows\system32\cmd.exeВ | C:\Windows\system32\cmd.exe। Hi Ш & м '। Hi Ш & м 'Рис. 8.1. Доступ к MySQL из командной строки WindowsРис. 8.1. Доступ к MySQL из командной строки Windows
В | C:\Windows\system32\cmd.exe
В | C:\Windows\system32\cmd.exe
। Hi Ш & м '
। Hi Ш & м '
Рис. 8.1. Доступ к MySQL из командной строки Windows
Рис. 8.1. Доступ к MySQL из командной строки Windows
Для пользователей Mac OS X
Чтобы иметь возможность выполнять то, о чем говорится в этой главе, нужно в соответствии с инструкциями, изложенными в главе 2, установить программу Zend Server СЕ. Следует также иметь работающий веб-сервер с запущенным сервером MySQL.
Для входа в интерфейс командной строки MySQL необходимо запустить программу Terminal (доступную в меню Utilities программы Finder). Затем вызвать программу MySQL, которая должна быть установлена в каталоге /usr/local/zend/ mysql/bin.
По умолчанию исходным пользователем для MySQL будет пользователь по имени root, и пароль у него также будет root. Поэтому для запуска программы наберите следующую команду:
/usr/loca-1/zend/mysql/bin/mysql -u root
Эта команда предпишет MySQL зарегистрировать вас как пользователя root и запросить пароль. Чтобы проверить, что все в порядке, наберите следующую команду (результаты ее выполнения показаны на рис. 8.2):
SHOW databases;
Если будет получено сообщение об ошибке, не позволяющей подключиться к локальному серверу MySQL (Can't connect to local MySQL server through socket), значит, у вас не запущен сервер MySQL. Убедитесь в том, что вы последовали совету в главе 2, касавшемуся настройки MySQL на запуск вместе с запуском OS X.
Теперь вы готовы перейти к подразделу «Использование интерфейса командной строки».
Для пользователей Linux
На машинах, работающих под управлением Unix-подобных операционных систем, к которым относится и Linux, РНР и MvSQL практически всегда будут
заранее установлены и запущены, позволяя вводить текст примеров следующего раздела. Но сначала нужно войти в систему MySQL, введя следующую команду:
mysql -u root -р
iMac:zend robin$ show databases;
-bash: show: command not found
iMac:zend robin$ /usr/local/zend/mysql/bin/mysql -u root
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.1.54 MySQL Community Server (GPL)
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This software comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to modify and redistribute it under the GPL v2 license
Type ’help;' or '\h‘ for help. Type 4 c ‘ to clear the current input statement.
mysql> show databases;
| Oatabase|
++
| information_schema |
I nysqlj
I test|
3 rows in set (0.00 sec)
mysql> |
Рис. 8.2. Доступ к MySQL из программы Terminal на OS X
Эта команда предписывает MySQL зарегистрировать вас под именем root и запросить пароль. Если у вас есть пароль, то нужно его ввести, если пароль отсутствует — просто нажать клавишу Enter.
После входа в систему нужно проверить работоспособность программы, набрав следующую команду, примерный результат выполнения которой показан на рис. 8.3:
SHOW databases:
Если по каким-то причинам эта команда не сработает, обратитесь к разделу главы 2 «Установка LAMP на Linux», чтобы обеспечить правильную установку MySQL.
Если команда будет выполнена, можете перейти к подразделу «Использование интерфейса командной строки».
MySQL на удаленном сервере
При получении доступа к MySQL на удаленном сервере нужно выйти на удаленную машину с помощью Telnet (с точки зрения безопасности предпочтительнее для этого воспользоваться SSH), которая, скорее всего, будет работать под управлением операционной системы семейства типа Linux/FreeBSD/Unix. После подключения к удаленной машине можно встретиться с незначительными вариациями в порядке работы, зависящими от настроек сервера, которые выполнены системным администратором, особенно если этот сервер предназначен для коллективного пользования.
Поэтому следует убедиться в доступности MySQL и в том, что у вас есть имя пользователя и пароль. Получив эти сведения, можно набрать следующую команду, в которой вместо username следует вставить предоставленное имя пользователя:
mysql -u username -р
Рис. 8.3. Доступ к MySQL в Linux
После появления приглашения следует ввести пароль. Затем можно попробовать ввести следующую команду, примерный результат выполнения которой показан на рис. 8.3:
SHOW databases;
В перечне баз данных могут присутствовать pi другие ранее созданные базы, среди которых базы test может и не оказаться.
Следует также понимать, что все находится под полным контролем системного администратора, и вы можете столкнуться с некоторыми неожиданными настройками. Например, может оказаться, что вам следует ставить перед именами всех создаваемых вами баз данных уникальную идентификационную строку, обеспечивающую их бесконфликтную работу с базами данных, созданными другими пользователями.
При возникновении любых проблем нужно переговорить с системным администратором, который должен с ними разобраться. У него нужно запросить имя пользователя и пароль и попросить дать вам возможность создавать новые базы данных или как минимум попросить создать для вас хотя бы одну готовую к работе базу данных. Тогда в этой базе можно будет создать все необходимые таблицы.
Использование интерфейса командной строки
Для всего, что изложено далее в тексте главы, нет никакой разницы, из какой именно системы — Windows, Mac OS X или Linux —вы получаете непосредственный
доступ к MySQL, поскольку все используемые команды (и сообщения об ошибках, которые могут быть получены) абсолютно одинаковы.
Точка с запятой
Начнем с самого простого. Набирая команду, вы, наверное, заметили точку с запятой (;) в конце SHOWdatabases; ? Этот символ используется в MySQL для завершения команд или отделения их друг от друга. Если забыть поставить этот символ, MySQL выдаст приглашение и будет ожидать от вас его ввода. Запрашиваемая точка с запятой стала частью синтаксиса, позволяющего вводить длинные команды, разбивая их на несколько строк. Она также позволяет вводить сразу несколько команд, после каждой из которых стоит точка с запятой. После нажатия клавиши Enter интерпретатор получит все эти команды в едином пакете и выполнит их в порядке следования.
at -м
Вместо результата введенной команды довольно часто появляется приглашение MySQL. Это «, означает, что вы забыли поставить завершающую точку с запятой. В таком случае нужно просто ввести точку с запятой, нажать клавишу Enter, и вы получите желаемый результат.
На экране могут появляться шесть разных приглашений MySQL (табл. 8.2), позволяющих определить, на каком именно этапе многострочного ввода вы находитесь.
Таблица 8.2. Шесть приглашений к вводу команды MySQL
Приглашение MySQL
Значение
mysql >
MySQL готова к работе и ждет ввода команды
->
Ожидание следующей строки команды
'>
Ожидание следующей строки строкового значения, которое начиналось с одинарной кавычки
">
Ожидание следующей строки строкового значения, которое начиналось с двойной кавычки
' >
Ожидание следующей строки строкового значения, которое начиналось с символа засечки (')
/*>
Ожидание следующей строки комментария, который начинался с символов /*
Отмена команды
Если, набрав часть команды, вы решили, что ее вообще не следует выполнять, то ни в коем случае не нужно пользоваться сочетанием Ctr+C! Оно закроет программу. Вместо него можно ввести символы \с и нажать клавишу Enter. Порядок использования этой пары символов показан в примере 8.1.
Пример 8.1. Отмена ввода строки бессмысленная для mysql строка \с
При наборе этой строки MySQL проигнорирует все ранее введенные символы и выдаст новое приглашение. Без \с программа выведет сообщение об ошибке. Но этой парой символов нужно пользоваться с оглядкой: если у вас уже есть откры-
тая строка или комментарий, то прежде, чем применить \с, вам придется их закрыть, иначе MySQL примет \с за часть строки. В примере 8.2 показано, как в таком случае следует задействовать \с.
Пример 8.2. Отмена ввода из строки
это "бессмысленная для mysql строка" \с
Следует также заметить, что комбинация \с после точки с запятой работать не будет, поскольку это уже будет новая инструкция.
Команды MySQL
Нам уже приходилось встречаться с командой SHOW, которая выводит список таблиц, баз данных и многих других элементов. В табл. 8.3 приведен перечень наиболее востребованных команд.
Таблица 8.3. Подборка наиболее востребованных команд MySQL
Команда
Параметр(ы)
Назначение
ALTER
База данных, таблица
Внесение изменений в базу данных или таблицу
BACKUP
Таблица
Создание резервной копии таблицы
\с
Отмена ввода
CREATE
База данных, таблица
Создание базы данных или таблицы
DELETE
Выражение с участием таблицы и строки
Удаление строки из таблицы
DESCRIBE
Таблица
Описание столбцов таблиц
DROP
База данных, таблица
Удаление базы данных или таблицы
EXIT (Ctrl+C)
Выход
GRANT
Пользователь подробности
Изменение привилегий пользователя
HELP (\h, \?)
Элемент
Отображение подсказки по элементу
INSERT
Выражение с данными
Вставка данных
LOCK
Таблица (таблицы)
Блокировка таблицы (таблиц)
QUIT (\q)
То же самое, что и EXIT
RENAME
Таблица
Переименование таблицы
SHOW
Множество элементов для вывода в виде списка
Список сведений об элементах
SOURCE
Имя_файла
Выполнение команд из файла имя_файла
STATUS (\s)
Отображение текущего состояния
TRUNCATE
Таблица
Опустошение таблицы
UNLOCK
Таблица (таблицы)
Снятие блокировки таблицы (таблиц)
UPDATE
Выражение с данными
Обновление существующей записи
USE
База_данных
Использование базы_данных
Многие из этих команд будут рассмотрены по мере изучения этой главы, но сначала следует запомнить два важных положения, касающихся команд MySQL.
О Команды и ключевые слова SQL нечувствительны к регистру. Все три команды — CREATE, create и СгЕаТе — абсолютно идентичны по смыслу. Но, чтобы было понятнее, для команд рекомендуется использовать буквы верхнего регистра.
О Имена таблиц не чувствительны к регистру в Windows, но чувствительны к регистру в Linux и Mac OS X. Поэтому из соображений переносимости нужно всегда выбирать буквы одного из регистров и пользоваться только ими. Для имен таблиц рекомендуется использовать буквы нижнего регистра или комбинацию из букв верхнего и нижнего регистра.
Создание базы данных
Если вы работаете на удаленном сервере, у вас только одна учетная запись пользователя и вы имеете допуск только к одной созданной для вас базе данных, то можете перейти к изучению пункта «Создание таблицы» далее. А если это не так, то продолжим, выдав следующую команду для создания новой базы данных по имени publications:
CREATE DATABASE publications;
При успешном выполнении команды будет выведено сообщение, пока не имеющее для нас особого смысла, —Query OK, 1 row affected (0.38 sec) (Запрос выполнен, обработана 1 строка за 0,38 с), но вскоре все станет на свои места. После создания базы данных с ней нужно будет работать, поэтому даем следующую команду:
USE publications:
Теперь должно быть выведено сообщение об изменении текущей базы данных (Database changed), и после этого база будет готова к продолжению работы со следующими примерами.
Организация доступа пользователей
Теперь, когда вы уже убедились в том, насколько просто пользоваться MySQL, и создали свою первую базу данных, настало время посмотреть на то, как происходит организация доступа пользователей, поскольку, вполне вероятно, вам не захочется предоставлять PHP-сценариям привилегированный доступ (root) к MySQL, что грозит большими неприятностями в случае, если кому-то вздумается взломать ваш веб-сайт.
Для создания нового пользователя выдается команда предоставления прав — GRANT, которая принимает следующую форму (не вздумайте все это набирать, поскольку это еще не команда):
GRANT ПРАВА ON база_данных.объект ТО ’имя_пользователя@имя_хоста'
IDENTIFIED BY 1 пароль1:
Эта форма не должны вызвать каких-либо затруднений, быть может, за исключением фрагмента база данных. объект. Это ссылка на саму базу данных и на содержащиеся в ней объекты, например на таблицы (табл. 8.4).
Таблица 8.4. Примерные параметры для команды GRANT
Параметр
Значение
* *
Все базы данных и все их объекты
база_данных*
Только база данных с именем база_данных и все ее объекты
база_данных.объект
Только база данных с именем база_данных и ее объект с именем объект
Итак, создадим пользователя, который получит доступ только к новой базе данных publications и ко всем ее объектам, и введем для этого следующую команду (заменив в ней имя пользователя jim и пароль mypasswd выбранными вами именем и паролем): GRANT ALL ON publications.* TO 'jim'@’1ocalhost'
IDENTIFIED BY 'mypasswd':
Эта команда предоставляет пользователю jim@l ocal host полный доступ к базе данных publications при использовании пароля mypasswd. Работоспособность этой установки можно проверить, если ввести команду QUIT для выхода из системы, а затем перезапустить MySQL, воспользовавшись прежним способом запуска, но вместо -u root -р набрав - и jim - р или использовав в этой строке созданное вами имя пользователя. В табл. 8.5 показаны команды, соответствующие используемой вами операционной системе, при этом предполагается, что вы установили Zend Server СЕ (как указано в главе 2), но если на вашей системе MySQL-клиент установлен в другой каталог, то в команду следует внести соответствующие коррективы.
Таблица 8.5. Запуск MySQL и вход в систему под именем jim(3>localhost
Операционная система
Пример команды
32-разрядная версия Windows
"C:\Program Files\Zend\MySQL51\bin\mysql" -u jim -p
64-разрядная версия Windows
"C:\Program Files (x86)
\Zend\MySQL51\bin\mysql" -u jim -p
Mac OS X
/usr/local/zend/mysql/bin/mysql -u jim -p
Linux
mysql -u jim -p
Теперь, как только появится приглашение, нужно лишь ввести свой пароль, и вход в систему будет открыт. Кстати, при желании можете поместить пароль сразу же после ключа -р (не используя никаких пробелов). Тем самым вы избежите его ввода после появления приглашения. Но такой подход не приветствуется, поскольку, если в вашей системе зарегистрировались и другие пользователи, они могут подсмотреть вводимую вами команду и получить доступ к вашему паролю..
-**^—I
Вы можете предоставлять только те права, которыми уже обладаете, и должны иметь пра- О*- J « во на ввод команд GRANT. Этим и ограничивается выбор предоставляемых вам прав, если
-——A*!* только вам не предоставлены абсолютно все права. Если вас интересуют подробности, то, пожалуйста, зайдите на сайт, где, кроме всего прочего, описана и команда REVOKE, позволяющая отозвать уже предоставленные права: http://tinyurl.com/mysqlgrant.
Вам также следует знать, что, если при создании нового пользователя не будет указана инструкция IDENTIFIED BY, у пользователя не будет пароля, из-за чего возникнет неблагоприятная с точки зрения безопасности ситуация, которой следует избегать.
Создание таблицы
В данный момент вы должны находиться в системе MySQL, обладать всеми (ALL) правами, выделенными для базы данных publications (или той базы данных, которая была для вас создана), и быть готовыми к созданию своей первой таблицы. Поэтому нужно включить базу данных в работу, набрав следующую команду (и заменив publications именем своей базы данных, если оно у нее другое):
USE publications:
Теперь наберите построчно команды, которые приведены в примере 8.3.
Пример 8.3. Создание таблицы по имени classics
CREATE TABLE classics (
author VARCHAR(128),
title VARCHAR(128),
type VARCHAR(16).
year CHAR(4)) ENGINE MyISAM;
Эту же команду можно ввести одной строкой:
CREATE TABLE classics (author VARCHAR(128). title
b' VARCHAR(128). type VARCHARC16). year CHAR(4)) ENGINE MyISAM;
но команды MySQL могут быть длинными и сложными, поэтому я рекомендую набирать их построчно до тех пор, пока вы не привыкнете к набору длинных строк.
После ввода команды MySQL должна выдать ответ: Query ОК. О rows affected, а также показать время, затраченное на выполнение команды. Если вместо этого появится сообщение об ошибке, внимательно проверьте синтаксис команды. Должны быть на месте все скобки и запятые, а может быть, допущена какая-нибудь опечатка. Команда ENGINE My I SAM, которая своим присутствием в примере могла вызвать у вас недоумение, указывает MySQL тип механизма управления базой данных, применяемого к этой таблице.
Чтобы проверить факт создания новой таблицы, наберите команду:
DESCRIBE classics;
Если все в порядке, то вы увидите последовательность команд и ответов, показанных в примере 8.4, в которой особое внимание следует обратить на отображение формата таблицы.
Пример 8.4. Сеанс работы с MySQL: создание и проверка формата новой таблицы mysql> USE publications;
Database changed
mysql> CREATE TABLE classics (
> author VARCHAR(128).
> title VARCHAR(128),
> type VARCHAR(16).
> year CHAR(4)) ENGINE MyISAM:
Query OK, 0 rows affected (0.03 sec)
mysql> DESCRIBE classics;
Field _| Туре_. | Null . | Key | Default | Extra
author _|varchar(128) _|YE_S_||NULL|
Ititlejvarchar(128)jYESjjNULL|I
Itypejvarchar(16)|YESj|NULL|I
Iyearjchar(4)jYES||NULL|I
+ + - + - - - -+++ -+
4 rows in set (0.00 sec)
Команда DESCRIBE является неоценимым средством отладки, когда нужно убе-. диться в успешном создании таблицы MySQL. Этой командой можно воспользоваться также для того, чтобы просмотреть имена полей или столбцов таблицы и типы данных в каждом из них. Рассмотрим подробнее все заголовки:
О Field — имя каждого из полей или столбцов таблицы;
О Туре — тип данных, сохраняемых в поле;
О Nul 1 — показывает, может ли поле содержать значение NULL;
О Key — MySQL поддерживает ключи, или индексы, позволяющие ускорить просмотр и поиск данных. Под заголовком Key показан тип применяемого ключа (если таковой имеется);
О Default — исходное значение, присваиваемое полю, если при создании новой строки не указано никакого значения;
О Extra — дополнительная информация, например о настройке поля на автоматическое приращение его значения.
Типы данных
В примере 8.3 можно было заметить, что для трех полей таблицы объявлены типы данных VARCHAR, а для одного — тип данных CHAR. Термин VARCHAR означает VARiable length СНАRacter string — строка символов переменной длины, а команда воспринимает числовое значение, указывающее MySQL максимальную длину, разрешенную для строки, хранящейся в этом поле.
Этот тип данных очень удобен, поскольку позволяет MySQL планировать размер базы данных и эффективнее осуществлять просмотр и поиск данных. Но есть у него и недостаток: если присвоить строковое значение длиннее позволенного, оно будет усечено до максимальной длины, объявленной в определении таблицы.
Но у поля year (год) более предсказуемые значения, поэтому вместо VARCHARдля него используется более подходящий тип данных — CHAR(4). Параметр 4 позволяет выделить под него 4 байта данных, поддерживающих все года от -999 и до 9999. Можно было бы, конечно, сохранять год и в значении, состоящем из двух цифр, но если данные не утратят своей актуальности и в следующем столетии или показатель лет каким-то образом опять вернется к нулевому значению, то эту проблему нужно решать в первоочередном порядке, поскольку она очень похожа на «проблему 2000 года» из-за которой даты начиная с 1 января 2000 года на многих самых крупных в мире компьютерных системах могли быть отнесены к 1900 году.
»ч Тип данных YEAR не выбран для хранения года в таблице classics потому, что он поддерживает только 0000 год и диапазон лет с 1901-го по 2155-й. MySQL из соображений U*? 4 эффективности хранит значение года в 1 байте, а это значит, что храниться могут только ул' 256 значений, в то время как книги в таблице classics изданы задолго до сохраняемого диапазона лет.
Оба типа данных, и CHAR, и VARCHAR, принимают строки текста, ограничивая их длину размером поля. Разница между ними состоит в том, что каждая строка в поле CHAR имеет указанный размер. Если поместить в него строку меньшего размера, она будет дополнена пробелами. В поле VARCHAR дополнения текста не происходит; его размер может изменяться таким образом, чтобы в него помещался вставленный текст. Но при использовании поля VARCHAR требуется идти на небольшие издержки, чтобы отслеживать размер каждого значения. Поэтому CHAR больше подходит для тех случаев, когда данные во всех записях имеют одинаковый размер, a VARCHAR эффективнее применять, когда размеры могут сильно отличаться друг от друга и возрастать. Но за это приходится расплачиваться тем, что доступ к данным типа VARCHAR осуществляется несколько медленнее, чем к данным типа CHAR.
Тип данных CHAR
В табл. 8.6 перечислены все типы символьных данных CHAR. Все они предлагают указать параметр, устанавливающий максимальную (или точную) длину строки, которая может быть помещена в поле. Из таблицы следует, что у каждого типа есть присущее ему максимальное значение длины. Для данных типа VARCHAR длиной от О до 255 байт требуется еще 1 байт в хранилище, а для данных длиной более 256 байт требуется еще 2 байта.
Таблица 8.6. Типы данных CHAR, используемые в MySQL
Тип данных
Количество байтов
Примеры
CHAR(n)
В точности равное л (< 255)
CHAR(5) Hello использует 5 байт CHAR(57) New York использует 57 байт
VARCHAR(n)
Вплоть до п (< 65 535)
VARCHAR(IOO) Greetings использует 9 байт плюс еще 1 байт
VARCHAR(7) Morning использует 7 байт плюс еще 1 байт
Тип данных BINARY
Тип данных BINARY используется для хранения строк, заполненных байтами, не имеющими никакой связи с таблицей символов (табл. 8.7). Например, тип данных BINARY можно использовать для хранения изображения в формате GIF.
Таблица 8.7. Типы данных BINARY, используемые в MySQL
Тип данных
Количество байтов
Примеры
BINARY(n) или BYTE(n)
В точности равное п (< 255)
Похож на CHAR, но содержит двоичные данные
VARBINARY(n)
Вплоть до л (< 65 535)
Похож на VARCHAR, но содержит двоичные данные
Типы данных TEXT и VARCHAR
Типы данных TEXT и VARCHAR имеют незначительные отличия друг от друга.
О До выхода версии 5.0.3 MySQL удаляла из полей VARCHAR все начальные и замыкающие пробелы, и они могли быть длиной только до 256 байт.
О В полях типа TEXT не может быть исходных значений.
О В столбце TEXT MySQL индексирует только первые п символов (п задается при создании индекса).
Это означает, что VARCHARявляется более приемлемым и быстрее обрабатываемым типом данных, если нужно вести поиск по всему содержимому поля. Если поиск никогда не будет выполняться более чем в конкретном количестве начальных символов хранящегося в поле значения, то, наверное, нужно остановить свой выбор на типе данных TEXT (табл. 8.8).
Таблица 8.8. Типы данных TEXT, используемые в MySQL
Тип данных
Количество байтов
Особенности
TINYTEXT(n)
Вплоть до л (< 255)
Считается строкой с набором символов
ТЕХТ(л)
Вплоть до л (< 65 535)
Считается строкой с набором символов
MEDIUMTEXT(n)
Вплоть до п (< 16 777 215)
Считается строкой с набором символов
LONGTEXT(n)
Вплоть до п (< 4 294 967 295)
Считается строкой с набором символов
Тип данных BLOB
Термин ВШ В означает Binary Large Object — «большой двоичный объект», и поэтому, как и можно было предположить, тип данных BLOB больше всего подходит для хранения двоичных данных, превышающих по объему 65 536 байт. Другим основным отличием BLOB от типа данных BINARY является то, что для столбцов типа BLOB нельзя задавать исходные значения (табл. 8.9).
Таблица 8.9. Типы данных BLOB, используемые в MySQL
Тип данных
Количество байтов
Особенности
TINYBLOB(n)
Вплоть до л (< 255)
Считается не набором символов, а двоичными данными
BLOB(n)
Вплоть до п (< 65 535)
Считается не набором символов, а двоичными данными
MEDIUMBLOB(n)
Вплоть до п (< 16 777 215)
Считается не набором символов, а двоичными данными
LONGBLOB(n)
Вплоть до л (< 4 294 967 295)
Считается не набором символов, а двоичными данными
Числовые типы данных
В MySQL поддерживаются различные числовые типы данных, от одиночного байта до чисел с плавающей точкой с удвоенной точностью. Хотя для числового поля можно использовать до 8 байт, лучше все же выбрать поле с самым скромным типом данных, в котором способно уместиться наибольшее из ожидаемых вами значений. Тогда ваша база данных будет небольшой по объему и быстрой по доступу.
В табл. 8.10 перечислены числовые типы данных, поддерживаемые MySQL, и диапазоны значений, которые могут содержаться в их полях. Если вы не знакомы с терминологией, поясню, что число со знаком имеет диапазон возможных значений от отрицательного до нуля и от нуля до положительно.-: значения, а число без знака может быть в диапазоне от нуля до положительного значения. Оба они могут иметь одинаковую величину, нужно лишь представить чнзл : :: з наком сдвинутым наполовину влево, с одной половиной в отрицательном, а : другой — в положительном диапазоне. Следует заметить, что значения с ллазаззлзен точкой (любой точности) могут быть только числами со знаком.
Таблица 8.10. Числовые типы данных, исполё5>емае =
Тип данных
Количество байт
Минимальное значение (со знаком/без знака)
М тд п ш мое значение (со тстиом/без знака)
TINYINT
1
-128
0
12
SMALLINT
2
-32 768
0
32 757
MEDIUMINT
3
-8 388 608 0
5 3SS 507
—7 215
INT или INTEGER
4
-2 147 483 648
0
21* "48 3647
* 29* 967 295
BIGINT
8
-9 223 372 036 854 ^5 0
г Z23 372 036 854 775 807
15^ 467 440 737 09 551 615
FLOAT
4
-3,402823466Е+38
(без знака не бьвэе-'
3 ^€2823466Е+38
без знака не бывает)
DOUBLE или REAL
8
-1,797693134862315^—306 (без знака не бьеае^
1.7976931348623157Е+308 без знака не бывает)
Чтобы указать, какой именно тип данных ислзльзуется. со знаком или без знака, применяется спецификатор UNSIGNED. В слезтузолзем гзримере создается таблица по имени tablename, содержащая поле fie'c^ -e с пезом данных UNSIGNED INTEGER:
CREATE TABLE tablename (fieldname INT UNSI
При создании числового поля можно также пет>зз^тъ в качестве параметра необязательное число:
CREATE TABLE tablename (fieldname INT(4:
Но при этом следует помнить, что в отличие от ишов данных BINARY и CHAR этот параметр не показывает количество байтов. выделяемъо: под хранение. Может быть, это противоречит интуитивному восприятию, н з на самом деле это число обозначает отображаемую ширину данных в поле при его извлечении. Оно часто используется вместе со спецификатором ZERC-FI_:
CREATE TABLE tablename (fieldname INT'- I*^I__
Этот спецификатор указывает на то. что все числа шириной меньше четырех символов дополняются одним или несколькими нулями, хтя того чтобы ширина
отображаемого поля составляла четыре символа. Если поле уже занимает четыре и более символа, дополнение не производится.
Типы данных DATE и TIME
В табл. 8.11 показана еще одна важная категория типов данных, поддерживаемая MySQL, которая относится к дате и времени.
Таблица 8.11. Типы данных DATE и TIME, используемые в MySQL
Тип данных
Формат времени-даты
DATETIME
'0000-00-00 00:00:00'
DATE
'0000-00-00'
TIMESTAMP
'0000-00-00 00:00:00'
TIME
'00:00:00'
YEAR
0000 (только годы 0000 и 1901-2155)
Значения, имеющие типы данных DATETIME и TIMESTAMP, отображаются одинаково. Основное различие в том, что у TIMESTAMP слишком узкий диапазон (от 1970 и до 2037 года), а в DATETIME может храниться практически любая нужная дата, если только вы не интересуетесь античной историей или научной фантастикой.
Но TIMESTAMP также полезен, потому что, используя его, можно позволить MySQL установить для вас нужное значение. Если при добавлении строки не задавать значение для поля с этим типом данных, то в него автоматически будет вставлено текущее время. Можно также заставить MySQL обновлять столбец с типом данных TIMESTAMP при каждом изменении строки.
Тип данных AUTO.INCREMENT
Иногда нужно обеспечить уникальность каждой строки, имеющейся в базе данных. В вашей программе это можно сделать за счет тщательной проверки вводимых данных и обеспечения их различия хотя бы в одном из значений в любых двух строках. Но такой подход не защищен от ошибок и работает только в конкретных обстоятельствах. Например, в таблице один и тот же автор может появляться несколько раз. Точно так же, скорее всего, будет повторяться год издания и т. д. В таком случае гарантировать отсутствие продублированных строк будет довольно трудно.
В общем виде эта проблема решается за счет специально выделенного для этой цели дополнительного столбца. Вскоре мы рассмотрим использование ISBN (International Standard Book Number —международный стандартный книжный номер) издания для обеспечения уникальности строк в таблице classics, но сначала нужно представить вам тип данных с автоприращением — AUT0_ INCREMENT.
В соответствии с названием столбца, которому назначен этот тип данных, его содержимому будет устанавливаться значение на единицу большее, чем значение записи в этом же столбце в предыдущей вставленной строке. В примере 8.5 показано, как нужно добавлять новый столбец по имени id к таблице classics и придавать ему свойства автоприращения.
Пример 8.5. Добавление столбца id с автоприращением
ALTER TABLE classics ADD id INT UNSIGNED NOT NULL AUTOJNCREMENT KEY;
Здесь представлена команда ALTER, очень похожая на команду CREATE. Команда ALTER работает с уже существующей таблицей и может добавлять, изменять или удалять столбцы. В нашем примере добавляется столбец по имени id, имеющий следующие характеристики:
О I NT UNSIGNED — делает столбец способным принять целое число, достаточно большое для того, чтобы в таблице могло храниться более 4 млрд записей;
О NOT NULL — обеспечивает наличие значения в каждой записи столбца. Многие программисты используют его в поле NULL, чтобы показать отсутствие в нем какого-либо значения. Но тогда могут появляться дубликаты, противоречащие самому смыслу существования этого столбца. Поэтому появление в нем значения NULL запрещено;
О AUTO_INCREMENT — заставляет MySQL установить хтя этого столбца уникальное значение в каждой строке, как было описано ранее. Фактически мы не управляем значением, которое будет появляться в каждой строке этого столбца, но это и не нужно: все, о чем мы беспокоимся, —гарантия уникальности этого значения;
О KEY — столбец с автоприращением полезно использовать в качестве ключа, поскольку вы будете стремиться искать строки на основе значений этого столбца. Пояснения будут даны в разделе «Индексы» далее.
Теперь каждая запись будет иметь уникальное число в столбце id, для первой записи это будет начальное число 1, а счет других записей будет вестись по нарастающей. Как только будет вставлена новая строка, в ее столбец id будет автоматически записано следующее по порядку число.
Этот столбец можно не добавлять после создания таблицы, а сразу включить в нее, слегка изменив формат команды CREATE. В данном случае команда из примера 8.3 должна быть заменена командой из примера 8.6. Обратите особое внимание на ее последнюю строку.
Пример 8.6. Добавление столбца id с автоприрашением при создании таблицы
CREATE TABLE classics (
author VARCHAR(128).
title VARCHAR(128).
type VARCHAR(16),
year CHAR(4),
id INT UNSIGNED NOT NULL AUTOJNCREMENT KEY) ENGINE My I SAM:
Если хочется проверить, был ли добавлен столбец, нужно посмотреть имеющиеся в таблице столбцы и типы данных, воспользовавшись следующей командой:
DESCRIBE classics:
Теперь, когда мы закончили изучение этого типа данных, столбец i d нам больше не нужен, поэтому, если вы его создали, воспользовавшись командой из примера 8.5, его нужно удалить, воспользовавшись командой из примера 8.7.
Пример 8.7. Удаление столбца id
ALTER TABLE classics DROP id:
Добавление данных к таблице
Для добавления данных к таблице используется команда INSERT. Рассмотрим ее в действии, заполнив таблицу classics данными из таблицы 8.1, многократно используя одну и ту же форму команды INSERT (пример 8.8).
Пример 8.8. Заполнение таблицы classics
INSERT INTO classics(author. title , type, year)
VALUES('Mark Twain'.’The Adventures of Tom SaawyerFitction 1876');
INSERT INTO classics(author. title , type, year)
VALUES('Jane Austen'. 'Pride and Prejudice'. 'Ficttion'.'1811');
INSERT INTO classics(author. title , type, year)
VALUES('Charles Darwin' , 'The Origin of Species' . 'Non-Fiction'.'1856');
INSERT INTO classics(author, title , type, year)
VALUES('Charles Dickens' . 'The Old Curiosity Shop', 'Fiction'. '184Г);
INSERT INTO classics(author, title , type, year)
VALUES('William Shakespeare'.'Romeo and Ju1liett','P 1 lay','1594');
После каждой второй строки вы должны увидеть сообщение об успешной обработке запроса — Query ОК. Как только будут введены все строки, наберите следующую команду, которая отобразит содержимое таблицы:
SELECT * FROMclassics;
Результат должен быть похож на тот, что показан на рис. 8.4.
Рис. 8.4. Заполнение таблицы classics и просмотр ее содержимого
Сейчас не стоит обращать внимания на команду SELECT, ее очередь наступит в разделе «Запросы к базе данных MySQL с помощью РНР». Достаточно сказать, что в данном виде она отображает все только что введенные данные.
Теперь вернемся назад и посмотрим, как используется команда INSERT. Ее первая часть, INSERT INTO classics, сообщает MySQL, куда нужно вставлять следующие за ней данные. Затем в круглых скобках перечисляются четыре имени столбцов: author, tittle , type и year, которые отделяются друг от друга запятыми. Таким образом MySQL сообщается, что именно в эти четыре поля будут вставляться данные.
Во второй строке каждой команды INSERT содержится ключевое слово VALUES, за которым следуют четыре строковых значения, взятых в кавычки и отделенных друг от друга запятыми. Они обеспечивают MySQL теми четырьмя значениями, которые будут вставлены в четыре ранее указанных столбца. (Как и во всех остальных примерах, разбиение команды на строки было всего лишь моим собственным решением, придерживаться которого не обязательно.)
Каждый элемент данных будет вставлен по порядку в соответствующие столбцы. Если порядок перечисления столбцов и данных будет случайно перепутан, данные попадут не в те столбцы. А количество указанных столбцов должно соответствовать количеству элементов данных.
Переименование таблиц
Переименование таблицы, как и любые другие изменения ее структуры или метаданных, осуществляется посредством команды ALTER. Поэтому, чтобы, к примеру, изменить имя таблицы classics на рге1900. воспользуйтесь следующей командой:
ALTER TABLE classics RENAME prel900:
Если применить эту команду, то потом, чтобы без изменений работали все последующие примеры этой главы, вам придется вернуть таблице ее прежнее имя, для чего нужно будет ввести такую команду:
ALTER TABLE prel900 RENAME classics:
Изменение типа данных столбца
Для изменения типа данных столбца также используется команда ALTER, но в этом случае вместе с ней применяется ключевое слово MODIFY. Поэтому для изменения типа данных столбца year с CHAR(4) на SMALL I NT (для которого потребуется только 2 байта памяти, что способствует экономии дискового пространства), нужно ввести следующую команду:
ALTER TABLE classics MODIFY year SMALLINT;
После этого, если для MySQL есть смысл конвертировать тип данных, система автоматически изменит данные, сохраняя их значение. В данном случае она заменит каждое строковое значение сопоставимым с ним целым числом, пока строку можно будет распознать как отображение целого числа.
Добавление нового столбца
Предположим, что таблица создана и заполнена большим объемом данных и тут выяснилось, что нужен еще один столбец. Не стоит расстраиваться. Посмотрите, как можно добавить к таблице новый столбец pages, который будет использоваться для хранения количества страниц, имеющихся в книге:
ALTER TABLE classics ADD pages SMALLINT UNSIGNED:
Эта команда добавляет новый столбец по имени pages, в котором, используется тип данных UNSIGNED SMALL INT, подходящий для хранения значений вплоть до 65 535. Этого наверняка более чем достаточно для любой когда-либо изданной книги!
И если запросить у MvSQL описание обновленной таблицы, воспользовавшись показанной далее командой DESCRIBE, то можно будет увидеть внесенные в нее изменения (рис. 8.5):
DESCRIBE classics:
Рис. 8.5. Добавление нового столбца pages и просмотр таблицыРис. 8.5. Добавление нового столбца pages и просмотр таблицы
Рис. 8.5. Добавление нового столбца pages и просмотр таблицы
Рис. 8.5. Добавление нового столбца pages и просмотр таблицы
Переименование столбца
Посмотрев еще раз на рис. 8.5, можно заметить, что наличие в ней столбца type может привести к путанице, поскольку такое же имя используется MySQL для идентификации типа данных. Но это не проблема — давайте изменим имя этого столбца на category:
ALTER TABLE classics CHANGE type category VARCHAR(16);
Обратите внимание на добавление VARCHAR( 16) в конце этой команды. Это связано с тем, что ключевое слово CHANGE требует указания типа данных даже в том случае, если вы не собираетесь его изменять, и VARCHAR( 16) — это тот самый тип данных, который был указан при создании столбца type.
Удаление столбца
Поразмыслив, можно прийти к выводу, что столбец pages, в котором хранится количество страниц, не представляет для данной базы данных особой ценности, поэтому его можно удалить, используя ключевое слово DROP:
ALTER TABLE classics DROP pages:
Учтите, что ключевое слово DROP нужно применять с особой осторожностью, поскольку его действие носит необратимый характер и по недоразумению можно удалить целые таблицы (и даже базы данных)!
Удаление таблицы
Удалить таблицу очень просто. Но поскольку я не хочу заставлять вас заново вводить все данные в таблицу cl assi cs, мы ее удалять не станем. Давайте вместо этого просто создадим новую таблицу, проверим факт ее существования, а затем удалим ее, набрав команду, приведенную в примере 8.9. Результат выполнения всех четырех команд показан на рис. 8.6.
Пример 8.9. Создание, просмотр и удаление таблицы
CREATE TABLE disposable(trash INT);
DESCRIBE disposable:
DROP TABLE disposable:
SHOW tables:
Рис. 8.6. Создание, просмотр и удаление таблицы
Индексы
В данный момент у нас есть действующая таблица cl assies, в которой можно будет без труда, пользуясь средствами MySQL, отыскать нужную информацию. Но все так просто лишь до тех пор, пока она не разрастется до пары сотен строк. Тогда с каждой добавленной строкой доступ к базе данных будет становиться все медленнее и медленнее, поскольку MySQL при обработке запроса придется вести поиск в каждой строке. Это похоже на поиск нужной информации в каждой книге, имеющейся в библиотеке.
Разумеется, вам не придется вести поиск в библиотеках подобным образом, поскольку в них есть либо обычная картотека, либо, что более вероятно, собственная база данных. То же самое относится и к MySQL, поскольку ценой небольших затрат оперативной памяти и дискового пространства можно создать «картотеку» для таблицы, которая будет использоваться MySQL для выполнения мгновенного поиска.
Создание индекса
Возможности быстрого поиска можно добиться путем добавления индекса либо при создании таблицы, либо в любое время впоследствии. Но сделать это не так-то просто. Существуют, к примеру, различные типы индексов, такие как INDEX, PRIMARY KEY и FULLTEXT. Кроме того, нужно решить, каким столбцам нужен индекс, а для этого нужно спрогнозировать, по каким данным этих столбцов будет осуществляться поиск. Индексы можно усложнять, комбинируя в одном индексе данные из нескольких столбцов. И даже когда вы все это поймете, у вас будет возможность сократить размер индекса за счет ограничения объема данных каждого индексируемого столбца.
Если представить себе поисковые операции применительно к таблице classics, становится ясно, что поиск может осуществляться во всех столбцах. Но если бы не был удален столбец pages, созданный в пункте «Добавление нового столбца» выше, то он, наверное, не понадобился бы для индекса, поскольку большинство людей вряд ли стали бы искать книги по количеству страниц. Давайте все же продолжим и добавим индекс к каждому столбцу, воспользовавшись командами, приведенными в примере 8.10.
Пример 8.10. Добавление индексов к таблице classics
ALTER TABLE сclassics ADD INDEXCauthor(20));
ALTER TABLE сclassics ADD INDEX(title(20)):
ALTER TABLE сclassics ADD INDEX(category(4));
ALTER TABLE сclassics ADD INDEX(year);
DESCRIBE classics;
Первые две команды создают индексы для столбцов авторов и названий — author и title , ограничивая каждый индекс только первыми двадцатью символами. Например, когда MySQL индексирует название:
The Adventures of Tom Sawyer
то на самом деле в индексе будут сохранены только первые 20 символов:
The Adventures of То
Это делается для сокращения размера индекса и для оптимизации скорости доступа к базе данных. Я выбрал 20 символов, поскольку их должно быть достаточно для обеспечения уникальности большинства строк, встречающихся в данных столбцах. Если MySQL обнаружит два индекса с одинаковым содержимым, ей нужно будет понапрасну потратить время на обращение к самой таблице и на проверку проиндексированного столбца, для того чтобы определить, какая именно строка действительно соответствует условиям поиска.
Что касается столбца категории — category, то на данный момент, чтобы идентифицировать уникальность строки, достаточно только первого символа (F для Fiction, N для Non-Fiction и Р для Play), но я выбрал индекс из четырех символов, чтобы дать возможность в будущем вводить такие категории, уникальность которых можно будет определить только по четырем символам. (Если позже набор категорий усложнится еще больше, этот столбец можно будет переиндексировать.)
И наконец, я не стал задавать ограничения на индекс столбца года издания — year, поскольку в нем хранятся не строки, а целые числа.
Результат ввода этих команд (и команды DESCRIBE, позволяющей убедиться в том, что они работают) можно увидеть на рис. 8.7, который показывает наличие ключа MUL для каждого столбца. Это означает, что в этом столбце может многократно присутствовать одно и то же значение, что, собственно, нам и нужно, поскольку имена авторов могут встречаться многократно, одни и те же названия книг могут использоваться множеством авторов и т. д.
Рис. 8.7. Добавление индексов ктаблице classics
Использование команды CREATE INDEX
Индекс можно добавить не только командой ALTER TABLE, но и командой CREATE INDEX.
Эти две команды являются равнозначными, за исключением того, что CREATE INDEX не может использоваться для создания индекса типа первичного ключа — PRIMARY KEY(см. пункт «Первичные ключи» далее). Формат этой команды показан во второй строке примера 8.11.
Пример 8.11. Эти две команды являются эквивалентными
ALTER TABLE classics ADD INDEX(author(20)):
CREATE INDEX author ON classics (author(20));
Добавление индексов при создании таблиц
Чтобы добавить индекс, не нужно выжидать какое-то время после создания таблицы. Это может отнять много времени, поскольку добавление индекса к большой таблице — длительный процесс. Поэтому давайте рассмотрим команду, создающую таблицу classics с уже имеющимися индексами.
Пример 8.12 является переработкой примера 8.3, в котором одновременно с таблицами создаются и индексы. Учтите, что для включения всех изменений, выполненных в данной главе, в этой версии используется новое имя столбца category вместо прежнего имени type, а для столбца year указан тип данных SMALLINT, а не
CHAR(4). При желании попробовать эту команду в работе без предварительного удаления текущей таблицы cl assi cs замените слово cl assi cs в первой строке каким- нибудь другим словом, например classicsl, а после завершения работы удалите таблицу classicsl I .
Пример 8.12. Создание таблицы classics с индексами
CREATE TABLE classics (
author VARCHARQ28).
title VARCHAR(128).
category VARCHAR(16).
year SMALL INT,
INDEX(author(20)).
INDEX(ti tle(20)).
INDEX(category(4)),
INDEX(year)) ENGINE MylSAM;
Первичные ключи
В данный момент у нас создана таблица classics и за счет добавления индексов обеспечен быстрый поиск, но кое-что все же упущено. Можно вести поиск по всем имеющимся в таблице изданиям, но нет единого уникального ключа для каждого издания, обеспечивающего мгновенный доступ к строке. Важность наличия ключа с уникальным значением для каждой строки (известного как первичный ключ) проявится, когда мы станем комбинировать данные из разных таблиц (см. подраздел «Первичные ключи: ключи к реляционным базам данных» раздела «Проектирование базы данных» главы 9).
В представленном ранее пункте «Тип данных AUTO_INCREMENT» подраздела «Типы данных» предыдущего раздела, в котором рассматривался создаваемый столбец id с автоприращением, было сказано, что он может быть использован в качестве первичного ключа для этой таблицы. Но я захотел возложить эту задачу на более подходящий столбец: признанный во всем мире номер ISBN.
Поэтому продолжим работу с таблицей и создадим новый столбец для этого ключа. Теперь, помня о том, что номер ISBN состоит из 13 символов, можно решить, что с этой задачей справится следующая команда:
ALTER TABLE classics ADD isbn CHARC13) PRIMARY KEY:
Но это не так. Если запустить эту команду на выполнение, будет получено сообщение об ошибке, связанной с дубликатом записи для ключа 1: Duplicate entry. Причина в том, что таблица уже заполнена даннйми, а эта команда пытается добавить столбец со значением NULL к каждой строке, что запрещено, поскольку все столбцы, использующие первичный ключ, должны иметь уникальное значение. Но если бы таблица была пуста, то эта команда была бы выполнена без проблем, как и при добавлении первичного ключа сразу же после создания таблицы.
В сложившейся ситуации нужно немного схитрить: создать новый столбец без индекса, заполнить его данными, а затем воспользоваться командой из примера 8.13. К счастью, в этом наборе данных каждый год имеет уникальное значение, поэтому для идентификации каждой обновляемой строки можно воспользоваться столбцом
year. Учтите, что в этом примере применяются ключевые слова UPDATE и WHERE, которые более подробно будут рассмотрены в подразделе «Создание запросов к базе данных MySQL» далее.
Пример 8.13. Заполнение столбца isbn данными и использование первичного ключа
ALTER TABLE classics ADD isbn CHAR(13);
UPDATE classics; SESETisbn=’9781598184891' кWHERE year='1876':
UPDATE classics; SESETisbn=’9780582506206* кWHERE year='1811’ ;
UPDATE classics; SESETisbn='9780517123201' кWHERE year='1856’ ;
UPDATE classics; SESETisbn=’9780099533474' кWHERE year=,1841, :
UPDATE classics. SESETisbn=19780192814968’ кWHERE year=’1594, ;
ALTER TABLE classics ADD PRIMARY KEY(isbn);
DESCRIBE classics;
После ввода этих команд будет получен результат, похожий на копию экрана, показанную на рис. 8.8. Обратите внимание на то, что в синтаксисе команды ALTER TABLE ключевое слово INDEX заменено ключевыми словами PRIMARY KEY (сравните примеры 8.10 и 8.13).
Рис. 8.8. Добавление первичного ключа к таблице classics
Чтобы создать первичный ключ при создiан^ таблицы cl assi cs, можно воспользоваться командой, показанной в примере 8.14. И в этом случае, если вы хотите испробовать эту команду в работе, нужно заменить имя classics в строке 1 каким- нибудь другим, а затем удалить проверочную таблицу.
Пример 8.14. Создание таблицы classics вместе с индексами
CREATE TABLE classics (
author VARCHAR(128),
tittle VARCHAR(128).
category VARCHAR(16).
year SMALLINT, isbn CHAR(13).
INDEX(author(20)).
INDEX(ti tle (20)),
INDEX(category(4)).
INDEX(year).
PRIMARY KEY (isbn)) ENGINE MyISAM;
Создание индекса FULLTEXT
В отличие от обычного индекса, имеющийся в MySQL индекс FULLTEXT позволяет осуществлять сверхбыстрый поиск целых столбцов текста Он сохраняет каждое слово каждой строки данных в специальном индексе, в котором можно вести поиск, используя «естественный язык» наподобие того, что используется в поисковом механизме.
•йг **
-Во-о-б-щ-е--т-о-у-тв-е-р-ж-де-н-и-е- о- т-ом- ,- ч-т-о-с-ис-т-ем- а--M-yS-Q-L- х-р-а-ни-т- в-с-е -сл-о-в-а-в- и-н-де-к-с-е -F-UL-L-TE-X-T-, -не « вполне соответствует действительности, поскольку в ней имеется встроенный список более из 500 слов, которые она предпочитает игнорировать в силу их широкой распростра
ненности и практической бесполезности при любом поиске. Это список, называемый стоповыми словами — stopwords, включает слова the, as, is, of и т. д. Список помогает MySQL работать при FULLTEXT-поиске намного быстрее и не раздувать размеры базы данных. Полный список стоповых слов приведен в приложении В.
Рассмотрим некоторые особенности индексов FULLTEXT, о которых нужно знать.
О Индексы FULLTEXT могут использоваться только с таблицами типа MyISAM, использующими исходное ядро (механизм хранения) MySQL (MySQL поддерживает как минимум десять различных ядер). Если нужно привести таблицу к типу MyISAM, можно применить команду MySQL:
ALTER TABLE tablename ENGINE = MyISAM;
О Индексы FULLTEXT могут создаваться только для столбцов с типами данных CHAR, VARCHARи TEXT.
О Определение индекса FULLTEXT может быть дано в инструкции CREATE TABLE при создании таблицы или добавлено позже с использованием инструкции ALTER TABLE (или CREATE INDEX).
О Намного быстрее будет загрузить большие наборы данных в таблицу, не имеющую индекса FULLTEXT, а затем создать индекс, чем загружать их в таблицу, у которой уже имеется индекс FULLTEXT.
Чтобы создать индекс FULLTEXT, примените его к одной или нескольким записям, как в примере 8.15, в котором индекс FULLTEXT добавляется к двум столбцам — author и title , принадлежащим таблице cl assi cs (этот индекс является дополнением к тем, что уже были созданы, и не влияет на их работу).
Пример 8.15. Добавление индекса FULLTEXT к таблице classics
ALTER TABLE classics ADD FULLTEXT(author.title);
Теперь в этой паре столбцов можно вести поиск с использованием индекса FULLTEXT. Эта возможность могла бы проявиться в полную силу, если бы вы могли
теперь ввести весь текст этих книг в базу данных (учитывая, что они не защищены авторскими правами), тогда они были бы полностью доступны для поиска. Поисковые операции с использованием индекса FULLTEXT рассмотрены далее в пункте «MATCH...AGAINST» подраздела «Создание запросов к базе данных MySQL».
Если система MySQL станет при доступе к вашей базе данных работать медленнее, чем вы « от нее ожидали, то проблема чаще всего заключается в ваших индексах. Либо у вас нет ?• индекса там, где он нужен, либо индексы составлены неоптимальным образом. Зачастую
данная проблема решается за счет тонкой настройки индексов таблиц. Производительность не входит в тематику данной книги, но в главе 9 будет дан ряд подсказок, чтобы вы знали, что именно нужно искать.
Создание запросов к базе данных MySQL
Итак, мы создали базу данных MySQL и таблицы, заполнили их данными и добавили к ним индексы, чтобы ускорить поиск. Теперь настало время посмотреть, как именно ведется этот поиск и какие для этого имеются команды и спецификаторы.
SELECT
На рис. 8.4 уже было показано, что команда SELECT используется для извлечения данных из таблицы. В том разделе я воспользовался ее наипростейшей формой для выбора всех данных и их отображения, что вам вряд ли когда-нибудь пригодится, разве что для просмотра самых маленьких таблиц, поскольку все данные будут прокручиваться на экране и скрываться в нечитаемой области. А теперь рассмотрим команду SELECT более подробно.
Ее основной синтаксис имеет следующий вид:
SELECT что-нибудь FROM имя_таблицы;
Этим что-нибудь, как вы уже видели, может быть символ звездочки (*), означающий «каждый столбец», вместо него можно указать какие-нибудь конкретные столбцы. В примере 8.16 показано, как выбрать только автора и название (author и titl е) и только название и ISBN. Результат выполнения этих команд показан на рис. 8.9.
Пример 8.16. Две разных инструкции SELECT
SELECT author,title FROM classics;
SELECT title.isbn FROM classics:
SELECT COUNT
Другим вариантом параметра что-нибудь является функция COUNT, которая может быть использована множеством способов. В примере 8.17 она отображает количество строк в таблице за счет передачи ей в качестве параметра символа звездочки (*), означающего «все строки». В соответствии с вашими ожиданиями будет возвращено число 5, поскольку в таблицу внесены сведения о пяти книгах.
Пример 8.17. Подсчет количества строк
SELECT COUNT(*) FROM classics:
Рис. 8.9. Вывод, полученный в результате выполнения двух разных инструкций SELECT
SELECT DISTINCT
Этот спецификатор (и его синоним DISTINCTROW) позволяет исключать множество записей, имеющих одинаковые данные. Предположим, к примеру, что вам нужно получить список всех авторов, фигурирующих в таблице. Если просто выбрать столбец author из таблицы, содержащей несколько книг одного и того же автора, то будет отображен длинный список с одинаковыми именами авторов, повторяющимися снова и снова. Но за счет добавления ключевого слова DISTINCT можно показать каждого автора всего лишь один раз. Давайте проверим этот спецификатор, добавив еще одну строку, в которой повторяется один из уже имеющихся авторов (пример 8.18).
Пример 8.18. Дублирование данных
INSERT INTO classics-:author, title , category, year, isbn)
VALUESC'Charles Dickens’. ’Little Dorrit'.'Fiction'. '1 857'. '9780141439969');
Теперь, когда Чарльз Диккенс появляется в таблице дважды, мы можем сравнить результаты использования команды SELECT со спецификатором DISTINCT и без него. В примере 8.19 и на рис. 8.10 показано, что при вводе простой команды SELECT Диккенс будет показан дважды, а команда со спецификатором DISTINCT выводит его только один раз.
Пример 8.19. Команда SELECT со спецификатором DISTINCT и без него
SELECT author FROMclassics:
SELECT DISTINCT author FROMclassics:
DELETE
Когда нужно удалить строку из таблицы, используется команда DELETE. Ее синтаксис похож на синтаксис команды SELECT, он позволяет сузить диапазон удаляемой информации до конкретной строки или строк путем использования таких спецификаторов, как WHERE и LIMIT.
Рис. 8.10. Выборка данных с использованием спецификатора DISTINCT и без него
Теперь, если вы вводили команду, показанную в примере 8.18, и изучали работу спецификатора DISTINCT, нужно удалить Little Dorrit путем ввода команды, показанной в примере 8.20.
Пример 8.20. Удаление новой записи
DELETE FROM classics WHERE title=’Littl I e Dorrit';
В этом примере команда DELETE выдается для всех строк, в столбце ti tl е которых содержится строковое значение Li ttl е Dorri t.
Ключевое слово WHERE обладает большими возможностями, и очень важно, чтобы оно было набрано правильно. Ошибка может навести команду на не те строки (или вообще ни к чему не привести в том случае, если условию WHERE не будет найдено ни одного соответствия). Поэтому теперь нужно уделить немного внимания этому условию, играющему очень важную роль в языке SQL.
WHERE
Ключевоое слово WHERE позволяет сузить диапазон действия запроса, возвращая только те данные, в отношении которых конкретное выражение возвращает истинное значение. За счет использования оператора равенства = код в примере 8.20 возвращает только те строки, в которых значение столбца tittle в точности соответствует строке Littl1e Dorrit. В примере 8.21 показаны еще два фрагмента, в которых WHERE используется с оператором =.
Пример 8.21. Использование ключевого слова WHERE
SELECT author.title FROM classics WHERE author="Mark Twain";
SELECT author,title FROM classics WHERE isbn="9781598184891 ";
Применительно к нашей таблице эти две команды отобразят один и тот же результат. Но мы можем без особого труда добавить еще несколько книг Марка Твена, и тогда команда в первой строке отобразит все названия книг, принадлежа-
щих его перу, а команда во второй строке — прежний результат (потому что, как мы знаем, ISBN имеет уникальное значение) — The Adventures of Tom Sawyer. Ины- ) ми словами, поисковые операции, использующие уникальный ключ, более предсказуемы, и новые доказательства этого вы увидите позже, при рассмотрении ролй уникальных и первичных ключей.
При проведении поисковых операций можно также осуществлять проверку на соответствие шаблону, для чего используется спецификатор LIKE, позволяющий вести поиск в разных частях строк. Этот спецификатор должен использоваться с символом %до или после некоторого текста. Если его поместить до текста, это будет означать «что-нибудь до», а если после текста — «что-нибудь после». В примере 8.22 показаны три разных запроса, один из которых предназначен для начала строки, другой — для конца, а третий —для любого места в строке. Результат выполнения этих команд приведен на рис. 8.11.
Пример 8.22. Использование спецификатора LIKE
SELECT author.title FROM (classics WHEREauthorо LIKE< "Charles^";
SELECT author.title FROM (classicsWHERE title LIKEЕ Ч"^Species";
SELECT author.title FROM (classics WHERE title LIKEЕ Чапе"XandX”
Щ C:\Windows\system32\cmd.exeЩ C:\Windows\system32\cmd.exeРис. 8.11. Использование ключевого слова WHERE со спецификатором LIKEРис. 8.11. Использование ключевого слова WHERE со спецификатором LIKE
Щ C:\Windows\system32\cmd.exe
Щ C:\Windows\system32\cmd.exe
Рис. 8.11. Использование ключевого слова WHERE со спецификатором LIKE
Рис. 8.11. Использование ключевого слова WHERE со спецификатором LIKE
Первая команда выведет книги, принадлежащие перу как Чарльза Дарвина, так и Чарльза Диккенса, потому что спецификатор LIKE был настроен на возвращение всего соответствующего строке Charles, за которой следует любой другой текст. Затем будет возвращена информация о книге The Origin of Species, потому что есть только одна строка, столбец которой заканчивается строковым значением Species. И на последний запрос будет возвращена информация о книгах Pri de and Prejudi ce и Romeo and Jul iet, потому что обе записи соответствуют запросу строкового значения and в любом месте столбца.
Символ %будет также соответствовать пустому месту в той позиции, которую он занимает. Иными словами, он может соответствовать пустой строке.
LIMIT
Спецификатор LIMIT позволяет выбрать количество выводимых в запросе строк и место, с которого таблица начнет их возвращать. Когда передается один параметр, он указывает MySQL начать действие спецификатора с верхней части результатов и вернуть только то количество строк, которое задано этим параметром. Если передать спецификатору два параметра, то первый укажет смещение относительно начала результатов, которое MySQL должна учесть при их отображении, а второй укажет, сколько строк нужно вывести. Можно представить, что первый параметр сообщает: «Нужно пропустить это количество результатов, ведя счет сверху».
В пример 8.23 включены три команды. Первая возвращает первые три строки из таблицы. Вторая возвращает две строки, начиная с позиции 1 (пропуская первую строку). А последняя возвращает одну строку, начинающуюся с позиции 3 (пропуская первые три строки). Результаты выполнения всех трех команд показаны на рис. 8.12.
Пример 8.23. Ограничение к количества в возвращаемых результато
SELECT author.title! FFFROM classics LIMIT LIMIT 3:
SELECT author.title! FFFROM classicsLIMIT LIMIT 1.2;
SELECT author.title: FFFROM classicsLIMIT LIMIT 3.1:
Рис. 8.12. Ограничение диапазона выводимых строк с помощью спецификатора LIMITРис. 8.12. Ограничение диапазона выводимых строк с помощью спецификатора LIMIT
Рис. 8.12. Ограничение диапазона выводимых строк с помощью спецификатора LIMIT
Рис. 8.12. Ограничение диапазона выводимых строк с помощью спецификатора LIMIT
Ключевое слово UMIT требует особого внимания, поскольку смещение начинается с нулевой позиции, а количество возвращаемых строк — с единицы. Поэтому спецификатор LIMIT 1,3 означает возвращение трех строк, начиная со второ й строки.
MATCH...AGAINST
Конструкция MATCH.. .AGAINST может быть применена к столбцу, для которого был создан индекс FULLTEXT (см. выше пункт «Создание индекса FULLTEXT»). Используя эту конструкцию, можно вести поиск, применяя в качестве критерия элементы
обычного языка, как при. работе с поисковыми механизмами Интернета. В отличие от использования конструкций *HERE. . .= или WHERE.. .LIKE, конструкция MATCH... AGAINST позволяет вводить в поисковый запрос несколько слов и проверять на их наличие все слова в столбцах, имеющих индекс FULLTEXT. Индексы FULLTEXT нечувствительны к регистру букв, поэтому неважно, какой именно регистр используется в ваших запросах.
Предположим, что вы добавили индекс FULLTEXT к столбцам author и tittle и ввели три запроса, показанные в примере 8.24. Первый из них требует вернуть любой из этих столбцов, в котором содержится слово and. Поскольку and является стоповым словом. MySQL его проигнорирует, и запрос всегда будет возвращать пустой набор независимо от того, что хранится в столбцах. Второй запрос требует вернуть любые строки, содержащие в любом месте и в любом порядке оба слова: ol d и shop. Третий запрос применяет тот же вид поиска для слов tom и sawyer. Результаты выполнения этих запросов показаны на рис. 8.13.
Пример 8.24. / ~=зевание конструкции MATCH... AGAINST с индексами FULLTEXT
SELECT authc'W '/e rR0M classics
WHERE MATCH .title)) AGAINST('and'):
SELECT autnc'W -'e "ROM classics
WHERE MATCH, s.:— . v tie) AGAINSTCold shop'):
SELECT autr: -.v"'e rR0M classics
WHERE MATCr =.:r:r.:'tle)) AGAINSTCtom sawyer'):
Рис. 8.13. /сг-сльзование конструкции MATCH...AGAINST в индексе FULLTEXT
MATCH...AGAINST...IN BOOLEAN MODE
При желании придать своим .запросам, использующим конструкцию MATCH... AGAINST, более широкие возможности, нужно воспользоваться булевым режимом. Это изменение выражается в том. что стандартный запрос по индексу FULLTEXT ведет поиск любой комбинации искомых слов, не требуя присутствия всех этих слов в тексте. Присутствие отдельноге слова в столбце приводит к тому, что поисковая операция возвращает строку.
Булев режим позволяет также ставить впереди искомых слов знак + или чтобы показать, что они должны быть включены или исключены. Если обычный булев режим требует «искать присутствие любого из этих слов», то знак «плюс» означает, что «это слово обязательно должно присутствовать, иначе строку возвращать не нужно». Знак «минус» означает, что «этого слова быть не должно, если оно присутствует, то строку возвращать не нужно».
В примере 8.25 показаны два запроса, использующие булев режим. Первый запрос требует вернуть все строки, в которых содержится слово charl es и нет слова species. Во втором запросе используются двойные кавычки, чтобы потребовать вернуть все строки, включающие в себя фразу origin of. На рис. 8.14 показаны результаты выполнения этих запросов.
Пример 8.25. Использование MATCH.. .AGAINST.. .IN BOOLEAN MODE
SELECT author.title FROM classics
WHERE MATCH(author.title)
AGAINST(’+charles -species' IN BOOLEAN MODE);
SELECT author,title FROM classics
WHERE MATCH(author.title)
AGAINST(' "origin of"’ IN BOOLEAN MODE):
Рис. 8.14. Использование конструкции MATCH...AGAINST... IN BOOLEAN MODEРис. 8.14. Использование конструкции MATCH...AGAINST... IN BOOLEAN MODE
Рис. 8.14. Использование конструкции MATCH...AGAINST... IN BOOLEAN MODE
Рис. 8.14. Использование конструкции MATCH...AGAINST... IN BOOLEAN MODE
Как, наверное, и ожидалось, первый запрос вернет только запись о книге The Old Curiosity Shop Чарльза Диккенса. Запись о книге Чарльза Дарвина игнорируется, поскольку из результата должна быть исключена любая строка, содержащая слово species.
Во втором запросе есть кое-что интересное: частью искомой строки является стоповое слово of, но оно все же используется в поиске, поскольку двойные кавычки отменяют учет стоповых слов.
UPDATE...SET
Эта н н тт т:ция позволяет обновлять содержимое поля. Если нужно изменить содер:~:пм >е :иного или нескольких полей, сначала следует сузить область действия зызтоза до того поля или полей, которые будут подвергаться изменениям, пракпгсе-сзп: тем же способом, который применялся в команде SELECT. В примере 8.26 измазаны два разных способа использования UPDATE.. .SET. Копия экрана с результатами работы этих команд приведена на рис. 8.15.
Пример 8.2с. /:~:льзование UPDAIE...SET
UPDATE5 нs' :т SET author='Mark1аTwain;а(SamuelntLanghorneirClemens)'1S) '
WHEREa-. := us"< 7w5rk Twain';
UPDATE :£ SET category='Classic Fiction'
WHERE C5ie;:\«= r "lotion';
Pmc. 8.15 . Обновление столбцов в таблице classicsPmc. 8.15 . Обновление столбцов в таблице classics
Pmc. 8.15 . Обновление столбцов в таблице classics
Pmc. 8.15 . Обновление столбцов в таблице classics
пет н- v галто. г действие которого затрагивает только одну строку, к литера- !му л.’гьлзннму '■'з'х Twain добавляется настоящее имя писателя —Samuel : е *: з-азлюченное в скобки. А вот второй запрос воздействует на три
столбца, псс:*::.Tri-г.- :н заменяет все появления слова Fiction в столбце category термином ‘ 31 г I 7 1C г
При выполнении >зновления можно также воспользоваться такими уже приваленными здесь зптлитикаторами, как LIMIT, а также рассматриваемыми далее ключевыми словами 1-11= Е• и GROUP BY.
ORDER BY
Спецификатор ORZl2 z • позволяет отсортировать возвращаемые результаты по одному 51ли нескольким столбцам в возрастающем или в убывающем порядке. В примере 8.27 показаны два таких запроса, результаты работы которых можно увидеть на рис. 8.16.
Пример 8.27. Использование ORDER BY
SELECT author.title FROM classics ORDER BY author;
SELECT author.title FROM classics ORDER BY title DESC;
Рис. 8.16. Сортировка результатов запроса
Первый запрос возвращает издания, отсортированные по авторам в возрастающем алфавитном порядке (этот режим используется по умолчанию), а второй возвращает их отсортированными по названию в убывающем порядке.
Если нужно отсортировать все столбцы по авторам, а затем в убывающем порядке по году издания (чтобы сначала стояли самые последние), нужно ввести следующий запрос:
SELECT author.title.y , ear FROM classics ORDER BY author.year DESC:
Здесь показано, что каждый спецификатор сортировки по возрастанию и по убыванию применяется к отдельному столбцу Ключевое слово DESC применяется только к столбцу, который указан перед ним, — year. Поскольку для столбца author разрешено использовать порядок сортировки, применяемый по умолчанию, этот столбец сортируется в возрастающем порядке. Можно также указать порядок сортировки этого столбца по возрастанию и в явном виде, в итоге будут получены аналогичные результаты:
SELECT author.title.y > ear FROM classics ORDER BY author ASC.year DESC;
GROUP BY
Точно так же, как и при использовании ORDER BY, можно сгруппировать результаты, возвращаемые запросом, с помощью спецификатора GROUP BY, который больше всего подходит для извлечения информации о группе данных. Например, если нужно узнать, сколько изданий каждой категории присутствует в таблице class'cs, можно ввести запрос
SELECT category.COUNT(author) FROM classics GROUP BY category:
который вернет следующую информацию:
category
| C0UNT(author)
Classic Fiction
1з
Non-Fiction
11
Play
11
3 rows in set (0.00 sec)
Объединение таблиц
Управление несколькими таблицами, содержащими различные виды информации в одной базе данных, считается вполне обычным делом. Рассмотрим, к примеру, таблицу клиентов — customers, для которой нужно обеспечить возможность использования перекрестных ссылок с приобретенными ими книгами из таблицы cl ass i cs. Чтобы создать эту новую таблицу и поместить в нее информацию о трех клиентах и их покупках, введите команды из примера 8.28. Результаты показаны на рис. 8.17.
Пример 8.28. Создание и заполнение таблицы customers
CREATE TABLE customers (
name VARCHAR(128),
isbn VARCHAR(128).
PRIMARY KEY (isbn)) ENGINE MyISAM:
INSERT INTO customers(name.isbn)
VALUESCJoe Bloggs’ . '9780099533474’):
INSERT INTO customers(name.isbn)
VALUESCMary Smith', '9780582506206'):
INSERT INTO customers (name, isbn.)
VALUESCJack Wilson'. '9780517123201'):
SELECT * FROM customers;
Eg C:\Windows\system32\cmd.exeEg C:\Windows\system32\cmd.exeтчэ«1 > GREATК TABLE custonersтчэ«1 > GREATК TABLE custonersnane UftftCHft IK 12У > b hn и»ЧНСНйК< i 28 > PHI МАКУ KEYnane UftftCHft IK 12У > b hn и»ЧНСНйК< i 28 > PHI МАКУ KEY0 гои;;0 гои;;e t сd < Йe t сd < Й02 с с с02 с с сIHSEKT 1HTO-> UALUES < J •Joe В1 оs<4Query OK, 1 r'c.v a.frecordIHSEKT 1HTO-> UALUES < J •Joe В1 оs<4Query OK, 1 r'c.v a.frecordt’^ql > INSERT i НТО- > UftLUESi < ' Mar Query OK„ 1 pow af ft’^ql > INSERT i НТО- > UftLUESi < ' Mar Query OK„ 1 pow af fi I ti I tSo ti n te«So ti n te«ИЦИЦr«Vsql> INSERT INTO-> UALUES<'JaQuery OK, 1 row af£r«Vsql> INSERT INTO-> UALUES<'JaQuery OK, 1 row af£<t0<t0ELECTELECTпюмпюмn<?.nen<?.nei Joe ;i Joe ;80Ы7180Ы718S 018S 01u Jifu Jif.Ъм> 7г2:«.Ъм> 7г2:«2.S У-1ЛУ *Л ; > 1 ?'2.S У-1ЛУ *Л ; > 1 ?'
Eg C:\Windows\system32\cmd.exe
Eg C:\Windows\system32\cmd.exe
тчэ«1 > GREATК TABLE custoners
тчэ«1 > GREATК TABLE custoners
nane UftftCHft IK 12У > b hn и»ЧНСНйК< i 28 > PHI МАКУ KEY
nane UftftCHft IK 12У > b hn и»ЧНСНйК< i 28 > PHI МАКУ KEY
0 гои;;
0 гои;;
e t сd < Й
e t сd < Й
02 с с с
02 с с с
IHSEKT 1HTO
-> UALUES < J •Joe В1 оs<4
Query OK, 1 r'c.v a.frecord
IHSEKT 1HTO
-> UALUES < J •Joe В1 оs<4
Query OK, 1 r'c.v a.frecord
t’^ql > INSERT i НТО
- > UftLUESi < ' Mar Query OK„ 1 pow af f
t’^ql > INSERT i НТО
- > UftLUESi < ' Mar Query OK„ 1 pow af f
i I t
i I t
So ti n te«
So ti n te«
ИЦ
ИЦ
r«Vsql> INSERT INTO
-> UALUES<'Ja
Query OK, 1 row af£
r«Vsql> INSERT INTO
-> UALUES<'Ja
Query OK, 1 row af£
<t0
<t0
ELECT
ELECT
пюм
пюм
n<?.ne
n<?.ne
i Joe ;
i Joe ;
80Ы71
80Ы71
8S 01
8S 01
u Jif
u Jif
.Ъм> 7г2:«
.Ъм> 7г2:«
2.S У-1Л
У *Л ; > 1 ?'
2.S У-1Л
У *Л ; > 1 ?'
Существует также быстрый способ для вставки сразу нескольких строк данных, как в примере 8.28, позволяющий1заменить трилотдельных<запроса INSERTГINTO)одним, в котором перечисляются вставляемые данные, отделенные друг от друга запятыми:
INSERTт INTOоcustomers(name.isbn)I) VALUESS
CJoe BloggsV9780099533474').
(’Mary Smith','9780582506206').
('Jack Wilson'.'9780517123201'):
Разумеется, в настоящей таблице, содержащей сведения о покупателях, будут присутствовать также адреса, номера телефонов, адреса электронной почты и т. д., но на данном этапе изучения они для нас не представляют интереса.
При создании новой таблицы следует обратить внимание на то, что у нее есть кое-что общее с таблицей classics: столбец под названием isbn. Поскольку его предназначение в обеих таблицах совпадает (ISBN всегда является ссылкой на одну и туже книгу), этот столбец можно использовать для связывания двух таблиц вместе в едином запросе, как в примере 8.29.
Пример 8.29. Объединение двух таблиц в одном запросе SELECT
SELECT name.author,title from customers.classics
WHERE customers.isbn=classi cs.i sbn:
В результате будет выведена следующая информация:
I name|author|title ;|
++.++
| Joe Bloggs|CharlesDickens|The Old Curiosity Shop|
| MarySmithjJane AustenjPride and Prejudice|
| JackWilson|CharlesDarwinjThe Origin of Species
+--- + — ---.+-- _
3 rows in set (0.00 sec)
Видите, как этот запрос искусно связал вместе обе таблицы, чтобы продемонстрировать книги из таблицы classics, приобретенные покупателями из таблицы customers?
NATURAL JOIN
Используя NATURAL JOIN, можно сократить количество вводимого текста и сделать запросы немного более понятными. В этом виде объединения участвуют две таблицы, в которых автоматически объединяются столбцы с одинаковыми именами. Для получения тех же результатов, что и в примере 8.29, можно ввести следующий запрос:
SELECT name.author.title FROMcustomers NATURAL JOIN classics:
JOIN...ON
Если нужно указать столбец, по которому следует объединить две таблицы, используется конструкция JOIN.. .ON, благодаря которой можно получить те же результаты, что и в примере 8.29:
SELECT name.author.title FROM customers
JOIN classics ON customers.isbn=classics.isbn;
Использование ключевого слова AS
Можно сократить количество вводимого текста и улучшить читаемость запроса за счет создания псевдонимов с помощью ключевого слова AS. После имени таблицы нужно поставить AS, а затем используемый псевдоним. Следующий код идентичен по своей работе коду, приведенному в примере 8.29:
SELECT name.author.title from
customers AS cust. classics AS class WHERE cust.isbn=class.isbn;
Результат выполнения этой операции имеет следующий вид:
| name| author
| title
++
| Joe Bloggs| Charles
Dickens | The Old Curiosity Shop
j Mary Smith | Jane Austenj Pride and Prejudice
j Jack Wilson j
Charles Darwinj The Origin of Spec
++
*+
3 rows in set(0.00 sec)
Псевдонимы особенно полезны в длинных запросах, содержащих множественные ссылки на одни и те же имена таблиц.
Использование логических операторов
Для дальнейшего сужения пространства выбора в запросах MySQL, использующих ключевое слово WHERE, можно также использовать логические операторы AND, ORи NOT. В примере 8.30 показаны варианты применения каждого из них, но их можно использовать в любых сочетаниях.
Пример 8.30. Использование логических операторов
SELECT author.title FROMclassics WHERE
author LIKE "Charlesr AND author LIKE "fcDarwin";
SELECT author.title FROM classics WHERE
author LIKE "ЯМагк Twainfc" OR author LIKE "^Samuel Langhorne Clemens^":
SELECT author,title FROM classics WHERE
author LIKE "Charles^" AND author NOT LIKE "^Darwin":
Первый запрос выбран потому, что Чарльз Дарвин может фигурировать в некоторых строках под своим полным именем — Чарльз Роберт Дарвин. А запрос возвращает сведения о книгах, для которых значение столбца author начинается с Charles и заканчивается Darwin. Второй запрос ищет книги, принадлежащие перу Марка Твена, используя для этого либо литературный псевдоним — Mark Twain, либо настоящее имя писателя — Samuel Langhorne Cl emens. Третий запрос возвращает книги с авторами, чье имя Charles, а фамилия не Darwin.
Функции MySQL
Стремление применять функции MySQL при таком обилии достаточно мощных функций РНР может вызвать недоумение. А ответ предельно прост: функции MySQL работают с данными непосредственно в самой базе. А при использовании РНР приходится сначала извлекать строку данных из MySQL, выполнять обработку, а затем выдавать первоначально задуманный запрос к базе данных.
Применение встроенных функций MySQL не только существенно сокращает время обработки сложных запросов, но и упрощает сами запросы. При желании подробные сведения обо всех доступных функциях можно найти по следующим URL-адресам:
О строковые функции: http://tinyurl.com/mysqlstrfuncs:
О функции даты и времени: http://tinyurl.com/mysqkJatsfjncs.
Первоначальные сведения о наиболее востребованном наборе функций изложены в приложении Г.
Работа с MySQL через phpMyAdmin
Для работы с MySQL, безусловно, важно изучить все представленные здесь основные команды и особенности их работы, но после того, как они уже изучены, для управления базами данных и таблицами будет намного проще и быстрее использовать программу phpMyAdmin.
Весь дальнейший материал основан на предлоложешш о том, что вы уже проработали предыдущие примеры этой главы, создали в базе данных publications таблицы cl assi cs и customers и теперь остается только выбрать раздел, относящийся к вашей операционной системе.
Для пользователей Windows
Перед тем как вводить в адресную строку браузера следующую строку, нужно убедиться в том, что программа Zend Server СЕ уже запущена и. значит, база данных MySQL готова к работе:
http://1ocalhost/phpMyAdmi n
Теперь экран вашего браузера должен приобрести вид, показанный на рис. 8.18, где нужно будет набрать имя пользователя zend (предлагается по умолчанию) и не вводить никакого пароля. После этого вы должны увидеть экран, похожий на рис. 8.19. Теперь вы можете приступать к изучению подраздела «Использование phpMyAdmin».
Для пользователей Mac OS X
Убедитесь в том, что у вас запущена программа Zend Server СЕ и в ней запущены серверы Apache и MySQL, а затем наберите в браузере следующую строку: http://1ocalhost:10081/phpmyadmi п/
227227рш. phpMyAdminрш. phpMyAdminzendzendРис. 8.18. Регистрация в phpMyAdminРис. 8.18. Регистрация в phpMyAdmin
227
227
рш. phpMyAdmin
рш. phpMyAdmin
zend
zend
Рис. 8.18. Регистрация в phpMyAdmin
Рис. 8.18. Регистрация в phpMyAdmin
Работа с MySQL через phpMyAdmin
С Э € # 1«Ь http://localhost P - § <S X
: File Edit View Fgvorites Tools Help
Welcome to phpMyAdmin
Language
-Log in © -Username:Password:
^100%
iij^ij^^Migift
@|Ц@®®
I localhost
gb Databases ^SQL ф Status , fg|Variables f<j|Charsets ggEngines
1
^Privileges Ij, Replication
^Processes
gfo Export jgp)Import ^Synchronize
informatkm_schema (28)
my?iql(23)~
lest
Please select a database
Actions
Ф? Change password
® Logout
MySQL localhost
^ Create new database ©
MySQL
81 Server: localhost via TCP/IP
89 Server version: 5.1.50- community
► Protocol version: 10
►User root@loca(host
Ш MySQL charset: UTF-8 Unicode (utffi)
| Collation
| r| ( Create 1
HD MySQL connection collation:
| utf8_general_dM ©
Interface
SP Language © :
| Englishn
Webserver
►Apache/2.2.21 (Win32) mod_ssl/2.2.21
OpenSSL/O.9.80
MySQL client version: mysqind 5.0.8-dev - 20102224 - SRevision: 318113$
PHP extension: mysqli
1 Theme / Style: [OriginaF
Id
Д http://lOcalh^P ▼ 8 С Х~|| ^ localhost/localhost| php... X ^
file £d»t Vkw Fflvorites Iools tl^P
^100% *
Число 10081 обозначает интерфейсный порт сервера Zend и должно всегда вводиться для вызова интерфейса или любых подразделов, таких как phpMyAdmin.
Вы должны увидеть экран, похожий на рис. 8.18, где нужно будет набрать имя пользователя zend (предлагается по умолчанию) и не вводить никакого пароля. Ваш браузер должен приобрести вид, показанный на рис. 8.19, и теперь вы можете перейти к изучению подраздела «Использование phpMyAdmin».
Для пользователей Linux
Если у вас установлен Zend Server СЕ с MySQL, для запуска phpMyAdmin можно набрать в браузере следующий адрес:
https -.//local host: 10082/phpMyAdmin
Теперь ваш браузер должен приобрести вид, похожий на рис. 8.18. После ввода имени пользователя zend (предлагается по умолчанию) вы должны увидеть экран, похожий на рис. 8.19. Теперь можно переходить к изучению следующего подраздела.
Использование phpMyAdmin
На левой панели главной страницы phpMyAdmin, которая теперь должна появиться в окне вашего браузера, щелкните на раскрывающемся меню Databases (Базы данных) и выберите базу данных publications, после чего должна открыться база данных, а ниже ее названия должны появиться имена ее двух таблиц. Щелкните на имени cl assi cs, и вы увидите, что на правой панели появилась масса информации об этой таблице (рис. 8.20).
Отсюда можно осуществлять все основные операции над базами данных, включая их создание, добавление таблиц, создание индексов и многое другое. Документация по работе с программой phpMyAdmin выложена по адресу http://www. phpmyadmi n. net/documentation/.
Если вы прорабатывали вместе со мной все примеры этой главы, то я вас поздравляю с окончанием этого длинного путешествия. Мы прошли большой путь создания базы данных MySQL до отправки сложных запросов, сочетающих несколько таблиц, использующих булевы операторы и усиленных различными спецификаторами MySQL.
В следующей главе мы приступим к изучению подходов к проектированию эффективных баз данных, освоим современные SQL-технологии, а также функции и транзакции MySQL.
Проверьте ваши знания
Для чего нужна точка с запятой в запросах MySQL?
Какие команды используются для просмотра доступных баз данных или таблиц?
ft *16*1 G«S‘ft *16*1 G«S‘Щ localhost / localhost / publicabons / classics| phpMyAdmin 2.9.1.1 - Mozflla Firefox £4t •£* ¥icw Hgtwy fipoknwls loots•
ft *16*1 G«S‘
ft *16*1 G«S‘
6 X & fisjlr jJijs ( fiu 1http://localhosfc/home/mysql/
& Server: localhost ►№ Database: publications ►ШЗ Table: classics
рЬрШцА&йарЬрШцА&йаOSBrowse: EgStiucture ДвСН- .P Search ^Insert (Ц Export 03 Import ^Operations {■Empty IS Drop
рЬрШцА&йа
рЬрШцА&йа
Database pubfccabons (2)Database pubfccabons (2)publications (2)publications (2)а >• с*9 customersа >• с*9 customersFlehlTypeCoffationAttributesNunDefaultExtraОauthorvarchar(128)Iaftn1_swedish_aYesNULLE® s£3titlevarctef(128)tetini_swe<fishdYesNULLШsОcategoryvarchar(16)Iatm1jswedish_aYesNULLSB уОyearsmaKripYesNULLЩ|й^;ЕЭisbnchar(13)Iatm1_swedisti_aNoИ>t_Check All / Uncheck All With selected: Щj? X03Я ®ЕЭFlehlTypeCoffationAttributesNunDefaultExtraОauthorvarchar(128)Iaftn1_swedish_aYesNULLE® s£3titlevarctef(128)tetini_swe<fishdYesNULLШsОcategoryvarchar(16)Iatm1jswedish_aYesNULLSB уОyearsmaKripYesNULLЩ|й^;ЕЭisbnchar(13)Iatm1_swedisti_aNoИ>t_Check All / Uncheck All With selected: Щj? X03Я ®ЕЭXXXXШ xШ xAdtpnEBШ& nФ£.‘ иAdtpnEBШ& nФ£.‘ и
Database pubfccabons (2)
Database pubfccabons (2)
publications (2)
publications (2)
а >• с*
9 customers
а >• с*
9 customers
Flehl
Type
Coffation
Attributes
Nun
Default
Extra
О
author
varchar(128)
Iaftn1_swedish_a
Yes
NULL
E® s
£3
title
varctef(128)
tetini_swe<fishd
Yes
NULL
Шs
О
category
varchar(16)
Iatm1jswedish_a
Yes
NULL
SB у
О
year
smaKrip
Yes
NULL
Щ|й^;
ЕЭ
isbn
char(13)
Iatm1_swedisti_a
No
И>
t_
Check All / Uncheck All With selected: Щ
j? X
03
Я ®
ЕЭ
Flehl
Type
Coffation
Attributes
Nun
Default
Extra
О
author
varchar(128)
Iaftn1_swedish_a
Yes
NULL
E® s
£3
title
varctef(128)
tetini_swe<fishd
Yes
NULL
Шs
О
category
varchar(16)
Iatm1jswedish_a
Yes
NULL
SB у
О
year
smaKrip
Yes
NULL
Щ|й^;
ЕЭ
isbn
char(13)
Iatm1_swedisti_a
No
И>
t_
Check All / Uncheck All With selected: Щ
j? X
03
Я ®
ЕЭ
X
X
X
X
Ш x
Ш x
AdtpnEB
Ш& n
Ф£.
‘ и
AdtpnEB
Ш& n
Ф£.
‘ и
& Pnnt vniewJK Propose table structure ©
|e Add 1 field(s) ® At End of Table о At Beginning of Table о After author - | ,<aQ,,,|
Keyname PRIMARY author me catej|0ty6 year vufoarjtlKeyname PRIMARY author me catej|0ty6 year vufoarjtlIndexes:®Type CanflnaWy ActionIndexes:®Type CanflnaWy ActionSpace usageSpace usagePRIMARY $ЮЕХ INDEX ШЕХ INDEX FULLTEXTPRIMARY $ЮЕХ INDEX ШЕХ INDEX FULLTEXT5 SNone fNone SNone ffNone УNone f5 SNone fNone SNone ffNone УNone fXXXXXXReid isbn author title category year aiihor fifeReid isbn author title category year aiihor fife20202020Type DataIndex TotalType DataIndex TotalUsageUsage«47,592«47,592вВ ввВ в
Keyname PRIMARY author me catej|0ty6 year vufoarjtl
Keyname PRIMARY author me catej|0ty6 year vufoarjtl
Indexes:®
Type CanflnaWy Action
Indexes:®
Type CanflnaWy Action
Space usage
Space usage
PRIMARY $ЮЕХ INDEX ШЕХ INDEX FULLTEXT
PRIMARY $ЮЕХ INDEX ШЕХ INDEX FULLTEXT
5 S
None f
None S
None ff
None У
None f
5 S
None f
None S
None ff
None У
None f
X
X
X
X
X
X
Reid isbn author title category year aiihor fife
Reid isbn author title category year aiihor fife
20
20
20
20
Type Data
Index Total
Type Data
Index Total
Usage
Usage
«4
7,592
«4
7,592
в
В в
в
В в
Create an index on 1
DoneDone.llsSSSS&SdaSSS&tiSiitoSSfe.llsSSSS&SdaSSS&tiSiitoSSfe:®-O. ф m:®-O. ф m
Done
Done
.llsSSSS&SdaSSS&tiSiitoSSfe
.llsSSSS&SdaSSS&tiSiitoSSfe
:®-O. ф m
:®-O. ф m
Рис. 8.20. Таблица classics, отображаемая в phpMyAdmin
Как на локальном хосте создается новый пользователь MySQL с именем newuser и паролем newpass, которому открыт доступ ко всему содержимому базы данных newdatabase?
Как просмотреть структуру таблицы?
Для чего нужен индекс в MySQL?
Какие преимущества дает индекс FULLTEXT?
Что такое стоповое слово?
Оба спецификатора, и SELECT DISTINCT и GROUP BY, приводят к отображению только одной строки для каждого значения в столбце, даже если такое значение имеют несколько строк. Какое основное различие между SELECT DISTINCT и GROUP BY?
Как можно с помощью инструкции SELECT.. .WHERE вернуть только те строки, в которых в каком-нибудь месте столбца author таблицы cl assi cs, используемой в этой главе, содержится слово Langhorne?
Что должно быть определено в двух таблицах, чтобы появилась возможность их объединения?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 8».
Q Освоение MySQL
В главе 8 была заложена хорошая основа для работы с реляционными базами данных с использованием SQL. Были рассмотрены создание баз данных и включаемых в них таблиц, а также вставка, поиск, изменение и удаление данных.
Теперь, вооружившись этими знаниями, нужно изучить проектирование баз данных, работать с которыми можно максимально быстро и эффективно. Например, научиться принимать решения о том, какие данные в какие таблицы помещать. За годы существования баз данных были разработаны руководства, следуя которым можно обеспечить эффективную работу с ними и возможность их масштабирования по мере наполнения все новыми и новыми данными.
Проектирование базы данных
Перед тем как создавать базу данных, очень важно ее удачно спроектировать, в противном случае, скорее всего, придется возвращаться назад и изменять ее структуру, разбивая одни и объединяя другие таблицы и перемещая различные графы из таблицы в таблицу с целью достижения рациональных связей, которыми MySQL будет легче воспользоваться.
Для начала было бы неплохо сесть за стол с листом бумаги и карандашом и набросать подборку тех запросов, которые, на ваш взгляд, чаще всего будут нужны вам и вашим пользователям.
Для базы данных книжного интернет-магазина могут быть записаны следующие вопросы.
О Сколько авторов, книг и покупателей имеется в базе данных?
О Каким автором написана та или иная книга?
О Какие книги написаны тем или иным автором?
О Какая книга продается по самой высокой цене?
О Какая книга является лидером продаж?
О Какие книги не покупались в этом году?
О Какие книги приобретены тем или иным покупателем?
О Какие книги были приобретены вместе с какими-нибудь другими книгами?
Разумеется, к такой базе данных может быть сделано и множество других запросов, но даже эта подборка даст вам представление о том, как следует спланировать структуру таблиц.
Например, книги и номера ISBN должны быть, наверное, скомбинированы в одной таблице, поскольку они тесно взаимосвязаны (некоторые тонкости этого вопроса будут исследованы чуть позже). В отличие от этого книги и покупатели должны находиться в разных таблицах, поскольку они весьма слабо взаимосвязаны. Покупатель может купить любую книгу и даже несколько экземпляров одной и той же книги, а книга может быть приобретена многими покупателями, и может не привлечь внимания еще большего количества потенциальных покупателей.
Когда планируется множество поисковых операций по каким-нибудь столбцам, зачастую их лучше всего поместить в общую таблицу. А когда какие-то элементы слабо связаны друг с другом, их лучше поместить в отдельные таблицы.
Если принять во внимание эти элементарные правила, то можно предположить, что для удовлетворения всех этих запросов нам понадобятся как минимум три таблицы.
О authors (авторы). Предполагается большое количество поисков по авторам, многие из которых сотрудничали при написании книг, а значит, будут показаны вместе. Оптимальных результатов поиска можно добиться, если о каждом авторе будет дана вся относящаяся к нему информация, следовательно, нам нужна таблица авторов — authors.
О books (книги). Многие книги появляются в различных изданиях. Иногда у них разные издатели, а иногда разные книги имеют одно и то же название. Связи между книгами и авторами настолько сложны, что для книг нужна отдельная таблица.
О customers (покупатели). Причина, по которой покупатели должны находиться в собственной таблице, еще более прозрачна — покупатели могут приобрести любую книгу любого автора.
Первичные ключи: ключи к реляционным базам данных
Используя возможности реляционных баз данных, мы можем задавать всю информацию для каждых автора, книги и покупателя в одном и том же месте. Очевидно, что нас интересуют связи между ними, например, кто написал каждую книгу и кто ее приобрел, и мы можем сохранить эту информацию лишь за счет создания связей между тремя таблицами. Я покажу вам основные принципы, которые нетрудно будет усвоить на практике.
Секрет заключается в присвоении каждому автору уникального идентификатора. То же самое делается для каждой книги и каждого покупателя. Смысл всего этого был объяснен в предыдущей главе: нам нужен первичный ключ. Для книги имеет смысл использовать в этом качестве номер ISBN, хотя вам, может быть, придется столкнуться с несколькими одинаковыми книгами, имеющими разные номера ISBN. Авторам и покупателям можно просто назначить произвольные
ключи, имеющие свойство автоприращения —AUTO INCREMENT, что, судя по предыдущей главе, делается весьма просто.
Короче говоря, каждая таблица будет спроектирована вокруг какого-нибудь объекта, в котором, скорее всего, будет вестись интенсивный поиск, — в данном случае вокруг автора, книги или покупателя, и этот объект должен иметь первичный ключ. В качестве ключа не следует выбирать ничего, что могло бы иметь одинаковое значение для разных объектов. Ситуация с номером ISBN является тем самым редким случаем, когда сама издательская индустрия предоставила нам первичный ключ, который можно считать уникальным для каждого продукта. В большинстве случаев для этих целей следует создавать произвольный ключ, использующий свойство AUTOJNCREMENT.
Нормализация
Процесс распределения данных по таблицам и создания первичных ключей называется нормализацией. Основная цель нормализации —обеспечить, чтобы каждая порция информации появлялась в базе данных только один раз. Дублирование данных приводит к крайне неэффективной работе, поскольку неоправданно увеличивает объем базы данных и замедляет тем самым доступ к информации. Еще важнее то, что дубликаты создают большой риск обновления только одной строки продублированных данных, приводят к несогласованности в базе данных, являющейся потенциальным источником серьезных ошибок.
Если, к примеру, названия книг перечисляются и в таблице авторов, и в таблице книг и возникает необходимость исправить опечатку в названии, нужно будет вести поиск в обеих таблицах и вносить одинаковые изменения везде, где встречаются названия книг. Лучше хранить названия в одном месте, а в других местах использовать номер ISBN.
В процессе разбиения базы данных на несколько таблиц важно не зайти слишком далеко и не создать больше таблиц, чем требуется, что может также привести к неэффективности конструкции и замедлению доступа к данным.
К счастью, изобретатель реляционной модели Эдгар Кодд проанализировал понятие нормализации и разбил его на три отдельные схемы, названные первой, второй и трет I ьей нормальными формами. Если вносить изменения в базу данных, последовательно удовлетворяющие каждой из этих форм, то будет обеспечена оптимальная сбалансированность базы данных, способствующая достижению быстрого доступа и использованию минимального объема оперативной и дисковой памяти.
Чтобы понять, как выполняется нормализация, начнем с весьма несуразной базы данных, представленной в табл. 9.1, в которой имеется одна таблица, содержащая все сведения об авторах, книгах и вымышленных покупателях. Ее можно рассматривать в качестве первой попытки создания таблицы, отслеживающей, кто из покупателей какие книги заказал. Неэффективность такой конструкции не вызывает сомнений, поскольку данные повсеместно дублируются (дубликаты в таблице выделены полужирным шрифтом), но это только лишь наша отправная точка.
Таблица 9.1. Крайне неэффективная конструкция таблицы базы данных
Author 1 (Автор 1)
Author 2 (Автор 2)
Title (Название)
ISBN
Price (Цена)
Cust. name (Имя покупателя)
Cust. address (Адрес покупателя)
Purch. date (Дата покупки)
David Sklar
Adam Trachten- b erg
PHP
Cookbook
0596101015
44,99
Emma Brown
1565 Rainbow Road, Los Angeles, CA 90014
Mar 03 2009
Danny
Goodman
Dynamic HTML
0596527403
59,99
Darren Ryder
4758 Emily Drive, Richmond, VA 23219
Dec 19 2008
Hugh E. Williams
David Lane
PHP and MySQL
0596005436
44,95
Earl B.
Thurston
862 Gregory Lane, Frankfort, KY 40601
Jun 22 2009
David Sklar
Adam Trachtenberg
PHP
Cookbo ok
0596101015
44,99
Darren Ryder
4758 Emily Drive, Richmond, VA 23219
Dec 19 2008
Rasmus Lerdorf
Kevin Tatroe & Peter MacIntyre
Programming PHP
0596006815
39,99
David Miller
3647 Cedar Lane, Waltham, MA 02154
Jan 16 2009
В следующих трех разделах мы проанализируем эту конструкцию базы данных, и вы увидите, как она может быть улучшена за счет удаления продублированных записей и разбиения одной таблицы на несколько более практичных таблиц, в каждой из которых будет храниться один тип данных.
Первая нормальная форма
Чтобы база данных соответствовала первой нормальной форме, она должна выполнять три требования.
В ней не должно быть повторяющихся столбцов, содержащих одни и те же типы данных.
Все графы должны содержать только одно значение.
Для уникальной идентификации каждой строки должен быть первичный ключ.
Рассматривая по порядку эти требования, вы заметите, что в столбцы Author 1 и Author 2 заложены повторяющиеся типы данных. Итак, у нас уже появилась та самая графа, которую следует поместить в отдельную таблицу, поскольку повторяющаяся графа Author противоречит правилу 1.
Второе несоответствие связано с тем, что для последней книги, Programming РНР, указаны три автора. Я считаю, что использование одного и того же столбца Author 2 для имен двух авторов — Kevin Tatroe и Peter MacIntyre — нарушает правило 2. Это еще одна причина перемещения всех сведений об авторах в отдельную таблицу.
А вот правило 3 здесь соблюдается, потому что первичный ключ в столбце ISBN уже создан.
В табл. 9.2 показаны результаты перемещения столбцов авторов из табл. 9.1. Теперь здесь уже меньше беспорядка, хотя все еще остаются дубликаты, выделенные полужирным шрифтом.
Таблица 9.2. Результаты удаления столбца Authors из табл. 9.1
Title (Название)
ISBN
Price (Цена)
Cust. name (Имя покупателя)
Cust. Address (Адрес покупателя)
Purch.
Date (Дата покупки)
РНР Cookbook
0596101015
44,99
Emma Brown
1565 Rainbow Road, Los Angeles, CA 90014
Mar 03 2009
Dynamic HTML
0596527403
59,99
Darren Ryder
4758 Emily Drive, Richmond, VA 23219
Dec 19 2008
РНР and MySQL
0596005436
44,95
Earl B.
Thurston
862 Gregory Lane, Frankfort, KY 40601
Jun 22 2009
PHP Co okbook
0596101015
44,99
Darren Ryder
4758 Emily Drive, Richmond, VA 23219
Dec 19 2008
Programming PHP
0596006815
39,99
David Miller
3647 Cedar Lane, Waltham, MA 02154
Jan 16 2009
Новая таблица Authors приведенная в табл. 9.3, проста по структуре и имеет довольно небольшой размер. В ней просто перечисляются номера ISBN, принадлежащие книге с тем или иным названием, рядом с которыми размещается автор. Если у книги более одного автора, соавторы получают собственную строку Поначалу эта таблица может показаться несуразной, потому что по ней нельзя понять сразу, кто из авторов какую книгу написал. Но не стоит переживать: MySQL может быстро проинформировать вас об этом. Для этого нужно лишь сообщить, для какой именно книги нужна такая информация, и MySQL воспользуется ее ISBN для поиска в таблице авторов, что займет какие-то миллисекунды.
Таблица 9.3. Новая таблица Authors
ISBN
Author (Автор)
0596101015
David Sklar
0596101015
Adam Trachtenberg
0596527403
Danny Goodman
0596005436
Hugh E Williams
0596005436
David Lane
0596006815
Rasmus Lerdorf
0596006815
Kevin Tatroe
0596006815
Peter MacIntyre
Как было отмечено ранее, ISBN будет служить в качестве первичного ключа для таблицы книг — Books, когда дело дойдет до ее создания. Я упомянул об этом, чтобы подчеркнуть, что ISBN тем не менее не являет первичным ключом для таблицы Authors. При практической разработке для таблицы Authors также нужно создать первичный ключ, обеспечивающий его уникальную идентификацию.
Поэтому для таблицы Authors графа ISBN является простой графой, для которой в целях ускорения поиска может быть, наверное, создан ключ, но этот ключ будет уже не первичным. Фактически в этой таблице он и не может быть первичным, поскольку не обладает уникальностью: один и тот же номер ISBN появляется по нескольку раз в тех случаях, когда над одной книгой работали несколько авторов.
Поскольку мы будем использовать такой ключ для связи авторов с книгами в другой таблице, эта графа называется внешним ключом.
Ключи (которые также называются индексами) имеют в MySQL несколько предназначений. Основной целью создания ключа является ускорение поиска. В главе 8 были показаны примеры, в которых ключи использовались в условиях WHERE для осуществления поиска. Но ключ можно применять и для уникальной идентификации элемента. Таким образом, уникальный ключ часто задействуется в качестве первичного ключа в одной таблице и в качестве внешнего ключа для связи строк этой таблицы со строками другой.
Вторая нормальная форма
Первая нормальная форма позволяет разобраться с продублированными данными (или избыточностью) в нескольких столбцах. Вторая нормальная форма имеет отношение только к решению проблемы избыточности в нескольких строках. Чтобы привести базу данных ко второй нормальной форме, ваши таблицы должны уже иметь первую нормальную форму.
Как только это будет сделано, для определения столбцов, данные в которых повторяются в разных местах, и последующего их перемещения в собственные таблицы применяется вторая нормальная форма.
Давайте еще раз посмотрим на табл. 9.2. Видите, Darren Ryder приобрел две книги, и поэтому его данные продублированы. Это говорит о том, что графы, имеющие отношение к покупателю (Customer name и Customer address), следует переместить в их собственные таблицы. В табл. 9.4 показан результат удаления двух столбцов, касающихся покупателя, из табл. 9.2.
Таблица 9.4. Новая таблица Titles
ISBN
Title (Название)
Price (Цена)
0596101015
РНР Cookbook
44,99
0596527403
Dynamic HTML
59,99
0596005436
PHP and MySQL
44,95
0596006815
Programming PHP
39,99
Таким образом, в табл. 9.4 остались только графы номера ISBN, названия (Ti tl е) и цены (Price) для четырех уникальных книг, поэтому теперь это эффективная в использовании и независимая таблица, удовлетворяющая требованиям как первой, так и второй нормальной формы. Попутно мы справились с сокращением информации до уровня тех данных, которые имеют непосредственное отношение к книгам с определенными названиями. Эта таблица может также включать год издания, количество страниц, количество переизданий и т. д., поскольку все эти данные имеют тесную связь друг с другом. Единственное правило, которого следует придерживаться, гласит: сюда нельзя помещать графы, которые могут содержать несколько значений для одной книги, поскольку тогда нам придется указывать одну и ту же книгу в нескольких строках, нарушая таким образом правила второй нормальной формы. К примеру, к нарушениям на этом этапе нормализации может привести восстановление столбца авторов.
Но, изучая извлеченные графы, относящиеся к покупателям, которые теперь показаны в табл. 9.5, можно заметить, что эта таблица все же требует дополнительной нормализации, поскольку сведения о покупателе Darren Ryder по-прежнему продублированы. Следует также признать, что правило 2 первой нормальной формы (все графы должны содержать только одно значение) здесь не соблюдается, поскольку адресные данные нужно разбить на отдельные графы для адреса — Address, города — City, штата —State и почтового индекса — Zip.
Таблица 9.5. Сведения о покупателях из табл. 9.2
ISBN
Cust Name (Имя покупателя)
Cust. Address (Адрес покупателя)
Purch. Date (Дата покупки)
0596101015
Emma Brown
1565 Rainbow Road, Los Angeles, CA 90014
Mar 03 2009
0596527403
Darren Ryder
4758 Emily Drive, Richmond, VA 23219
Dec 19 2008
0596005436
Earl B. Thurston
862 Gregory Lane, Frankfort, KY 40601
Jun 22 2009
0596101015
Darren Ryder
4758 Emily Drive, Richmond, VA 23219
Dec 19 2008
0596006815
David Miller
3647 Cedar Lane, Waltham, MA 02154
Jan 16 2009
Нужно продолжить разбиение этой таблицы, чтобы обеспечить однократный ввод каждого из сведений, касающихся покупателя. Поскольку ISBN не относится к таким сведениям и не может использоваться в качестве первичного ключа для идентификации покупателей (или авторов), должен быть создан новый ключ.
В табл. 9.6 показан результат нормализации таблицы Customers в соответствии с правилами первой и второй нормальных форм. Теперь у каждого покупателя есть уникальный номер покупателя, который называется CustNo, используется в качестве первичного ключа и который, скорее всего, был создан с использованием свойства автоприращения — AUTO INCREMENT. Все составляющие адресов были также распределены по разным столбцам, для того чтобы упростить их поиск и обновление.
Таблица 9.6. Новая таблица Customers
CustNo
(Номер покупателя)
Name (Имя)
Address (Адрес)
City (Город)
State (Штат)
Zip (Почтовый индекс)
1
Emma Brown
1565 Rainbow
Road
Los Angeles
CA
90014
2
Darren Ryder
4758 Emily Drive
Richmond
VA
23219
3
Earl В.
Thurston
862 Gregory Lane
Frankfort
KY
40601
4
David Miller
3647 Cedar Lane
Waltham
MA
02154
В то же время для нормализации табл. 9.6 необходимо было удалить информацию о покупках, поскольку в противном случае в ней встречались бы одни и те же сведения о покупателе для каждой купленной им книги. Вместо этого данные о покупках теперь помещены в новую таблицу Purchases (табл. 9.7).
Таблица 9.7. Новая таблица Purchases
CustNo (Номер покупателя)
ISBN
Date (Дата)
1
0596101015
Mar 03 2009
2
0596527403
Dec 19 2008
2
0596101015
Dec 19 2008
3
0596005436
Jun 22 2009
4
0596006815
Jan 16 2009
Здесь в качестве ключа, связывающего вместе таблицы Customers и Purchases, опять используется графа CustNo из табл. 9.6. Поскольку здесь повторно появляется графа ISBN, эта таблица может быть связана также либо с таблицей Authors, либо с таблицей Titles.
Графа CustNo может быть полезна в качестве ключа (но только не первичного) в таблице Purchases: один и тот же покупатель может приобрести несколько книг (и даже несколько экземпляров одной и той же книги), поэтому графа CustNo не может служить первичным ключом. Фактически у таблицы Purchases вообще нет первичного ключа. И это вполне нормально, поскольку потребностей в отслеживании уникальных покупок не предвидится. Если один покупатель приобретет два экземпляра одной и той же книги, то придется смириться с двумя строками, содержащими одну и ту же информацию. Для упрощения поиска можно определить в качестве ключей, только не первичных, обе графы, CustNo и ISBN.
Теперь у нас четыре таблицы, на одну больше, чем те три, которые потребовались бы *<SV 4 * согласно нашим первоначальным прикидкам. Мы пришли к этому решению в процессе — нормализации, методически следуя правилам первой и второй нормальных форм, которые однозначно позволили выявить необходимость существования дополнительной четвертой таблицы под названием Purchases (Покупки).
У нас есть следующие таблицы: Authors (см. табл. 9.3), Titles (см. табл. 9.4), Customers (см. табл. 9.6) и Purchases (см. табл. 9.7). Каждая из них может быть связана с любой другой с помощью либо ключа Cust No, либо ключа ISBN.
Например, для того, чтобы посмотреть, какие книги приобрел Darren Ryder, их можно поискать через табл. 9.6, Customers, где мы увидим, что CustNo этого покупателя — 2.
Теперь, имея этот номер, можно перейти к табл. 9.7, Purchases, найти там графу ISBN и увидеть, что он приобрел книги с номерами 0596527403 и 059610101519 декабря 2008 года. Подобные поиски кажутся утомительными для человека, но не составляют ни малейшего труда для MySQL.
Определить названия этих книг можно, обратившись затем к табл. 9.4, Titles, и увидев, что это книги Dynamic HTML и РНР Cookbook. Если нужно узнать авторов этих книг, следует воспользоваться номерами ISBN, которые теперь нужно найти в табл. 9.3, Authors. Станет понятно, что книгу с номером ISBN 0596527403, Dynamic HTML, написал Danny Goodman, а авторы книги с номером ISBN 0596101015, РНР Cookbook — David Sklar и Adam Trachtenberg.
Третья нормальная форма
После приведения в соответствие первой и второй нормальным формам база данных приобрела вполне подходящий вид, и в дальнейшем вам, возможно, уже не придется что-либо в ней изменять. Но если применить к базе данных более суровые требования, то можно довести ее до соответствия правилам третьей нормальной формы, которые требуют, чтобы данные, не имеющие непосредственной зависимости от первичного ключа, но имеющие зависимость от другого значения в таблице, были также перемещены в отдельные таблицы в соответствии с тем, к чему они имеют отношение.
Например, касательно табл. 9.6, Customers, можно утверждать, что ключи State, Ci ty и Zi р code не имеют прямого отношения к каждому покупателю, поскольку эти же составляющие будут присутствовать в адресах многих других людей. Но они напрямую связаны друг с другом тем, что улица в адресе —Address относится к городу — City, а город относится к штату — State.
Поэтому, чтобы соблюсти правила третьей нормальной формы для табл. 9.6, ее нужно разбить на табл. 9.8-9.11.
Таблица 9.8. Таблица Customers, соответствующая правилам третьей нормальной формы
CustNo (Номер покупателя)
Name (Имя)
Address (Адрес)
Zip (Почтовый индекс)
1
Emma Brown
1565 Rainbow Road
90014
2
Darren Ryder
4758 Emily Drive
23219
3
Earl В. Thurston
862 Gregory Lane
40601
4
David Miller
3647 Cedar Lane
02154
Таблица 9.9. Таблица Zip codes, соответствующая правилам третьей нормальной формы
Zip (Почтовый индекс)
CitylD (Идентификатор города)
90014
1234
23219
5678
40601
4321
02154
8765
Таблица 9.10. Таблица Cities, соответствующая правилам третьей нормальной формы
CitylD (Идентификатор города)
Name (Название)
StatelD (Идентификатор штата)
1234
Los Angeles
5
5678
Richmond
46
4321
Frankfort
17
8765
Waltham
21
Таблица 9.11. Таблица States, соответствующая правилам третьей нормальной формы
StatelD (Идентификатор штата)
Name (Название)
Abbreviation (Аббревиатура)
5
California
CA
46
Virginia
VA
17
Kentucky
KY
21
Massachusetts
MA
Ну и как пользоваться этим набором из четырех таблиц вместо одной таблицы 9.6? В табл. 9.8 нужно найти Zip-код, затем в табл. 9.9 — соответствующий ему город. Располагая этой информацией, в табл. 9.10 можно найти название'города, а затем — идентификатор штата —StatelD, который можно использовать в табл. 9.11 для поиска его названия.
Хотя подобное подстраивание под третью нормальную форму может показаться излишним, у него могут быть и свои преимущества. Например, взгляните на табл. 9.11, в которую удалось включить как название, так и двухбуквенную аббревиатуру штата. Сюда же при желании можно также включить данные о количестве жителей и другие демографические сведения.
t3f ч
Таблица 9.10 может также содержать более локализованную демографическую информа- ^Х 4 * цию, которая может оказаться полезной вам и (или) вашим покупателям. Разбивая эти — -—*#?* данные на части, вы можете упростить обслуживание своей базы данных в будущем, когда потребуется добавить к таблицам дополнительные графы.
Решение о том, к чему именно следует применить правило третьей нормальной формы, может оказаться непростым. Оценка должна основываться на том, какие дополнительные данные могут понадобиться со временем. Если вы абсолютно уверены в том, что ничего, кроме имени и адреса покупателя, не понадобится, то, наверное, без этой заключительной стадии нормализации можно будет обойтись.
С другой стороны, представьте, что вы создаете базу данных для такой крупной организации, как Почтовая служба США. Что вы будете делать, если город будет переименован? С такой таблицей, как табл. 9.6, вам придется проводить глобальный поиск и менять название города везде, где оно упоминается. Но если ваша база данных нормализована по правилам третьей нормальной формы, нужно будет изменить всего лишь одну запись в табл. 9.10 для того, чтобы это изменение отразилось на всей базе данных.
Поэтому я советую ответить себе на два вопроса, которые помогут принять решение, нужно ли применять нормализацию по правилам третьей нормальной формы к той или иной таблице.
Существует ли вероятность того, что к таблице нужно будет добавить много новых граф?
Может ли когда-нибудь для любого из полей этих таблиц потребоваться гло- бальноеобновление?
Если оба ответа на эти вопросы положительные, значит, наверное, вам все же следует провести заключительную стадию нормализации.
Когда не следует проводить нормализацию
Теперь, когда вы ознакомились со всеми тонкостями нормализации, я хочу рассказать о том, почему нужно отбросить все эти правила при работе с сайтами, имеющими высокий уровень обращений. Теперь я не говорю, что вы зря потратили время на чтение последних нескольких страниц (скорее всего, это не так), но, не следует проводить полную нормализацию таблиц, используемых сайтом, если это приведет к излишней загруженности MySQL.
Видите ли, нормализация требует распространения данных по нескольким таблицам, а это означает, что при каждом запросе будет осуществляться несколько вызовов MySQL. Если на сайте, пользующемся высокой популярностью, будут нормализованные таблицы и счет одновременно обслуживаемых пользователей пойдет на десятки, скорость доступа к базе данных существенно снизится, потому что для их обслуживания потребуются сотни обращений к этой базе. Если серьезно, то я хочу пойти еще дальше и сказать, что вы должны провести максимально возможную денормалнзацию любых часто востребуемых данных.
Причина в том, что дублирование данных в таблицах позволяет существенно сократить количество необходимых дополнительных запросов, потому что основная масса востребованных данных доступна в каждой таблице. Это означает, что можно будет просто добавить к запросу еще одну графу, и это поле станет доступно для всех соответствующих результатов, хотя (разумеется), вам придется смириться со всеми упомянутыми ранее издержками, включая использование большого объема дискового пространства и обеспечение обновления каждой отдельной копии продублированных данных, когда одна из них требует модификации.
Конечно, многократные обновления можно компьютеризировать. Система MySQL предоставляет свойство под названием триггеры (triggers), которые осуществляют автоматические изменения базы данных в соответствии с произведенными вами изменениями. (Триггеры в данной книге не рассматриваются.) Другой способ копиро-
вания в среде избыточных данных состоит в настройке PHP-программы на регулярный запуск и поддержание всех копий в синхронизированном состоянии. Программа считывает изменения с «ведущей» таблицы и обновляет все остальные. (Способы доступа к MySQL из РНР будут показаны в следующей главе.)
Но пока вы не приобретете опыт работы с MySQL, я рекомендую проводить полную нормализацию всех ваших таблиц, чтобы это вошло в привычку и принесло пользу в дальнейшем. Только после этого можно приступать к выявлению «заторов» в работе MySQL и присматриваться к денормализации.
Отношения
MySQL называют системой управления реляционными базами данных, потому что в ее таблицах содержатся не только данные, но и отношения между ними. Существует три категории отношений.
«Один к одному»
Отношение «один к одному» между двумя типами данных похоже на традиционные брачные отношения: каждый элемент данных соотносится только с одним элементом другого типа. Это на удивление редкий тип отношений. Например, автор может написать несколько книг, у книги может быть несколько авторов, и даже адрес может быть связан с несколькими покупателями. Возможно, наилучшим примером, встречавшимся в этой главе, может послужить отношение «один к одному» между названием штата и его двухбуквенной аббревиатурой.
Чтобы легче было объяснить, что это такое, давайте предположим, что по какому-нибудь конкретному адресу может проживать только один покупатель. В таком случае отношение Customers-Addresses на рис. 9.1 будет отношением «один к одному»: только один покупатель живет по каждому адресу, и по каждому адресу может жить только один покупатель.
Обычно, когда у двух элементов имеется отношение «один к одному», их включают в качестве граф в одну и ту же таблицу. Для отнесения их к двум отдельным таблицам могут быть две причины:
О вы хотите быть готовыми к тому, что позже это отношение изменится;
О в таблице слишком много граф, и вы полагаете, что производительность работы системы или возможности ее обслуживания улучшатся за счет ее разбиения.
Разумеется, когда дело дойдет до создания вашей собственной, настоящей базы данных, между покупателями и адресами нужно будет создать отношения «один ко многим» (один адрес, много покупателей).
«Один ко многим»
Отношения «один ко многим» (или «многие к одному») возникают в том случае, когда одна строка в одной таблице связана со многими строками в другой таблице. Вы уже поняли, что в табл. 9.8 возникли бы отношения «один ко многим», если бы
несколько покупателей проживали по одному и тому же адресу. В таком случае ее нужно разбить.
Таблица 9.8, a (Customers)
Таблица 9.8, б (Addresses)
CustNo
Name , „ ,
Addre$&
Ш^
1
Emma Brown
1565Rainbow Road
90014
2
Darren Ryder
47583EmilyуDrive
23219
3
Earl В. Thurston
8622Gregory Lane
40601
4
David Miller
36477CedarгLaneв
02154
Рис. 9.1. Таблица покупателей, Customers (см. табл. 9.8), разбитая на две таблицы
Если посмотреть на табл. 9.8, а, показанную на рис. 9.1, можно увидеть, что у нее имеется отношение «один ко многим» с таблицей покупок, табл. 9.7, поскольку каждый покупатель представлен только одним конкретным человеком из табл. 9.8, а.
Но табл. 9.7, Purchases, может содержать (и содержит) более одной покупки, сделанной одним и тем же покупателем. Поэтому один покупатель имеет отношение ко многим покупкам.
На рис. 9.2 эти две таблицы показаны рядом друг с другом, а линии соединяют строки в каждой таблице и, начинаясь в одной строке левой таблицы, могут соединять с ней более одной строки правой таблицы. Схема отношения «один ко многим» также хорошо подходит и для описания отношения «многие к одному», в этом случае нужно левую и правую таблицы поменять местами и рассматривать их как отношение «один ко многим».
Таблица 9.8, a (Customers)
Таблица 9.7 (Purchases)
Custblo
Name
CustNo
ISBN
Date
1
Emma Brown
1 1
0596101015
Mar 03 2009
2
Darren Ryder—
—..2 ’
0596527403
Dec 19 2008
L—
2 ’
0596101015
Dec 19 2008
3
Earl B. Thurston
3 3
0596005436
Jun 22 2009
4
David Miller-
4 4
0596006815
Jan 16 2009
Рис. 9.2. Иллюстрация отношения между двумя таблицами
«Многие ко многим»
В отношении «многие ко многим» многие строки в одной таблице связаны со многими строками в другой таблице. Чтобы создать это отношение, нужно добавить третью таблицу, содержащую по столбцу из каждой из этих двух таблиц, с помощью
которых они могут быть связаны. В третьей таблице больше ничего не содержится, она предназначена только для связи других таблиц.
Именно такой промежуточной таблицей и является табл. 9.12. Она была извлечена из табл. 9.7, Purchases (Покупки), но в ней отсутствует информация о дате покупки. Теперь она содержит копию номера ISBN каждой проданной книги, а также номер покупателя.
Таблица 9.12. Промежуточная таблица
Customer (Покупатель)
ISBN
1
0596101015
2
0596527403
2
0596101015
3
0596005436
4
0596006815
С помощью этой промежуточной таблицы можно пройти по всем хранящимся в базе данным, пользуясь схемой их отношений. За отправную точку можно взять адрес и найти авторов любых книг, приобретенных покупателем, проживающим по этому адресу.
Предположим, к примеру, что нужно найти покупки, связанные с почтовым индексом 23219. Если поискать этот почтовый индекс (zip code) в табл. 9.8, б, то можно обнаружить, что покупатель с номером 2 приобрел как минимум одну книгу, имеющуюся в базе данных. Теперь можно воспользоваться табл. 9.8, а, и найти имя этого покупателя или воспользоваться новой промежуточной табл. 9.12, для того чтобы найти приобретенную им книгу или книги.
По этой таблице можно определить, что были приобретены две книги, и, отследив их номера в табл. 9.4, найти названия и цены этих книг или обратиться к табл. 9.3 и увидеть в ней их авторов.
Если вам показалось, что все это, по сути, не что иное, как сочетание нескольких отношений «один ко многим», то так оно и есть. Чтобы проиллюстрировать это, на рис. 9.3 все три таблицы представлены вместе.
Столбцы из таблицы 9.8, б (Customers)
Промежуточная таблица 9.12 (Customers/ISBN)
Столбцы из таблицы 9.4 (Titles)
Zip
Cust
CustNo
ISBN
ISBN
Title
90014
1
— 1
0596101015
0596101015
PHP Cookbook
23219
2 г ~
-- 2
0596527403
L™
—2
0596101015
0596527403
Dynamic HTML
40601
з
— 3
0596005436
0596005436
PHP and MySQL
02154
4 ...-
4
0596006815
0596006815
Programming PHP
Рис. 9.3. Создание отношения «многие ко многим» с помощью третьей таблицы
Проследите по любому почтовому индексу (zip-коду) в левой таблице связанные с ним идентификаторы покупателей. Далее можно проследить их связь с промежуточной таблицей, которая объединяет левую и правую таблицы путем связывания покупательских идентификаторов и номеров ISBN. Теперь остается только проследовать по ISBN к правой таблице, чтобы увидеть, к какой книге он относится.
Промежуточную таблицу можно использовать также для следования в обратном направлении — от названий книг до zip-кода. Из таблицы Titl es можно взять ISBN, которым воспользоваться для поиска в промежуточной таблице идентификационных номеров покупателей этих книг, и, наконец, в таблице Customers идентификационные номера будут сопоставлены с zip-кодами мест проживания покупателей.
Базы данных и анонимность
Интересный аспект использования отношений заключается в том, что о каком- нибудь элементе, например покупателе, можно собрать массу сведений, не зная ничего о его личности. Обратите внимание на то, что в предыдущем примере мы прошли от покупательских zip-кодов к их покупкам и вернулись назад, не определяя имен покупателей. Базы данных могут использоваться для отслеживания сведений о людях, но они также могут использоваться и для защиты относящихся к ним конфиденциальных данных, при этом сохраняется возможность поиска полезной информации.
Транзакции
В некоторых приложениях жизненно необходимо, чтобы последовательность запросов шла в нужном порядке и при этом каждый отдельный запрос успешно завершался. Представим, например, что создается последовательность запросов для перевода средств с одного банковского счета на другой. Вам бы не хотелось, чтобы при этом происходило что-либо подобное.
О Вы зачислили средства на второй счет, а когда попытались снять их с первого счета, при обновлении данных произошел сбой, и теперь эти средства числятся на обоих счетах.
О Вы сняли средства с первого банковского счета, но при запросе на обновление с целью их зачисления на второй счет произошел сбой и теперь эти средства бесследно исчезли.
Как видите, для этого типа транзакций важен не только порядок выполнения запросов, необходимо также, чтобы все части транзакции завершились успешно. Но как все это обеспечить? Ведь после осуществления запроса аннулировать его уже невозможно. Необходимо ли отслеживать все части транзакции, а затем проводить полный откат, если одна из ее частей даст сбой? Ничего этого делать не нужно, поскольку MySQL поставляется с мощным средством обработки транзакций, которое защищает именно от таких непредвиденных обстоятельств.
Кроме того, транзакции предоставляют одновременный доступ к базе данных множеству пользователей или программ за счет обеспечения очередности проведения всех транзакций, и каждый пользователь или программа соблюдают очередность, не наступая друг другу на пятки, — MySQL со всем этим прекрасно справляется.
Ядра (механизмы хранения) транзакций
Чтобы использовать имеющееся в MySQL средство обработки транзакций, нужно воспользоваться MySQL-ядром InnoDB. Это делается довольно просто, потому что нужно всего лишь использовать другой параметр при создании таблицы. Создадим таблицу банковских счетов, введя команды, показанные в примере 9.1. (Напомню, что для этого нужно получить доступ к командной строке MySQL и воспользоваться подходящей для этой таблицы базой данных.)
Пример 9.1. Создание таблицы, готовой к обработке транзакций
CREATE TABLE accounts (
number INT, balance FLOAT. PRIMARY KEY(number)
) ENGINE InnoDB;
DESCRIBE accounts;
Команда, которая находится в последней строке этого примера, отобразит содержимое новой таблицы, позволяя убедиться в ее успешном создании. Будет выведена следующая информация:
| Field | Type | Null | Key |Default| Extra |
+ »b=r"+„ta)i ’+«•Го+;
| balance | float | YES || NULL ||
++++-+
2 rows in set (0.00 sec)
Теперь создадим в этой таблице две строки, которые можно будет задействовать в транзакциях. Для этого введем команды, показанные в примере 9.2.
Пример 9.2. Заполнение таблицы accounts
INSERT INTO accounts(number, balance) VALUES(12345. 1025.50);
INSERT INTO accounts(number, balance) VALUES(67890, 140.00);
SELECT * FROM accounts;
Команда в третьей строке отобразит содержимое таблицы, подтверждая успешное создание строк. Будет выведена следующая информация:
| number | balance |
+"12345'14^:51
| 67890 |140 |
+++
2 rows in set (0.00 sec)
После создания и предварительного заполнения этой таблицы можно приступить к использованию транзакций.
Использование команды BEGIN
Транзакции в MySQL начинаются либо с команды BEGIN, либо с команды START TRANSACTION. Чтобы отправить транзакцию системе MySQL, введите команды, показанные в примере 9.3.
Пример 9.3. Транзакция MySQL
BEGIN;
UPDATE accounts SET balance=balance+25.11 WHERE number=12345;
COMMIT;
SELECT * FROM accounts;
Результаты этой транзакции выводятся командой, содержащейся в последней строке, и должны иметь следующий вид:
| number | balance |
Г^Ю.бГ;
| 67890 |140 |
+++
2 rows in set (0.00 sec)
Как видите, баланс счета 12345 увеличился на 25,11 и теперь составляет 1050,61.
В примере 9.3 можно было также заметить команду COMMIT, которая рассматривается в следующем разделе.
Использование команды COMMIT
Когда вы убедитесь в том, что ряд запросов, входящих в транзакцию, успешно выполнен, введите команду COMMIT, чтобы передать все изменения базе данных. До тех пор пока не будет получена команда COMMIT, все внесенные изменения рассматриваются MySQL как временные. Эта особенность позволяет отменить транзакцию, отправляя вместо команды передачи COMMIT команду отката ROLLBACK.
Использование команды ROLLBACK
Используя команду ROLLBACK, можно заставить MySQL забыть обо всех запросах, выданных с начала до конца транзакции. Можете проверить эту команду в действии путем ввода транзакции по переводу средств, показанной в примере 9.4.
Пример 9.4. Транзакция по переводу средств
BEGIN;
UPDATE accounts SET balance=balance-250 WHERE number=12345;
UPDATE accounts SET balance=balance+250 WHERE number=67890;
SELECT * FROM accounts;
Как только будут введены эти строки, вы увидите следующий результат:
| number | balance |
Г^Г
|67890 |390 |
+ - - - - - - - - + +
2 rows in set (0.00 sec)
Теперь у первого банковского счета значение на 250 единиц меньше, чем раньше, а значение второго увеличилось на 250 единиц — вы осуществили между ними перевод на 250 единиц. А теперь предположим, что что-то пошло не так и эту транзакцию нужно отменить. Для этого нужно лишь ввести команду, показанную в примере 9.5.
Пример 9.5. Отмена транзакии/ :команды ROLLBACK
ROLLBACK;
SELECT * FROM accounts:
Теперь вы должны увидеть следующую выходную информацию, показывающую восстановление прежнего баланса на обоих счетах, благодаря тому что транзакция была отменена командой ROLLBACK:
number | balance
12345 | 1050.61
67890 |140
++
2 rows in set (0.00 sec)
Использование команды EXPLAIN
Система MySQL поставляется с мощным инструментарием, который позволяет исследовать, как она интерпретировала выданные ей запросы. Используя команду EXPLAIN, можно получить отображение состояния любого запроса, чтобы понять, можно ли его выдать более удобным или эффективным способом. Применение этой команды с созданной ранее таблицей accounts показано в примере 9.6.
Пример 9.6. Использование команды EXPLAIN
EXPLAIN SELECT * FROM accounts WHERE number='12345’ ;
Результаты выполнения команды EXPLAIN будут выглядеть следующим образом:
i~yse'ect_type| table|type |possible_keys|key|key_len|ref |oowsEExtaa|
+ ___+ + + + + + + + +
| 1 51МЗ_Е | accounts | const | PRIMARY| PRIMARY 14| const |1||
+ -+++++++. - --++
1 "2л •- se: (0.00 sec)
Здесь MySQL предоставляет вам следующую информацию:
О select_type. Тип выборки простой — SIMPLE. При объединении таблиц будет показан объединенный (join) тип;
О table. Текущей запрашиваемой таблицей была accounts;
О type. Тип запроса — const. Если идти от худшего к лучшему, то возможные значения выстраиваются в следующий ряд: ALL, index, range, ref, eq_ref, const, system и NULL;
О possi bl e_keys. Возможно, это первичный ключ, PRIMARY, а это значит, что доступ должен быть быстрым;
О key. В данном случае используется ключ PRIMARY, что является хорошим показателем;
О key len. Длина ключа равна 4. Это количество байтов индекса, которое будет использовано MySQL;
О ref. Столбец ref отображает, какие графы или константы используются с ключом. В данном случае используется константный ключ;
О rows. Количество строк, которые должны быть просмотрены этим запросом, равно 1, что также является хорошим показателем.
Когда появится запрос, который подозревается в лишней трате времени на свое выполнение, попробуйте воспользоваться командой EXPLAIN, чтобы посмотреть, как его можно оптимизировать. Вы сможете обнаружить, какие ключи (если таковые имеются) были задействованы, какова их длины и т. д., и тогда можно будет соответствующим образом подкорректировать запрос или конструкцию таблицы (или таблиц).
После того как эксперименты с временной таблицей accounts будут завершены, может по* явиться желание удалить эту таблицу с помощью следующей команды:
DROP TABLE accounts;
Резервное копирование и восстановление данных
Независимо от того, какого рода данные хранятся в вашей базе данных, они все равно должны представлять для вас определенную ценность, даже если она измеряется временем, необходимым для их повторного ввода в случае повреждения жесткого диска. Поэтому для защиты вложенного вами труда важно сохранять резервные копии. Может также возникнуть потребность в перемещении вашей базы данных на новый сервер, и наилучшим способом является предварительное снятие с нее резервной копии. Важно также время от времени проверять резервные копии, для того чтобы убедиться в их целостности и работоспособности.
Создание резервных копий и восстановление данных MySQL существенно облегчается при использовании команды mysqldump.
Использование команды mysqldump
Команда mysqldump позволяет выгрузить базу данных или коллекцию баз данных в один или несколько файлов, содержащих все инструкции, необходимые для воссоздания всех ваших таблиц и повторного заполнения их данными. Эта команда также может создавать файлы в формате с разделением значений запятыми — CSV (Comma-Separated Values) и в других текстовых форматах, использующих разделители, или даже в XML-формате. Главный недостаток этой команды заключается в том, что в процессе резервного копирования таблицы нужно обеспечить, чтобы никто не вел в нее запись. Эта задача решается разными способами, но самый простой заключается в остановке MySQL:cepBepa перед запуском mysqldump и его повторном запуске после окончания ее работы.
Можно также перед запуском команды mysql dump заблокировать все копируемые таблицы. Для блокировки чтения таблиц (поскольку нам нужно считать данные) в командную строку MySQL нужно ввести следующую команду:
LOCK TABLES имя_таблицы1 READ, имя_таблицы2 READ ...
А для снятия блокировки нужно ввести следующую команду:
UNLOCK TABLES:
По умолчанию вся выходная информация выводится командой mysqldump на стандартное устройство, но ее можно перенаправить в файл, воспользовавшись символом >.
Стандартный формат mysqldump имеет следующий вид:
mysqldump -и пользователь -рпароль база_данных
Но перед тем, как выгружать содержимое базы данных, следует убедиться в том, что путь к программе mysqldump может быть найден по умолчанию или что ее размещение указано в самой команде. В табл. 9.13 показаны наиболее вероятные места нахождения этой программы для различных установок и операционных систем, рассмотренных в главе 2. Если у вас какой-нибудь другой вариант установки, ее местонахождение может быть несколько иным.
Таблица 9.13. Наиболее вероятные места нахождения программы mysqldump для различных установок
Операционная система и программа
Наиболее вероятная папка местонахождения
ZendServerCE для 32-разрядной версии Windows
C:\Pro0ram Files\zend\MySQL51\bin
ZendServerCE для 64-разрядной версии. Windows
C:\Program Files(x86)\zend\MySQL51\bin
OS X Zend Server СЕ
/usr/local/zend/mysql/bin
Linux Zend Server CE
/usr/local/zend/mysql/bin
Для вывода содержимого базы данных publications, созданной в главе 8, на экран наберите mysql dump (или при необходимости укажите полный путь) и команду, показанную в примере 9.7.
Пример 9.7. Вывод базы данных publications на экран mysqldump -и пользователь -рпароль publications
Вместо слов пользователь и пароль подставьте имя пользователя и пароль, которые используются в вашей установке MySQL. Если пароль для пользователя не установлен, эту часть команды можно опустить, но часть команды - и пользователь является обязательной, если только у вас не установлен привилегированный доступ (root) без пароля и вы не работаете в этом режиме (что делать не рекомендуется). Результат ввода этой команды будет ::сх-:ж на тот, что изображен на рис. 9.4.
с= : - i£i W#Mi3с= : - i£i W#Mi3В 2| C:\Window5\5y5tem3Z\cmd.exe
с= : - i£i W#Mi
3
с= : - i£i W#Mi
3
ENGINE;-Нс/ISAM DEFAULT CHARSET = 1 at in 1 ;
•Uta foi‘
/ ^ ? 'I^?«t-'1H ALTER INSEKT INTO 'c« B85iV l232et •’ >; s*»M8000 fl Lia UNLOCK TABLES;/ ^ ? 'I^?«t-'1H ALTER INSEKT INTO 'c« B85iV l232et •’ >; s*»M8000 fl Lia UNLOCK TABLES;гпгокжгTABLEгпгокжгTABLEUfiLUEUfiLUEJHSftBLE KEJHSftBLE KEENaiiLEENaiiLE..ZONE..ZONE>*♦40101 ./*♦48814 '/**48814 у **. Л Л.Ш>*♦40101 ./*♦48814 '/**48814 у **. Л Л.ШSQI. HO»£v--tfOI.D POKE IGK КЕУ. C UNIQUE .CHECKS- СНОЯЛСТЕ.ЧSQI. HO»£v--tfOI.D POKE IGK КЕУ. C UNIQUE .CHECKS- СНОЯЛСТЕ.ЧSQL, MODE * / ; ECKS l30Li) 1 «Г -eOLi). UNI will; CHESQL, MODE * / ; ECKS l30Li) 1 «Г -eOLi). UNI will; CHE1Ш tl1Ш tl---e. w.SET CLJ £Н1 -PO! 1? < SET RESULTS - КЛ, tt)MNECTION*G"H» v Р0Ы1)J.?QL...NOTES **---e. w.SET CLJ £Н1 -PO! 1? < SET RESULTS - КЛ, tt)MNECTION*G"H» v Р0Ы1)J.?QL...NOTES **м I;м I;
/ ^ ? 'I^?«t-'1H ALTER INSEKT INTO 'c« B85iV l232et •’ >; s*»M8000 fl Lia UNLOCK TABLES;
/ ^ ? 'I^?«t-'1H ALTER INSEKT INTO 'c« B85iV l232et •’ >; s*»M8000 fl Lia UNLOCK TABLES;
гпгокжгTABLE
гпгокжгTABLE
UfiLUE
UfiLUE
JHSftBLE KE
JHSftBLE KE
ENaiiLE
ENaiiLE
..ZONE
..ZONE
>*♦40101 ./*♦48814 '/**48814 у **. Л Л.Ш
>*♦40101 ./*♦48814 '/**48814 у **. Л Л.Ш
SQI. HO»£v--tfOI.D POKE IGK КЕУ. C UNIQUE .CHECKS- СНОЯЛСТЕ.Ч
SQI. HO»£v--tfOI.D POKE IGK КЕУ. C UNIQUE .CHECKS- СНОЯЛСТЕ.Ч
SQL, MODE * / ; ECKS l30Li) 1 «Г -eOLi). UNI will; CHE
SQL, MODE * / ; ECKS l30Li) 1 «Г -eOLi). UNI will; CHE
1Ш tl
1Ш tl
---e. w
.SET CLJ £Н1 -PO! 1? < SET RESULTS - КЛ, tt)MNECTION*G"H» v Р0Ы1)J.?QL...NOTES **
---e. w
.SET CLJ £Н1 -PO! 1? < SET RESULTS - КЛ, tt)MNECTION*G"H» v Р0Ы1)J.?QL...NOTES **
м I;
м I;
£ 'А--'А0 1.1 : ii
Рис. 9.4. Выгрузка базы данных publications экз-ан
Создание файла резервной копии
Запустив команду mysqldump и убедившись в том. что она выводит на экран нужные данные, можно перенаправить данные резервной копии непосредственно в файл, используя символ >.
Если предположить, что вам захотелось назвать файл резервной кошш ' cati ons. sq1l, нужно ввести команду, показанную в примере 9.8 (не забудьте подставшъ вместо слов пользователь и пароль настоящее имя пользователя и пароль).
Пример 9.8. Выгрузка базы данных publicav : -s е Zz?r mysqldump -u пользователь -pпароль pub"~:ат~^s > i^ r'^catiors.sc'
Команда в примере 9.8 сохраняет файл резервной копии в текущем каталоге. Если нужно * сохранить его в каком-нибудь другом месте, то перед именем файла следует указать соответствующий путь. Кроме того, необходимо убедиться в том, что каталог, куда будет сохраняться файл резервной копии, имеет соответствующие установки доступности, позволяющие записывать в него этот файл.
При выводе файла резервной копии на экран или загрузке его в текстовый редактор вы увидите, что он состоит из последовательности SQL-команд:
DROP TABLE IF EXISTS 'classics';
CREATE TABLE 'classics' (
'author' varchar(128) default NULL.
’title ' varchar(128) default NULL,
'category' varchar(16) default NULL.
'year' smal1int(6) default NULL.
'isbn' char С13) NOT NULL default
PRIMARY KEY ('isbn').
KEY 'author' ('author' (20)).
KEY 'title ' ( 'title ' (20)).
KEY 'category' ('category' (4)).
KEY 'year' ('year') .
FULLTEXT KEY 'author_2' ('author'. 'title ')
) ENGINE=MyISAM DEFAULT CHARSET=latinl;
Это весьма продуманный код, который может быть использован для восстановления базы данных из резервной копии, даже если она уже существует, поскольку сначала он удалит все таблицы, которые должны быть воссозданы, избавляясь таким образом от потенциальных ошибок MySQL.
Создание резервной копии отдельной таблицы
Чтобы создать резервную копию отдельной таблицы базы данных (такой как таблица classics базы данных publications), сначала нужно из командной строки MySQL заблокировать таблицу, набрав следующую команду:
LOCK TABLES publications.classics READ;
Это обеспечит работу MySQL в режиме чтения, но сделает невозможной запись. Затем, не закрывая командную строку MySQL, используйте другое окно терминала, чтобы ввести из командной строки операционной системы следующую команду: mysqldump -и пользователь -рпароль publications classics > classics.sq l
Теперь можно снять блокировку таблицы, для чего в первом окне терминала в командной строке MySQL нужно ввести следующую команду, которая разблокирует все таблицы, заблокированные в текущем сеансе:
UNLOCK TABLES;
Создание резервной копии всех таблиц
Если понадобится создать резервную копию сразу всех ваших баз данных MySQL (включая и такие системные базы данных, как mysql), можно воспользоваться командой, показанной в примере 9.9, которая позволит восстановить всю установку базы данных MySQL, но при этом следует не забыть про блокировку там, где она потребуется.
Пример 9.9. Выгрузка всех баз данных MySQL в файл
mysqldump -и пользователь -рпароль --all-databases > all1 _databases.sql
& ч
Г^
л *Разумеется, в файлах резервных копий баз данных MySQL содержится очень много строк SQL-
м?» 4 « кода. Я советую потратить несколько минут на изучение ряда этих строк для ознакомления —^ с типами команд, которые встречаются в файлах резервных копий, и с порядком их работы.
Восстановление данных из файла резервной копии
Чтобы восстановить данные из файла, нужно вызвать исполняемую программу mysql и передать ей файл, из которого восстанавливаются данные, для чего следует воспользоваться символом <. Для восстановления всей базы данных, выгруженной с помощью ключа - -all-databases, используется команда, показанная в примере 9.10.
Пример 9.10. Восстановление полного набора баз "5-гь*
mysql - и пользователь -рпароль < all_databcses.se'
Для восстановления одной базы данных применяется ключ -D, за которым следует имя базы данных. В примере 9.11 показано, как восстановить базу данных publ i cati ons из резервной копии, созданной кодом, который показан в примере 9.8.
Пример 9.11. Восстановление базы данных publicat"ors
mysql -u пользователь -рпароль -D publications < publications.sql
Для восстановления отдельной таблицы базы данных используется команда, показанная в примере 9.12, где в базе данных pub! i cati ons восстанавливается только таблица cl assi cs.
Пример 9.12. Восстановление таблицы classics в базе данных publications mysql -и пользователь -рпароль -D publications < classics.sq l
Выгрузка данных в файлы формата CSV
Как уже отмечалось, программа mysql dump обладает завидной гибкостью и поддерживает различные типы выходных данных, в том числе формат CSV. В примере 9.13 показано, как можно выгрузить данные из таблиц cl assi cs и customers базы данных publications в файлы classics.txt и customers.txt, находящиеся в папке с: /temp. По умолчанию при работе в среде Zend Server СЕ пользователь должен быть под именем root и не применять пароль. Если работа идет в операционной системе OS X или Linux, следует изменить путь назначения на существующую папку.
Пример 9.13. Выгрузка данных в файлы формата CSV
mysqldump -и пользователь -рпароль --no-create-info --tab=c:/temp
--fields-terminated-by='.' publications
Команда слишком длинная, и в этом примере она занимает две строки, но вводить ее нужно в одной строке. В результате работы команды будет выведен следующий текст:
Mark Twain (Samuel Langhorne Clemens)'The Adventures
of Tom Sawyer’ . 'Classic Fiiction '.'1876'.'9781598184891
Jane Austen’ , 'Pride and Prejudice', 'Classic Fictio n '.'1811','9780582506206
Charles Darwin','The Origin of Species’ . 'Non-Fiction','1856'.'9780517123201
Charles Dickens'. 'The Old Curiosity Shop'. 'Classic Fictio n '.'1841','9780099533474
William Shakespeare'. 'Romeo and J u1l i iett'. 'Play'.'1594','9780192814968
Mary Smith’. ’9780582506206
Jack Wilson'.'9780517123201
Планирование резервного копирования
Золотое правило резервного копирования гласит, что его следует проводить с той периодичностью, которая имеет практический смысл. Чем ценнее данные, тем чаще следует создавать их резервные копии и тем больше резервных копий нужно делать. Если ваша база данных обновляется хотя бы раз в сутки, то резервное копирование нужно проводить ежедневно. Если же она не подвергается частым обновлениям, то резервные копии можно создавать значительно реже.
ar
Нужно также подумать о создании нескольких резервных копий и о хранении их в разных V*‘ df« местах. Если у вас используются несколько серверов, то можно просто растиражировать резервные копии по этим серверам. Можно также прислушаться к хорошему совету, заключающемуся в том, что; следует создавать физические резервные копии съемных жестких дисков, миниатюрных носителей, компакт-дисков или DVD и т. д. и хранить их в разных местах, предпочтительно в чем-то вроде сейфов.
После изучения материалов этой главы вы должны стать специалистом по работе как с РНР, так и с MySQL. В следующей главе будет показано, как можно объединить эти две технологии.
Проверьте ваши знания
Что означает слово отношение (relationship) применительно к реляционным базам данных?
Какое понятие применяется к процессу удаления повторяющихся данных и оптимизации таблиц?
Как формулируются три правила первой нормальной формы?
Как привести таблицу в соответствие с правилом второй нормальной формы?
Что нужно поместить в графу, для того чтобы связать две таблицы, содержащие элементы, имеющие отношение «один ко многим»?
Как создать базу данных с отношением «многие ко многим»?
Какие команды инициируют и завершают транзакцию MySQL?
Какие возможности предоставляет MySQL для изучения подробностей работы запроса?
Какую команду нужно использовать для создания резервной копии базы данных pub! icattions в файле publ icati ons. sql ?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 9».
Л#
Д0СТУП к MySQL
с использованием РНР
При полноценном изучении предыдущих глав вы должны были приобрести навыки работы как с MySQL, так и с РНР. В этой главе вы узнаете, как объединить эти два компонента путем использования встроенных в РНР функций доступа к MySQL.
Запросы к базе данных MySQL с помощью РНР
Смысл использования РНР в качестве интерфейса к MySQL заключается в форматировании результатов SQL-запросов и придании им внешнего вида, предназначенного для вывода на веб-страницу. Обладая возможностью входа в установленную систему MySQL с помощью своего имени пользователя и пароля, вы можете сделать то же самое и из РНР Но вместо использования командной строки MySQL для ввода команд и просмотра выходной информации нужно будет создать строки запроса, а затем передать их MySQL. Ответ MySQL поступит в виде структуры данных, которую РНР сможет распознать, а не в виде того отформатированного экранного вывода, который вы видели в процессе работы с командной строкой. Затем с помощью команд РНР можно будет извлекать данные и приводить их к формату веб-страницы.
Процесс
Процесс использования MySQL с помощью РНР заключается в следующем.
Подключение к MySQL.
Выбор базы данных, которая будет использоваться.
Создание строки запроса.
Выполнение запроса.
Извлечение результатов и вывод их на веб-страницу.
Повторение шагов с 3-го по 5-й до тех пор. пока не будут извлечены все необходимые данные.
Отключение от MySQL.
Далее процесс будет рассмотрен поэтапно, но сначала важно настроить все элементы входа в систему на безопасную работу, для того чтобы шпионы, заинтересовавшиеся вашей системой, натыкались на заслон при попытке получения доступа к вашей базе данных.
Создание файла регистрации
Большинство веб-сайтов, разработанных на РНР, содержат множество программных файлов, которым понадобится доступ к MySQL, и им нужны будут сведения, касающиеся входа в систему и пароля. Поэтому имеет смысл создать отдельный файл для их хранения, а затем включать его туда, где он необходим. Такой файл, который я назвал 1 ogin. php, показан в примере 10.1. Наберите текст этого примера, заменяя значения имя_пользователя и пароль теми, которыми вы пользуетесь для входа в свою базу данных MySQL, и сохраните текст в файле, поместив его в разработочный каталог, созданный согласно рекомендациям, сделанным в главе 2. Через некоторое время этот файл нам пригодится. Имя хоста 1 ocal host будет работать до тех пор, пока вы используете базу данных MySQL на своей локальной системе, точно так же будет работать и база данных publications, если вы вводили в компьютер код всех встречавшихся ранее примеров.
Пример 10.1. Файл login.php
<?php // login.php
$db_hostname = 'localhost';
$db_database = 'publications';
$db_username = ' имя_пользователя' ;
$db_password = 'пароль':
?>
Для файла 1 ogi n. php, показанного в примере 10.1, особую роль играют охватывающие теги <?php и ?>, поскольку они дают понять, что все строки, находящиеся между ними, должны интерпретироваться только как код РНР. Если их не поставить, то при вызове файла непосредственно с вашего веб-сайта он будет отображен в виде текста, раскрывая все ваши секреты. А когда теги на месте, на сайте будет видна пустая страница. Этот файл будет без каких-либо проблем включаться в другие ваши РНР-файлы.
Переменная $db_hostname сообщит РНР, какой компьютер следует использовать при подключении к базе данных. Ее присутствие обусловлено тем, что вы можете получить доступ к любой базе данных MySQL на любом компьютере, подключенном к той машине, на которой вы установили РНР, и она может потенциально включать в себя любой хост на просторах Всемирной паутины. Но примеры, приводимые в данной главе, будут работать только на локальном сервере. Поэтому здесь будет указываться не домен вроде mysql.myserver.com, а может просто использоваться слово localhost (или 1Р-адрес 127.0.0.1).
В роли рабочей базы данных, $db_database, будет выступать база данных publications, которую вы, наверное, уже создали, изучая главу 8, или одна из тех баз данных, которую вам предоставил администратор вашего сервера (в таком случае нужно будет также внести соответствующие изменения в файл 1 ogi n. php).
Переменным $db_username и $db_password нужно присвоить значения пользовательского имени и пароля, которые используются при работе с MySQL.
"«^
Другим преимуществом хранения всех сведений, необходимых для входа в систему, в одном
«У 4 « месте станет возможность изменения пароля с нужной ван периодичностью, для чего при- ——л^- дется обновлять только один файл, независимо от количества PHP-файлов, получающих доступ к MySQL.
Подключение к MySQL
После сохранения файла login.php можно будет с помощью инструкции require_ once включать его в любые PHP-файлы, которым нужен доступ к базе данных. Выбор пал именно на эту инструкцию, а не на инструкцию i ncl ude, поскольку, если файл не будет найден, он сгенерирует фатальную ошибку. И уж поверьте мне, если не будет найден файл, содержащий сведения хтя подключения к вашей базе данных, это действительно будет фатальной ошибкой.
А использование requi ге_опсе, а не reqi.'re означает, что файл будет считан только в том случае, если он не был включен до этого в какой-нибудь другой файл, что исключит совершенно бесполезные повторные обращения к диску. Код, используемый для подключения, показан в примере 10.2.
Пример 10.2. Подключение к базе данных MySQL
<?php
requi re_once ’ 1ogi n .php’ ;
$db_server = mysql_connect($db_hostname. Scz^se^rame. $db_password);
if ( !$db_server) die("HeB03M0>KH0 подключиться к MySQL: " . mysql_error()); ?>
ТВэтом примере запускается РНР-функция mysql connect, которой нужны три параметра: имя хоста (hostname) MySQL-сервера, имя пользователя (username) и пароль (password). В случае успешного подключения эта функция возвращает идентификатор сервера, а в случае неудачи — значение FALSE. Обратите внимание на то, что инструкция i f во второй строке используется вместе с функцией die, работа которой созвучна ее имени (прекратить) и заключается в выходе из РНР с сообщением об ошибке, если переменная Sdb server не имеет истинного значения.
В сообщении, передаваемом функции die, объясняется, что подключиться к базе данных MySQL не представилось возможным, и чтобы помочь выяснить причины произошедшего, здесь содержится вызов функции mysql_error. Эта функция выводит текст объяснения ошибки, возникшей при вызове последней функции MySQL.
Сервер базы данных, указанный в переменной $dbserver, будет задействован в ряде следующих примеров для идентификации запрашиваемого сервера MySQL. При таком использовании идентификаторов появляется возможность подключения и доступа к нескольким серверам MySQL из одной РНР-программы.
Функция die хорошо подходит для разработки кода РНР, но на рабочем сервере, вам, конечно же, захочется использовать более вразумительные сообщения об ошибках. В этом случае нужно будет не выходить из программы РНР в аварийном режиме, а составить сообщение, которое будет отображено при нормальном выходе из программы, например: function mysql_fatal_error($msg)
{
$msg2 - mysql_error();
echo «< _END
К сожалению, завершить запрашиваемую задачу не представилось возможным. Было получено следующее сообщение об ошибке:
<p>$msg: $msg2</p>
Пожалуйста, щелкните на кнопке возврата вашего браузера и повторите попытку. Если проблемы не прекратятся, пожалуйста. <а href=Mmailto:admin@server.com">coo6iiiHTe о них нашему администратору </а>. Спасибо.
END:
)
Выбор базы данных
После успешного подключения к MySQL появляется возможность выбрать базу данных, с которой нужно будет работать. Как это сделать, показано в примере 10.3.
Пример 10.3. Выбор базы данных
<?php
mysql_select_db($db_database)
or diе("Невозможно выбрать базу данных: " . mysql_error());
?>
Для выбора базы данных используется команда mysql_select_db. Ей нужно передать имя требуемой базы данных и сервер, к которому вы подключены. Как и в предыдущем примере для вывода сообщения об ошибке и объяснения ее причины в случае сбоя при выборе базы данных в код включена функция die. Единственное отличие состоит в том, что в этом примере не нужно запоминать значение, возвращаемое функцией mysql sel ect_db, поскольку она возвращает либо TRUE, либо FALSE. Поэтому был использован PHP-оператор or, означающий следующее: «Если при выполнении предыдущей команды произойдет сбой, нужно выполнить тот код, который следует за этим оператором». Учтите, что для срабатывания оператора or в конце первой строки кода не должно быть точки с запятой.
Создание и выполнение запроса
Оправка запроса к MySQL из РНР сводится к простому вызову функции mysql_ query. Порядок ее использования показан в примере 10.4.
Пример 10.4. Отправка запроса к базе данных
<?php
$query = "SELECT * FROMclassics":
$result = mysql_query($query);
if С!Sresult) die ("Сбой при доступе к базе данных: " . mysql_error()): ?>
Сначала переменной Squery присваивается значение, содержащее код предстоящего запроса. В данном случае запрашивается просмотр всех строк таблицы cl assi cs. Учтите, что, в отличие от командной строки MySQL, здесь в конце запроса точка с запятой не нужна, поскольку для завершения запроса используется функция mysql query, которая не может применяться для запроса, состоящего из последовательно отправляемых частей. Поэтому MySQL знает, что запрос завершен, и не ищет точки с запятой.
Функция возвращает результат, помещаемый в переменную Sresult. После использования MySQL в режиме командной строки можно подумать, что содержимое Sresul t будет таким же, как тот результат, который возвращался запросом, вводимым в командной строке, с горизонтальными и вертикальными линиями и т. д. Но в РНР возвращается не такой результат. В случае успешного выполнения запроса переменная Sresul t будет содержать ресурс, позволяющий извлечь результаты этого запроса. Извлечение данных будет показано в следующем разделе. При сбое переменная Sresul t содержит значение FALSE. Поэтому пример завершается проверкой значения этой переменной. Если оно равно FALSE, значит, произошла ошибка и должна быть выполнена функция die.
Извлечение результата
Ресурс, возвращенный функцией mysql_query, можно использовать для извлечения требуемых данных. Наиболее простой способ заключается в последовательном извлечении нужных ячеек с помощью функции mysql result. Пример 10.5 объединяет и расширяет предыдущие примеры до программы, которую можно набрать и самостоятельно запустить для получения возвращенных результатов. Я советую сохранить это программу в той же папке, в которой был сохранен файл 1 ogi n. php, и присвоить файлу этой программы имя query. php.
Пример 10.5. Поячеечное извлечение результатов
<?php // query.php
requi re_once '1ogi n.php’ ;
$db_server = mysql_connect($db_hostname. $db_username. Sdb_password);
if (!Sdb_server) die("HeB03M0MH0 подключиться к MySQL: " . mysql_error());
mysql_select_db($db_database)
or die("HeB03M0>KH0 выбрать базу данных: " . mysql_error());
Squery = "SELECT * FROMclassics":
Sresult = mysql_query(Squery):
if (!Sresult) die ("Сбой при доступе к базе данных: " . mysql_error()):
Srows = mysql_num_rows(Sresult):
for (Sj = 0 ; Sj < Srows : ++Sj)
echo 'Author: '
. mysql_result($result,$j,'author')
'<br />'/
echo 'Title : '
. m ysqlresult($result,$j.'title')
'<br />'/
echo 'Category:
_
' . mysql_result($result,$j.'category') .
'<br />'/
echo 'Year: '
. m ysql_result($result,$j.'year')
'<br />'/
echo 'ISBN: '
. mysql_result($result,$j.'isbn*)
'<br /><br /
?>
Здесь новыми для нас являются десять последних строк кода, поэтому их и рассмотрим. Они начинаются с присвоения переменной $rows значения, возвращенного в результате вызова функции mysql num rows. Эта функция сообщает о количестве строк, возвращенных запросом.
Получив счетчик строк, мы входим в цикл for, который извлекает каждую ячейку данных из каждой строки с помощью функции mysql resul t. В качестве параметров этой функции используются ресурс $result, возвращенный функцией mysql_ query, номер строки $j и имя графы, из которой следует извлечь данные.
Затем результаты, получаемые при каждом вызове функции mysql resul t, включаются в инструкции echo для отображения по одному полю на каждой строке с дополнительным символом перевода строки между строками. Результат запуска этой программы показан на рис. 10.1.
Ф Mozitla Rrefox
..
v. Ы@ИШ'
:5^У» №w Hifioiy
fiookmwte Доок ДОр ..
v:..Q.
..| ( D I http://localho5t/wefa/quMy.php/quwy.php
ф '«■ fc jlCbl Google
Author Mark Twain (Samuel Langhorne Clemens)
Tide: The Adventures ofTom Sawyer
Category. Classic Fiction
Year 1876
ISBN: 9781598184891
Author Jane Austen
Tide: Pride and Prejudice
Category: Classic Fiction
Year 1811
ISBN: 9780582506206
Author. Charles Darwin
Title: The Origin of Spedes
Category. Non-Fiction
Year 1856
ISBN: 9780517123201
Author Charles Dickens
Tide: The Old Curiosity Shop
Category: Classic Fiction
Year 1841
ISBN: 9780099533474
Author. WiBtam Shakespeare
DoneDone®:<z*> ФШI
Done
Done
Рис. 10.1. ДанныIе, выводимые программой query.php, представленной в примере 10.5
Вы, наверное, помните, что мы в главе 8 заполнили таблицу classics пятью строками, и, конечно же, программа query.php возвращает пять строк данных. Но в данном виде этот код выполняется крайне неэффективно и медленно, потому что для поячеечного извлечения всех данных в общей сложности осуществляется 25 вызовов функции mysql result. Существует гораздо более удачный способ извлечения данных, позволяющий при каждом вызове функции mysql_fetch_row получать отдельную строку таблицы.
В главе 9 шла речь о первой, второй и третьей нормальных формах, а теперь можно заметить, что таблица classics не удовлетворяет правилам этих форм, потому что сведения как об авторах, так и о книгах включены в одну и ту же таблицу. Причина в том, что эта таблица была создана еще до того, как мы приступили к изучению нормализации. Но ее повторное использование в целях иллюстрации доступа к MySQL из РНР избавляет нас от необходимости ввода нового набора тестовых данных, поэтому в данном случае мы продолжим работу с этой таблицей.
Извлечение строки
Мне было важно показать способ извлечения из базы данных MySQL отдельно взятой ячейки, но гораздо эффективнее извлекать всю строку. Поэтому замените цикл for в программе query. php (см. пример 10.5) новым циклом, показанным в примере 10.6, и увидите, что будет получен абсолютно такой же результат, как показанный на рис. 10.1.
Пример 10.6. Замена цикла for для построчного извлечения результатов
<?php
for ($j = 0 ; $j < Srows ; ++$j)
{
$row = mysql fetch_
_row($result):
echo 'Author: '
$row[0] .
' <br
/>':
echo 'Title: ' .
Srow[l] .
’ <br
/>':
echo 'Category: '
$row[2] .
'<br
/>':
echo 'Year: '
$row[3] .
'<br
/>’:
echo 'ISBN: '
$row[4] .
'<br
/><br /
} ?>
В этом измененном коде функция, обращающаяся к MySQL, вызывается в пять раз (на 80 %) реже, поскольку функция mysql_fetch_row позволяет извлекать каждую строку таблицы целиком. Эта функция возвращает одну строку данных в массиве, который затем присваивается переменой $row.
После извлечения данных останется только обратиться по очереди к каждому элементу массива $row (начиная с нулевого). Получается, что $row[0] содержит данные об авторе, $row[l] — о названии книги и т. д., потому что каждая графа помещена в массив в том порядке, в котором она появляется в таблице MySQL. Также использование вместо mysql_result функции mysql_fetch_row значительно сокращает объем кода РНР и время его выполнения, поскольку здесь применяется простая ссылка на каждый элемент данных по смещению в массиве, а не имя этого
элемента.
Отключение
По окончании работы с базой данных от нее следует отключиться. Это делается с помощью команды, показанной в примере 10.7.
Пример 10.7. Отключение от базы данных MySQL
<?php
mysql_close($db_server);
?>
Здесь функции нужно передать тот идентификатор, который был возвращен функцией mysql connect из примера 10.2 и сохранен в переменной $db_server.
Все подключения к базам данных автоматически закрываются по выходе из РНР, поэтому *, т°/ что подключение в примере 10.5 не было закрыто, не имеет никакого значения. Но в бо- t‘ лее длинных программах, где могут последовательно осуществляться подключения к базам данных и отключения от них, настоятельно рекомендуется по завершении доступа закрывать каждое подключение к базе данных.
Практический пример
Теперь настало время создать наш первый пример использования РНР для вставки данных в таблицу MySQL и удаления их оттуда. Я рекомендую набрать пример 10.8 и сохранить его в вашем разработочном каталоге в файле под именем sqltest.php. В результате работы кода из этого примера экран приобретает вид, показанный на рис. 10.2.
нЗГ•»
—
В примере 10.8 создается стандартная HTML-форма. Более подробно такие формы рассмат- uf'4jIL риваются в следующей главе, а здесь обработка формы используется только для демонстрации взаимодействия с базой данных.
Пример 10.8. Вставка и удаление данных с помощью программы sqltesIt.php
<?php // sqltest.php require_once 'login.php';
$db_server = mysql_connect($db_hostname. $db_username, $db_password);
if ( !$db_server) die("Невозможно подключиться к MySQL: " . mysql_error());
mysql_select_db($db_database. $db_server)
or die("Невозможно выбрать базу данных: " . mysql_error());
if (isset($_P0ST['delete']) && isset($_P0ST['isbn'])) {
$isbn = get_post('isbn'):
$query = "DELETE FROMclassics WHERE isbn='$isbn'
if (!mysql_query($query, $db_server))
Mozilla Firefox
№ №: Мм» . Ьй*ску goobnarie loots gdpv
в X A £Sk in i ( DD ] http://localhost/web/sqtest.php”"'"’фу " | (lE^IHjGocy* ' ' '/^[
Authorfl
T itle
Category
Year ISBNJ
1 AQPBECQRP.I
Author Hark Twain (Samuel Langhorne Clemens)
T itle The Adventures of Tom Sawyer
Category Classic F iction
Year 1876
ISBN 9781598184891
[и ш у ш У
Author Jane Austen
T itle Pride and Prejudice
Category Classic Fiction
Year 1811
ISBN 9780582S06206
ImeiEMCoRel
Author Charles Darwin
T itle The Origin of Species
Category Non- Fiction
Year 1856
Done© #>©,
Рис. 10.2. Вид экрана, получаемый в результате работы кода из примера 10.8,сохраненного в файле sqltest.php
echo "Сбой при удалении данных: $query<br />" mysql_error() . "<br /xbr />";
}
if (isset($_P0ST['author']) &&
isset($_POST['title']) &&
isset($_POST['category']) &&
isset($_POST['year']) &&
isset($_POST['isbn']))
{
Sauthor=get_post('author'):
Stitle=get_post('title'):
Scategory=get_post('category');
Syear=get_post('year'):
$i sbn=get_post('i sbn');
$query = "INSERT INTO classics VALUES" .
"('Sauthor'. '$title'. 'Scategory'. 'Syear', 'Sisbn')";
if (!mysql_query(Squery, $db_server))
echo "Сбой при вставке данных: $query<br />" mysql_error() . "<br /><br />";
}
echo <«_END
<form action="sqltest’.php" method="post"><pre>
Author <input type="text" name="author" />
Title <input type="textn name=,,titIle " />
Category <input type="text" name=,,category" />
Year <input type=,,text" name=Myear" />
ISBN <input type="text" name="isbn" />
<input type="submit" value="ADD RECORD" /> // кнопка
// ДОБАВИТЬ ЗАПИСЬ
</pre></form>
_END;
Squery = "SELECT * FROMclassics"; $result = mysql_query(Squery);
if ( !Sresult) die ("Сбой при доступе к базе данных: " . mysql_error());
Srows = mysql_num_ rows(Sresult);
for ($j = 0 ; $j < Srows ; ++$j) {
Srow = mysql_fetch_row($result):
echo <« _ END
<pre>
Author $row[0]
Title $row[l]
Category $row[2]
Year $row[3]
ISBN $row[4]
</pre>
<form action="sqltest.php" method="post">
<input type="hidden" name="delete" value=nyes" />
<input type="hidden" name="isbn" value="$row[4]" />
<input type="submit" value="DELETE RECORD" /></form> // кнопка
// УДАЛИТЬ ЗАПИСЬ _END:
mysql_close($db_server):
function get_post($var)
{
return mysql_real_escape_string($6POST[$var]);
} ?>
Эта программа со своими более чем 80 строками кода может показаться пугающей, но не стоит переживать — многие из этих строк уже были изучены в примере 10.5, а в работе этого кода нет ничего сложного.
Сначала производится проверка всех введенных данных, а затем в соответствии с предоставленным вводом осуществляется либо вставка новых данных в таблицу classics базы данных publ ications, либо удаление строки из этой таблицы. Независимо от того, что именно было введено, программа вслед за этим выводит все строки таблицы в браузер. Давайте посмотрим, как все это работает.
Первая часть нового кода начинается с использования функции isset, чтобы проверить, были ли отправлены программе значения для всех полей. На основании такого подтверждения каждая из первых шести строк, находящихся внутри инструкции if, вызывает функцию get post, которая появляется в самом конце программы. Эта функция делает небольшую, но очень важную работу: извлекает введенные данные из браузера.
Массив $_POST
В главе 3 я уже упоминал о том, что браузер отправляет пользовательский ввод в виде GET- или POST-запроса. Обычно предпочтение отдается POST-запросу, поэтому именно он здесь и используется. Веб-сервер объединяет все введенное пользователем (даже если в форме заполнено под сотню полей) и помещает его в массив $_P0ST. .
$_P0ST относится к ассоциативным массивам, рассмотренным в главе 6. Данные формы будут помещаться в ассоциативный массив по имени $_P0ST или S GET в зависимости от того, какой метод используется для отправки формы, POST или GET. Оба этих массива могут быть прочитаны абсолютно одинаковым способом.
У каждого поля имеется элемент в массиве, имеющий точно такое же имя. Поэтому, если в форме есть поле по имени i sbn, в массиве S P0ST будет элемент с ключом isbn. PHP-программа может прочитать это поле, ссылаясь на него либо в виде $_P0ST['isbn'], либо в виде $_P0ST["isbn"] (в данном случае одинарные и двойные кавычки обладают одинаковым действием).
Если синтаксис работы с массивом $_P0ST кажется вам слишком сложным, можно спокойно воспользоваться приемом, показанным в примере 10.8: скопировать введенные пользователем данные в другие переменные, после чего о массиве $_P0ST можно будет забыть. Для PHP-программ это вполне нормальный подход: они получают все поля из массива S P0ST в самом начале программы и больше к нему не обращаются.
& ч
———————————
Записывать элемент в массив $_POST нет никакого смысла. Он предназначен только для « передачи информации из браузера в программу, и перед его изменением лучше скопировать #?• данные в свои собственные переменные.
Возвращаясь к функции get post, следует отметить, что она пропускает каждый получаемый ею элемент через функцию mysql_real_escape_string, чтобы удалить любые символы, которые злоумышленник может вставить в целях взлома или изменения вашей базы данных.
Удаление записи
После загрузки разных переменных, которые могли быть отправлены с любыми переданными им значениями, программа проверяет, есть ли значение у переменной
$_POST[ 'delete' ]. Если у нее есть значение, значит, пользователь щелкнул на кнопке DELETE RECORD (Удалить запись). В этом случае должно быть отправлено и значение Sisbn.
Как уже говорилось, номер ISBN является уникальным идентификатором каждой записи. HTML-форма добавляет ISBN к строке запроса DELETE FROM, создаваемой в переменной Squery, которая затем передается функции mysql query, чтобы этот запрос попал к MySQL. Функция mysql_query возвращает либо TRUE, либо FALSE, и значение FALSE становится поводом для вывода сообщения об ошибке, объясняющего причины неудачи.
Если переменная Sdelete не содержит слова yes (да), выполняется следующая инструкция else. Переменной Squery присваивается текст команды INSERT INTO, за которым следует перечисление пяти вставляемых значений. Затем эта переменная передается функции mysql query, которая по завершении своей работы возвращает либо TRUE, либо FALSE. Если возвращено значение FALSE, выводится сообщение об ошибке.
Отображение формы
Теперь мы добрались до той части кода, которая отображает небольшую форму, показанную в верхней части рис. 10.2. Структура echo <« _ END, которая выводит на экран все, что находится между тегами END, должна быть вам знакома по предыдущим главам.
Вместо команды echo программа может завершить работу с интерпретатором РНР, используя <*У * • тег ?>, выдать код HTML, а затем опять вернуться к работе с интерпретатором РНР, исполь- I—i^. зуя тег <?php. Какой из стилей применять — дело вкуса программиста, но я всегда рекомендую оставаться в рамках PHP-кода в силу следующих причин:
при отладке (а также при разборе кода другими пользователями) это дает абсолютную уверенность в том, что все содержимое файла .php является кодом РНР. Поэтому не возникает нужды отлавливать временные выходы в код HTML;
когда значение переменной РНР нужно вставить непосредственно в код HTML, можно просто набрать ее имя внутри этого кода. А при выходе в HTML нужно будет временно вернуться к обработке РНР, вывести переменную, а затем снова вернуться в HTML.
Раздел HTML-формы направляет все, что сделано в форме, в адрес файла sqltest.php. Это означает, что при отправке формы содержимое ее полей будет передано файлу sql test, php, в котором и содержится сама программа. Форм а настроена также на отправку полей в POST-, а не в GET-запросе. Причина в том, что GET-запросы являются дополнением к отправляемому URL-адресу и могут иметь в вашем браузере неприглядный вид. Эти запросы также позволяют пользователям без особого труда вносить изменения в отправляемую информацию и предпринимать попытки взлома вашего сервера. Поэтому при малейшей возможности нужно использовать для отправки данных POST-запросы, которые к тому же имеют преимущество, позволяющее скрыть отправляемые данные от просмотра.
При выводе полей формы HTML отображает кнопку отправки — Submit с именем ADD RECORD (Добавить запись), и форма закрывается. Обратите внимание
на использование тегов <рге> и </рге>, позволяющих воспользоваться моноширинным шрифтом и выровнять по линейке все элементы ввода данных. Внутри тегов <рге> в выводимые данные попадают также символы возврата каретки, стоящие в конце каждой строки.
Запросы к базе данных
Далее код программы возвращается в привычное для нас русло примера 10.5, где в следующих четырех строках кода в адрес MySQL отправляется запрос на выдачу всех записей в таблице classics. Затем переменной Srows присваивается значение, равное количеству строк в таблице, и для отображения содержимого каждой строки вводится цикл for.
Следующую часть кода я немного упростил. Вместо использования тегов <br />, предназначенных в примере 10.5 для перевода строки, я решил воспользоваться тегом <рге>, чтобы выровнять на экране каждую запись, придав всему изображению привлекательный вид.
После отображения каждой записи следует вторая форма, которая также отправляет все свои данные файлу sql testt. php (то есть самой программе), но теперь в форме есть два скрытых поля: delete и isbn. Поле delete устанавливается в yes, а полю isbn присваивается значение, сохраненное в элементе массива $row[4], в котором содержится ISBN для этой записи. Далее отображается кнопка Submit с надписью DELETE RECORD (Удалить запись), и форма закрывается. Затем фигурная скобка закрывает тело цикла for, который продолжает работу до тех пор, пока не будут отображены все записи.
В самом конце программы дано определение функции get post, которую мы уже рассматривали. Вот так выглядит наша первая PHP-программа, предназначенная для управления базой данных MySQL. А теперь проверим, на что она способна.
Запуск программы
После набора программы (и исправления всех опечаток) попробуйте ввести в поля ввода следующие сведения о книге Moby Dick, которые предназначены для добавления новой записи к базе данных:
Herman Melville
Moby Dick
Fiction
1851
9780199535729
Когда эти данные будут отправлены с помощью кнопки ADD RECORD (Добавить запись), прокрутите веб-страницу до самого конца, чтобы посмотреть только что добавленную информацию. Ее предполагаемый вид показан на рис. 10.3.
Теперь посмотрим, как работает удаление записи, специально создав для этого ненужную запись. Попробуйте ввести во все пять полей только одну цифру 1 и щелкните на кнопке ADD RECORD (Добавить запись). Если теперь прокрутить
страницу вниз, станет видна новая запись, состоящая из одних единиц. Конечно, такая запись в таблице не нужна, поэтому теперь щелкните на кнопке DELETE RECORD (Удалить запись) и снова прокрутите страницу вниз, чтобы убедиться в том, что запись была удалена.
Mozilla Firefox
file £drt )ifcm Нфсму gookmarks Tools fcldp£>■
' С X ^ Qj?. Лй (D I http://localhost/web/sqltest.phpft - HEM ax*** p\
‘category won-nccxon~ ---
Year 1856
ISBN 9780517123201
| DELETE RECORD |
Author Charles Dickens
Title The Old Curiosity Shop
Category Classic Fiction
Year 1841
ISBN 9780099533474
[ DELETE RECORD |
Author William Shakespeere
Title Romeo and Juliet
Category Play
Year 1594
ISBN 9780192814968
[ DELETE RECORD |
Е
г Author Herman Melville
Title Moby Dick
Category Fiction
Year 1851
ISBN 9780199535729
| DELETE RECORD |
DoneDoneчч© ф Щ)
Done
Done
ч
ч
Рис. 1 0.3. Результат добавления сведений о книге M oby Dick в базу данных
Теперь, если все получилось, вы можете добавлять и удалять записи по своему усмотрению. Попробуйте сделать все это несколько раз, но основные записи (включая и новую запись о книге Moby Dick) оставьте нетронутыми, поскольку они нам еще пригодятся. Можно также попробовать добавить запись, состоящую из одних единиц, два раза и посмотреть, как при второй попытке будет выведено сообщение об ошибке, в котором говорится о том, что в таблице уже есть запись со значением ISBN, равным единице.
Практическая работа с MySQL
Теперь вы готовы к изучению некоторых практических приемов, которые можно использовать в РНР для доступа к базам данных MySQL, куда включены задачи создания и удаления таблиц, вставки, обновления и удаления данных, а также защиты вашей базы данных и веб-сайта от злоумышленников. Учтите, что в следующих примерах предполагается, что вы создали программу 1 ogi п. php, рассмотренную ранее в этой главе.
Создание таблицы
Предположим, что зоопарк поручил вам создать базу данных со сведениями обо всех содержащихся в нем представителях семейства кошачьих. Вам сказали, что в зоопарке девять представителей кошачьих: лев, тигр, ягуар, леопард, пума, гепард, рысь, каракал и домашний кот. Вам необходима отдельная графа для вида кошачьих. У каждого животного есть кличка, и для нее нужна еще одна графа. Необходимо также отслеживать возраст животных, и для этого требуется еще одна графа.
Разумеется, позже могут понадобиться дополнительные графы, например для учета рациона питания, сделанных прививок и других сведений, но пока, чтобы приступить к работе, достаточно и этих граф. Каждому животному нужен также уникальный идентификатор, поэтому решено было создать для него графу id.
В примере 10.9 показан код, который можно использовать для создания таблицы MySQL, хранящей все эти сведения. Операция присваивания, относящаяся к главному запросу, выделена полужирным шрифтом.
Пример 10.9. Создание таблицы cats
<?php
require_once 'login.php';
$db_server = mysql_connect($db_hostname. $db_username. $db_password):
if ( !$db_server) die("Невозможно подключиться к MySQL: " . mysql_errorO): mysql select_db($db_database)
or die("HeB03M0>KH0 выбрать базу данных: " . mysql_error()):
$query = "CREATE TABLE cats (
id SMALLINT NOT NULL AUTOINCREMENT,
family VARCHAR(32) NOT NULL,
name VARCHAR(32) NOT NULL, age TINYINT NOT NULL.
PRIMARY KEY (id)
)":
Sresult = mysql_query($query):
if ( !Sresult) die ("Сбой при доступе к базе данных: " . mysql_error()): ?>
Как видите, MySQL-запрос очень похож на тот запрос, который приходилось набирать непосредственно в командной строке, за исключением того, что в нем нет завершающей точки с запятой, поскольку при доступе к MySQL из РНР она не нужна.
Описание таблицы
Если вы не вошли в командную строку MySQL, то можно воспользоваться весьма полезным фрагментом кода, позволяющим проверить в браузере факт успешного создания таблицы. Этот код просто выдает запрос DESCRIBE имя_таблицы, а затем выводит HTML-таблицу, имеющую четыре заголовка: Column (Графа), Туре (Тип), Nul 1 (Нуль) и Key (Ключ), ниже которых отображаются все имеющиеся в таблице
графы. Чтобы использовать код примера 10.10 с другими таблицами, нужно просто заменить в запросе имя cats именем новой таблицы.
Пример 10.10. Описание таблицы cats
<?php
require_once 'login.php';
$db_server = mysql_connect(Sdb_hostname, $db_username, $db_password);
if ( !$db_server) die("Невозможно подключиться к MySQL: " . mysql_error());
mysql_select_db($db_database)
or die(,,Heвoзмoжнo выбрать базу данных: " . mysql_error()):
$query = "DESCRIBE cats";
Sresult = mysql_query(Squery);
if ( !Sresult) die ("Сбой при доступе к базе данных: " . mysql_error()):
Srows = mysql_num_ rows(Sresult):
echo "<table><tr> <th>Column</th> <th>Type</th> <th>Nul1</th> <th>Key</th> </tr>";
for (Sj = 0 : Sj < Srows : ++$j)
{
Srow = mysql_fetch_row(Sresult);
echo "< I tr>":
for (Sk = 0 : Sk < 4 ; ++Sk) echo "<td>Srow[Sk]</td>": echo "</tr>":
}
echo "</table>":
?>
Информация, выводимая программой, должна иметь следующий вид:
Column TypeNull Key
id smal1int(6) h N0 PRI
family varchar(32) h N0
name varchar(32) h N0
age tinyint(4) h N0
Удаление таблицы
Удалить таблицу очень легко, и это весьма опасное действие нужно выполнять с большой осторожностью. В примере 10.11 показан необходимый для этого код. Но я не советую его применять до тех пор, пока не будут проработаны все остальные примеры, поскольку в результате его выполнения будет удалена таблица cats и вам придется создавать ее заново с помощью кода, показанного в примере 10.9.
Пример 10.11. Удаление таблицы cats
<?php
requi re_once '1ogin.php';
$db_server = mysql_connect(Sdb_hostname. $db_username, $db_password):
if ( !$db_server) die("Невозможно подключиться к MySQL: " . mysql_error()); mysql_select_db($db_database)
or die("Heвoзмoжнo выбрать базу данных: " . mysql_error()):
$query « "DROP TABLE cats";
Sresult = mysql_query($query):
if ( !Sresult) die ("Сбой при доступе к базе данных: " . mysql_error()): ?>
Добавление данных
Добавим к таблице некоторые данные, воспользовавшись кодом, показанным в примере 10.12.
Пример 10.12. Добавление данных к таблице cats
<?php
require_once 'login.php':
$db_server = mysql_connect($db_hostname. $db_username. $db_password):
if ( !$db_server) die("Невозможно подключиться к MySQL: " . mysql_error()): mysql_select_db($db_database)
or die("Heвoзмoжнo выбрать базу данных: " . mysql_error()):
Squery = "INSERT INTO cats VALUES(NULL, 'Lion', 'Leo'. 4)";
Sresult = mysql_query(Squery);
if (!Sresult) die ("Сбой при доступе к базе данных: " . mysql_error()); ?>
Если изменить значение переменной Squery, как показано далее, можно добавить еще два элемента данных и еще раз вызвать программу из вашего браузера:
Squery = "INSERT INTO cats VALUESCNULL. 'Cougar'. 'Growler', 2)":
Squery = "INSERT INTO cats VALUESCNULL, 'Cheetah'. ’Charly'. 3)":
Кстати, вы заметили, что в качестве первого параметра было передано значение NULL? Это сделано, потому что столбец i d имеет тип AUTOINCREMENT и MySQL решит, что нужно присвоить следующее доступное в используемой последовательности значение, поэтому мы просто передаем значение NULL, которое будет проигнорировано.
•йг •*
Разумеется, наиболее эффективный способ заполнения MySQL данными заключается в со- « здании массива и вставке данных с использованием только одного запроса.
Извлечение данных
Теперь, когда в таблицу cats введены некоторые данные, в примере 10.13 показано, как можно убедиться в том, что они были благополучно вставлены в эту таблицу.
Пример 10.13. Извлечение строк из таблицы cats
<?php
require_once 'login.php';
$db_server = mysql_connect(Sdb_hostname, $db_username. $db_password);
if ( !$db_server) die("Невозможно подключиться к MySQL: " . mysql_error());
mysql_select_db($db_database)
or die("Невозможно выбрать базу данных: '' . mysql_error()):
$query = "SELECT * FROM cats":
$result = mysql_query(Squery);
if (!$result) die ("Сбой при доступе к базе данных: " . mysql_error()):
Srows = mysql_num_ rows(Sresult):
echo "<table><tr> <th>Id</th> <th>Family</th>
<th>Name</th><th>Age</th></tr>";
for (Sj = 0 ; Sj < Srows : ++$j) {
$row = mysql_fetch_row($result);
echo "<tr>”;
for ($k = 0 ; Sk < 4 ; ++$k) echo "<td>$row[$k]</td>"; echo "</tr>":
}
echo "</table>";
?>
Этот код выдает простой MySQL-запрос SELECT * FROMcats, а затем отображает все возвращенные строки. Выходная информация должна иметь следующий вид:
Id Family Name Age
1 Lion Leo 4
2 Cougar Growler 2
3 Cheetah Charly 3
Здесь можно убедиться в том, что столбец i d получает правильное автоприращение.
Обновление данных
Изменение внесенных в таблицу данных также выполняется очень просто. Вы заметили, что кличка гепарда (cheetah) записана как Charly? Исправим ее на Chari iе, как показано в примере 10.14.
Пример 10.14. Переименование гепарда Charly в Charlie
<?php
requi re_once '1ogi n.php':
$db_server = mysql_connect($db_hostname. $db_usernaine. $db_password);
i f ( !$db_server) die("HeB03M0WH0 подключиться к MySQL: " . mysql_error()); mysql_selectt db($db_database)
ог сИеС'Невозможно выбрать базу данных: " . mysql_error()):
Squery = "UPDATE cats SET name='Charlie' WHERE name='Chariy'
Sresult = mysql_query($query);
if ( !Sresult) die ("Сбой при доступе к базе данных: " . mysql_error()); ?>
Если теперь еще раз запустить код из примера 10.13, то будет выведена следующая информация:
Id Family Name Age
1 Lion Leo 4
2 Cougar Growler 2
3 Cheetah Charlie 3
Удаление данных
Пума по имени Growler была перевезена в другой зоопарк, поэтому сведения 0 ней нужно удалить из базы данных. Удаление данных из таблицы показано в примере 10.15.
Пример 10.15. Удаление сведений о пуме Growler из таблицы cats
<?php
require_once 'login.php’ :
Sdb_server = mysql_connect($db_hostname. $db_username, Sdb_password):
if ( !$db_server) die("Невозможно подключиться к MySQL: " . mysql_error());
mysql_select_db($db_database)
or die("Heвoзмoжнo выбрать базу данных: " . mysql_error()):
Squery = "DELETE FROM cats WHERE name='Gгrowler;
Sresult = mysql_query($query):
if ( !Sresult) die ("Сбой при доступе к базе данных: " . mysql_error()): ?>
Здесь используется стандартный запрос DELETE FROM, и когда будет запущен код из примера 10.13, в отображаемой информации вы сможете увидеть, что строка была удалена:
Id Family Name Age
1 Lion Leo 4
3 Cheetah Charlie 3
Использование свойства AUTO_INCREMENT
При использовании свойства AUT0 INCREMENT вы не можете знать, какое значение было дано графе строки, которая предшествовала вставляемой строке. Но если нужно узнать об этом, можно позже обратиться к MySQL с помощью функции mysql insertjd. Потребность в этом возникает довольно часто, например при обработке покупки можно вставить в таблицу Customers нового покупателя, а затем
сослаться на только что созданный Cust Id при вставке покупки в таблицу покупок. Пример 10.12 может быть переписан и превращен в пример 10.16, отображающий это значение после каждой вставки.
Пример 10.16. Добавление данных к таблице cats и отчет о вставленном id
<?php
requi re_once ’1ogin.php';
$db_server = mysql_connect($db_hostname, Sdb_username, $db_password);
if ( !$db_server) die("Невозможно подключиться к MySQL: " . mysql_error()); mysql_select_ab(Sab_database)
or die("Невозможно выбрать базу данных: " . mysql_error()):
Squery = "INSERT INTO cats VALUESCNULL, ’Lynx', 'Stumpy'. 5)";
Sresult = mysql_query($query):
echo "Значение вставленного ID равно " . mysql_insert_id();
if ( !Sresult) die С"Сбой при доступе к базе данных: " . mysql_error());
?>
Теперь содержимое таблицы приобретет следующий вид (обратите внимание на то, что ранее использовавшееся значение i d, равное 2, повторно не используется, поскольку в некоторых случаях это может вызвать сложности):
Id Family Name Age
1 Lion Leo &
3 Cheetah Charlie 3
4 Lynx Stumpy 5
Использование идентификаторов вставленных строк
Зачастую данные вставляются сразу в несколько таблиц: за книгой следует ее автор, за покупателем — его покупка и т. д. При вставке данных в графы с автоприращением нужно будет запомнить вставленный ID, возвращенный для его сохранения в связанной таблице.
Предположим, к примеру, что в целях привлечения дополнительных средств над представителями кошачьих могут брать шефство какие-нибудь организации, и когда животное сохраняется в таблице кошек, нам нужно создать ключ для привязки его к организации, взявшей над ним шефство. Код для этого похож на код примера 10.16, за исключением того, что возвращенный ID, который был вставлен в таблицу, сохраняется в переменной SinsertID, а затем используется в качестве составной части следующего запроса:
Squery = "INSERT INTO cats VALUES(NULL,'Lynx',к' ’Stumpy',5)":
Sresult = mysql_query(Squery);
SinsertID = mysql_insert_id():
Squery = "INSERT INTO owners VALUES(SinsertID, 'Ann',' Smith')":)";
Sresult = mysql_query(Squery);
Теперь животное связано со своим шефом посредством уникального кошачьего идентификатора, который был автоматически создан благодаря свойству AUT0_ INCREMENT.
Использование блокировок
Абсолютно безопасная процедура связывания таблиц посредством уже вставленного ID должна использовать блокировки. Если сразу несколько пользователей станут отправлять данные в одну и ту же таблицу, эти блокировки могут немного увеличить время отклика, но такие издержки вполне оправданны. При использовании блокировки наблюдается такая последовательность действий.
Блокировка первой таблицы (например, cats).
Вставка данных в первую таблицу
Извлечение уникального ID из первой таблицы с помощью функции mysql_ insert_id.
Снятие блокировки с первой таблицы.
Вставка данных во вторую таблицу
Перед вставкой данных во вторую таблицу блокировку можно спокойно снять, поскольку вставляемый ID был извлечен и сохранен в переменной программы. Вместо блокировки можно, как показано в главе 9, использовать транзакцию, но это еще больше замедлит работу MySQL-сервера.
Выполнение дополнительных запросов
Итак, хватит развлекаться с кошками. Чтобы исследовать некоторые более сложные запросы, нужно вернуться к использованию таблиц customers и cl assies, которые вы должны были создать в процессе работы с главой 8. В таблице customers должны быть сведения о двух покупателях, а в таблице classics — сведения о нескольких книгах. В них также совместно используется столбец с номером ISBN, названный isbn, который можно применять для выполнения дополнительных запросов.
Например, для отображения каждого из покупателей вместе с названиями и авторами купленных ими книг можно воспользоваться кодом, показанным на рис. 10.17.
Пример 10.17. Выполнение вторичного запроса
<?php
require_once 'login.php';
$db_server = mysql_connect($db_hostname. Sdbjjsername. $db_password);
if ( !$db_server) die("Невозможно подключиться к MySQL: " . mysql_error());
mysql_select_db($db_database)
or die("Невозможно выбрать базу данных: " . mysql_error());
Squery = "SELECT * FROM customers":
$result = mysql_query($query):
if ( !Sresult) die ("Сбой при доступе к базе данных: " . mysql_error()):
Srows = mysql_num_ rows(Sresult);
for (Sj = 0 : Sj < Srows : ++$j) {
$row = mysql_fetch_row($result);
echo ”$row[0] purchased ISBN $row[l]:<br />";
$subquery = "SELECT * FROM classics WHERE isbn='$row[l]'";
$subresult = mysql_query($subquery);
if ( !Ssubresult) die ("Сбой при доступе к базе данных: " . mysql_error());
$subrow = mysql_fetch_row(Ssubresult) :
echo " ’Ssubrow[l]' by $subrow[0]<br />":
В этой программе используется первичный запрос к таблице customers, чтобы найти всех покупателей. Затем на основе номера ISBN книг, приобретенных каждым покупателем, делается новый запрос к таблице cl assi cs, чтобы найти название и автора каждой из книг. При выполнении этого кода будет выведена следующая информация:
Mary Smith purchased ISBN 9780582506206:
'Pride and Prefer ее' :y Jsre Austeri
Jack Wilson purchases IS5S 9~S251~1232CL:
'The Origin c~ 5:e:~es' I^5r'es Darwin
Хотя это и не имеет отношения к иллюстрации использования дополнительных запросов, ^' 4 . но в данном случае можно вернуть точно такую же информацию, используя запрос с видом _объединения NATURAL JOIN (см. главу 8):
SELECT name.'s:^ е. с .г.'*:'* FROM1 customersS
NATURAL JOIN :'5ss‘:s:
Предотвращение внедрения SQL-кода
Наверное, опасность передачи MySQL не прошедшей проверку вводимой пользователем информации все же недооценивается. Представьте, к примеру, что для идентификации пользователя используется следующий простой фрагмент кода:
Suser = $_P0ST['user']:
Spass = S_P0ST[’pass’]:
Squery = "SELECT * FROM users WHERE user='Suser' AND pass='Spass'":
На первый взгляд этот код может показаться вполне приемлемым. Если пользователь вводит значения fredsmith и mypass, которые присваиваются переменным Suser и Spass, то строка запроса, которая передается MySQL, приобретает следующий вид:
SELECT * FROMusers WHERE user='fredsmith' AND pass=‘mypass'
Все это выглядит вполне пристойно, но что будет, если кто-нибудь введет для переменной Suser следующую строку (а для переменной Spass вообще ничего не станет вводить):
admin' #
Посмотрим на строку, которая будет отправлена MySQL:
SELECT * FROM users WHERE user= *admin' #' AND pass="
Вы поняли, в чем тут проблема (выделено полужирным шрифтом)? В MySQL с символа # начинается комментарий. Поэтому пользователь войдет в систему под именем admin (предположим, что в системе есть пользователь admin), и ему не нужно будет вводить пароль. В следующей строке исполняемая часть запроса выделена полужирным шрифтом, а все остальные символы будут проигнорированы.
SELECT * FROM users WHERE user=,admin’ #' AND pass="
Но вам еще очень посчастливится, если весь вред, нанесенный злоумышленником, этим и ограничится. По крайней мере, вам, может быть, удастся войти в свое приложение и отменить все изменения, внесенные туда пользователем, вошедшим в систему под именем admi п. А что, если ваша прикладная программа удаляет пользователя из базы данных? Тогда код может иметь следующий вид:
$user = $_P0ST['user'];
Spass = $_P0ST[’pass’ ]:
Squery = "DELETE FROM users WHERE user=’$user' AND pass='Spass'":
На первый взгляд, с ним опять вроде бы все в порядке, но что получится, если кто-нибудь введет для переменной Suser следующее значение:
anything' OR 1=1 #
MySQL интерпретирует это следующим образом (также выделено полужирным шрифтом):
DELETE FROM users WHERE user='anything' OR 1=1 #' AND pass="
Условие этого SQL-запроса всегда будет истинным, поэтому вы лишитесь всей базы данных, в которой содержатся сведения о пользователях. Так что же нужно делать в случае подобной атаки?
Не стоит полагаться на встроенное в РНР свойство «волшебных кавычек» (magic quotes), которое автоматически отключает любые символы одинарных и двойных кавычек путем установки перед ними символа обратного слеша (\). Почему? Да потому, что это свойство может быть отключено. Многие программисты именно так и делают, чтобы вставить собственный код, обеспечивающий безопасность. Поэтому нет никаких гарантий того, что на вашем рабочем сервере это свойство не было отключено. Фактически в РНР 5.3.0 его использование не приветствовалось, а из версии 6 оно вообще было удалено.
Вместо этого для всех обращений к MySQL нужно всегда использовать функцию mysql_real_escape_string. В примере 10.18 показана функция, которую можно использовать, чтобы удалить любые «волшебные кавычки», добавленные во введенную пользователем строку, а затем соответствующим образом обезвредить все имеющиеся в ней опасные компоненты.
Пример 10.18. Способ обезвреживания данных, введенных пользователем, приемлемый для MySQL
<?php
function mysql_fix_string($string)
{
if (get_magic_quotes_gpc()) Sstring = stripslashes(Sstring):
return mysql_real_escape_string(Sstring);
} ?>
Функция get_magic_quotes_gpc возвращает TRUE, если свойство «волшебных кавычек» находится в активном состоянии. Если это так, любые добавленные к строке слеши подлежат удалению, в противном случае функция mysql real eascape stri ng может отключить некоторые символы дважды, сделав строки непригодными для дальнейшего использования. В примере 10.19 показано, как можно вставить функцию mysql fi х в ваш код.
Пример 10.19. Способ безопасного доступа к MySQL при использовании данных, введенных пользователем
<?php
Suser = mysql_fi x_stri ng($_P0ST[’user’]);
Spass = mysql_fix_string($_POST['pass’]);
Squery = "SELECT * FROMusers WHERE user='Suser' AND pass='Spass'";
function mysql_fix_string(Sstring) {
if (get_magic_quotes_gpc()) Sstring = stripslashes(Sstring);
return mysql_real_escape_string($string);
}
?>
Следует запомнить, что функцию mysql_escape_string можно использовать только при активной базе данных MySQL, в противном случае произойдет ошибка.
Использование указателей места заполнения
Другой, фактически «пуленепробиваемый» способ предохранения от внедрения SQL-кода заключается в использовании так называемых указатIелей места заполнения. Его смысл — в предварительном определении запроса с использованием символов вопросительного знака (?) в тех местах, где появятся данные. Затем вместо непосредственного вызова MySQL-запроса осуществляется вызов предварительно определенного запроса, которому передаются данные. В результате этого гарантируется, что каждый элемент введенных данных попадает непосредственно в базу данных и не сможет интерпретироваться в качестве SQL-запроса. Иными словами, внедрение SQL-кода становится невозможным.
При использовании командной строки MySQL последовательность выполняемых запросов будет иметь вид, показанный в примере 10.20.
Пример 10.20. Использование указателей места заполнения
PREPARE statement FROM "INSERT INTO classics VALUESC?.?.?.?.?:4:
SET @author= "Emily Bronta".
©title= "Wutheringз Heights".
©category= "Classic Fiction".
@year= "1847",
@isbn= "9780553212587":
EXECUTE statement USING ©author,©title.©category.©year.='5:^:
DEALLOCATE PREPARE statement:
Первая команда подготавливает инструкцию st~te^ert хтя вставки данных в таблицу classics. Здесь показано, что вместо значений или переменных хтя вставляемых в таблицу данных эта инструкция содержит последовательность символов ?. Это и есть указатели места заполнения.
В следующих пяти строках переменным MvSQL присваиваются значения, соответствующие вставляемым данным. Затем при выполнении предварительно определенной инструкции эти переменные передаются ей в качестве параметров. II наконец, инструкция удаляется, чтобы освободить задействованные ею ресурсы.
В РНР код для этой процедуры имеет вид, показанный в примере 10.21 (здесь предполагается, что вы уже создали файл login.php с нужными хтя доступа к базе данных сведениями).
Пример 10.21. Использование указателей места заполнения с РНР
<?php
require 'login.php’:
$db_server = mysql_connect($db_hostname. $db_username. $db_passwor':: : if ( !$db_server) die("Невозможно подключиться к MySQL: " . m'Sy|sq" mysql_select_db($db_database)
or die("Невозможно выбрать базу данных: " . mysql_error()):
$query = 'PREPARE statement FROM "INSERT INTO classics
VALUESC?,?.?.?.?)"':
mysql_query(Squery);
Squery = 'SET ©author = "Emily Bronta".'
'©title = "Wuthering Heights",' .
'©category = "Classic Fiction",'
'©year = "1847",' .
'©isbn = "9780553212587"';
mysql_query(Squery);
Squery = 'EXECUTE statement USING ©author,©title.©category.©ye5r\?'s:^ - mysql_query(Squery);
Squery = ’DEALLOCATE PREPARE statement1:
mysql_query(Squery):
?>
После подготовки инструкции ее можно использовать необходимое количество раз до тех пор, пока она не будет удалена. Такие инструкции часто применяются внутри циклов для быстрой вставки данных в базы путем присваивания значений переменным MySQL с последующим выполнением инструкции. Данный подход более эффективен, чем создание заново всей инструкции при каждом проходе цикла.
Предотвращение внедрения HTML-кода
Нужно позаботиться о защите еще от одного вида внедрения, который связан не с безопасностью ваших собственных веб-сайтов, а с конфиденциальностью и защитой пользовательских данных. Речь идет о межсайтовом скриптинге (Cross Site Scripting), называемом также XSS.
Эта разновидность внедрения происходит в том случае, когда вы разрешаете пользователю вводить, а затем отображать на вашем веб-сайте HTML- или, что случается чаще, JavaScript-код. Одним из мест, где это часто происходит, является форма для комментариев. Чаще всего злоумышленник пытается написать код, ворующий у пользователей вашего сайта cookie, позволяющие ему узнать пары «имя пользователя —пароль» или другую информацию. Хуже того, злоумышленник может предпринять атаку с целью загрузки на пользовательский компьютер троянского коня.
Чтобы предотвратить это внедрение, нужно лишь вызвать функцию html entities, выявляющую все коды разметки HTML и заменяющую их формой, которая отображает символы, но не позволяет браузеру действовать в соответствии с их предназначением. Рассмотрим, к примеру, следующий код HTML:
<script src='http://x.com/hack.js> </script><script>hack();</script>
Этот код загружает программу на JavaScript, а затем выполняет вредоносные функции. Но если сначала этот код будет пропущен через функцию html entities, то он превратится в следующую абсолютно безвредную строку:
&11 :script src='http ://x.com/hack.j s '>:
<:/script><script>hack():&1t:/script>:
Поэтому если вы когда-нибудь соберетесь отобразить какие-нибудь данные, введенные пользователем, то нужно немедленно или сразу же после первого сохранения в базе данных обезвредить их с помощью функции html entities. Для этого я рекомендую вам создать новую функцию наподобие первой функции, показанной в примере 10.22, которая способна обезвредить попытки как SQL- так и XSS-внедрения.
Пример 10.22. Функции для предотвращения атак внедрения SQL и XSS
<?php
function mysql_entities_fix_string(Sstring)
{
return htmlentities(mysql_fix _string(Sstring));
}
function mysql_fix_string(Sstring)
{
if (get_magic_quotes_gpc()) Sstring = stripslashes(Sstring):
return mysql_real_escape_string($string);
}
?>
Функция mysql enti ti es_fi x_stri ng сначала вызывает функцию mysc1^ 4x_stri ng а затем, прежде чем вернуть полностью обезвреженную строку, пропускает результат через функцию html entities. В примере 10.23 показана новая, «максимально защищенная» версия примера 10.19.
Пример 10.23. Способ безопасного доступа к MySQL и предотвращения XSS-атак
<?php
$user = mysql_enti ti es_fi x_stri ng( $_P0ST[' user' ]);
Spass = mysql_entities_fix_string($_POST['pass']):
Squery = "SELECT * FROMusers WHERE user='Suser' AND pass='Spas s ;
function mysql_entities_fix_string(Sstring) {
return htmlentities(mysql_fix_string(Sstring));
}
function mysql_fix _string(Sstring)
{
if (get_magic_quotes_gpc()) $string = stripslashes(Sstring);
return mysql_real_escape_string($sthng);
}
?>
После изучения способов объединения РНР с MySQL и избавления от опасности, которой грозят введенные пользователем данные, в следующей главе будет подробно рассмотрена обработка данных формы, включая вопросы проверки приемлемости данных, выбора нескольких элементов, сопоставления с образцом и обеспечения безопасности.
Проверьте ваши знания
Какая стандартная PHP-функция предназначена для подключения к базе данных MySQL?
В каком случае работу функции mysql resul t нельзя признать оптимальной?
Назовите причину, по которой в большинстве случаев для отправки данных формы лучше воспользоваться методом POST, а не методом GET.
Как определить самое последнее значение, введенное в столбец со свойством AUT0 INCREMENT?
Какая PHP-функция отключает опасные символы в строке, делая ее пригодной к использованию в MySQL?
Какая функция может использоваться для предотвращения XSS-атак внедрения?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 10».
Jj 4 Обработка форм
Основным способом взаимодействия пользователей с РНР и MySQL является применение HTML-форм. Эти формы появились на заре разработки Всемирной паутины, в 1993 году, даже раньше, чем электронная коммерция, и благодаря простоте и легкости использования не утратили своего значения и по сей день.
Разумеется, с годами HTML-формы совершенствовались, получая дополнительные функциональные возможности обработки информации, поэтому данная глава познакомит вас с современными методами обработки формы и продемонстрирует самые лучшие способы реализации форм для достижения наибольшей степени удобства и безопасности.
Создание форм
Обработка форм является многоступенчатым процессом. Сначала создается форма, в которую пользователь может вводить необходимые данные. Затем эти данные отправляются веб-серверу, где происходит их разбор, зачастую совмещаемой с проверкой на отсутствие ошибок. Если код РНР найдет одно или несколько полей, требующих повторного ввода, форма может быть заново отображена вместе с сообщением об ошибке. Когда качество введенных данных удовлетворяет программу, она предпринимает некоторые действия, нередко привлекая для этого базы данных, к примеру, для ввода сведений о покупке.
Для создания формы потребуются как минимум следующие элементы:
О открывающий и закрывающий теги — соответственно <form> и </form>;
О тип передачи данных, задаваемый одним из двух методов GET или POST;
О одно или несколько полей для ввода данных;
О URL-адрес назначения, по которому будут отправлены данные формы.
В примере 11.1 показана очень простая форма, созданная с использованием кода РНР. Наберите этот код и сохраните его в файле formtest. php.
Пример 11.1. formtest.php - простой обработчик формы на РНР
<?php // formtest.php
echo <« _ END
<html>
<head>
<title>Form Test</title>
</head>
<body>
<form method="post" action="formtest.php" />
Как Вас зовут?
<input type="text" name="name" />
<input type^'submit" />
</form>
</body>
</html>
_END; ?>
В первую очередь следует отметить, что в этом примере использован прием, уже встречавшийся в данной книге: вместо многократного.входа в PHP-код и выхода из него для вывода многострочного HTML-кода я обычно применяю конструкцию echo <«_END.. ._END.
Внутри этого многострочного вывода находится стандартный код, с которого начинается HTML-документ, отображающий заголовок документа и обозначающий начало его тела. Затем следует форма, настроенная на отправку своих данных с использованием POST-метода в адрес PHP-программы formtest. php, то есть этой самой программы.
Остальной код программы закрывает все открытые элементы: форму, тело HTML-документа и PHP-инструкцию echo <«_END. Результат запуска этой программы в веб-браузере показан на рис. 11.1.
‘ Form Test - Mozilla FirefoxИй®
Файл Правка §ид Журнал Закладки Инструменты ^правка
— С X й j* & (ЕЕ http:/flocaJhost/web/formtest. php й j Ц Form Testт з / '-
Как Вас зовут? |11 Отправить запрос"]
Готово
Рис. 11.1. Результат запуска программыI formtest.php в веб-браузере
Извлечение отправленных данных
В примере 11.1 представлена только одна из частей многоступенчатого процесса обработки формы. Если ввести имя и щелкнуть на кнопке Отправить запрос, то абсолютно ничего, кроме повторного отображения формы, не произойдет. Поэтому сейчас нужно добавить PHP-код, обрабатывающий отправляемые формой данные.
В примере 11.2 показана расширенная версия предыдущей программы, включающая обработку данных. Наберите этот код или измените код программы formtest. php, добавив в него новые строки, сохраните программу в файле formtest2. php и попробуйте ее запустить. Результат запуска программы и введенное имя показаны на рис. 11.2.
Пример 11.2. Обновленная версия formtest.php
<?php // formtest2.php
if (isset($_POST[,name'])) $name = $_P0ST['name'];
else Sname = "(He введено)";
echo <«_END
<html>
<head>
<title>Form Test</title>
</head>
<body>
Вас зовут: $name<br />
<form method="post" action="formtest2.php">
Как Вас зовут?
<input type="text" name="name" />
<input type="submit" />
</form>
</body>
</html>
_END;
?>
Form Test - Mozilla FirefoxylUx]
Файл Главка Вид Журнал ^акладки Инструменты ^правка
с X(j^ttp^hos^
j QForm Test'f
Вас зовут: Иван Петров
Как Вас зовут? |] [ Отправить запрос |
Готово
Рис. 11.2. Программа formtest.php с обработкой данных
Изменения касаются двух строк в начале программы, в которых проверяется, содержит ли ассоциативный массив $_P0ST отправленное поле name. Ассоциативный массив S POST был рассмотрен в предыдущей главе, он содержит элемент для каждого поля HTML-формы. В примере 11.2 для вводимого имени использовалось поле name, а для отправки данных формы был избран метод POST, поэтому значение элемента name массива $_P0ST содержится в элементе массива $_P0ST[' name ’ ].
PHP-функция isset используется для проверки наличия значения у элемента $_P0ST[' name' ]. Если значение не было отправлено, программа присваивает переменной $name значение «(Не введено)». А если значение было отправлено, то оно сохраняется в этой переменной. После тега <body> была введена еще одна строка, предназначенная для отображения значения, сохраненного в переменной $name.
register_globals: склонность к использованию устаревших решений
Еще до того как вопросы безопасности приобрели столь большое значение, РНР по умолчанию присваивал значения массивов S P0ST и $_GET непосредственно РНР- переменным. К примеру, в использовании инструкции $name=$_P0ST[ 'name' ]; не было необходимости, поскольку РНР автоматически присваивал это значение переменной Sname, как только запускалась программа.
Первоначально (до появления версии РНР 4.2.0) это считалось весьма продуктивной идеей, избавляющей от набора большого объема дополнительного кода, но в данный момент такая практика уже не приветствуется, и по умолчанию это свойство отключено. Если обнаружится, что на рабочем веб-сервере, для которого выполняется разработка, свойство register globals включено, нужно срочно потребовать от администратора сервера его отключения.
Зачем отключать свойство register globals? Оно дает возможность кому угодно ввести GET-запрос в конце URL-адреса, например: http: //myserver. com?overri de=l, и если в вашем коде где-нибудь используется переменная Soverri de и вы забыли ее инициализировать (воспользовавшись, к примеру, инструкцией $override=0:), то это действие может поставить работу программы под угрозу.
Фактически из-за того, что многие установки во Всемирной сети сохраняют эту лазейку, я советую вам всегда инициализировать любую, используемую вами переменную на тот случай, что ваш код будет когда-нибудь запущен на подобной системе. В программировании инициализация является правилом хорошего тона, поскольку каждую инициализацию можно прокомментировать, чтобы оставить напоминание самому себе и другим программистам о назначении этой переменной.
Если когда-нибудь придется обслуживать код, в котором, на ваш взгляд, некоторым переменным присвоены значения, не имеющие явного смысла, вы можете вполне обоснованно предположить, что программист создавал этот код, используя свойство register_globaIs, и эти значения предполагается извлечь из отправленных POST- или GET-запросов. Если так оно и есть, я рекомендую вам переписать код так, чтобы значения для этих переменных загружались в явном виде из соответствующего массива $_POST или $_GET.
Значения по умолчанию
Иногда представляется удобным предложить посетителям вашего сайта принять в веб-форме значения по умолчанию. Предположим, к примеру, что вы разместили на сайте по недвижимости виджет, представляющий собой калькулятор погашения кредита. При этом есть смысл ввести в него значения по умолчанию,
скажем, 25 лет и 6 % годовых, чтобы пользователю осталось только ввести либо основную сумму заимствования, либо посильную для него сумму ежемесячных выплат. HTML-код для этих двух значений мог бы иметь вид, показанный в примере 11.3.
Пример 11.3. Установка значений по умолчанию
<form method="post" action="calc.php"><pre>
Сумма заимствования <input type="text" name="principlen />
Ежемесячная выплата <input type="text" name="monthly" />
Количество лет <input type=,,text" name="years" value="25” />
Процент годовых <input type="text" name="rate" value="6" />
<input type="submit" />
</pre></form>
Если есть желание испробовать в работе этот HTML-код (а также другие подобные примеры), наберите и сохраните его в файле с расширением HTML, например в файле test.html, а затем загрузите этот файл в свой браузер.
Обратите внимание на третий и четвертый элементы ввода данных. За счет указания значения для параметра val ue в поле отображается значение по умолчанию, которое пользователи в дальнейшем смогут изменить, если у них появится такое желание. Задавая вполне обоснованные значения по умолчанию, можно добиться более дружелюбного поведения от своих веб-форм за счет минимизации необязательного ввода данных. Результат работы предыдущего кода показан на рис. 11.3. Разумеется, он был создан только для иллюстрации значений по умолчанию, и поскольку программа calc.php не была написана, форма после передачи данных никак на это не отреагирует.
}Form Test Mozilla Firefox
Файл Правка Вид Журнал Закладки Инструменты Справка
ДШ
Г е X - ~~
Сумма заимствованияЕжемесячная выплатаКоличество летПроцент годовыхСумма заимствованияЕжемесячная выплатаКоличество летПроцент годовых
Сумма заимствования
Ежемесячная выплата
Количество лет
Процент годовых
Сумма заимствования
Ежемесячная выплата
Количество лет
Процент годовых
Готово
Рис. 11.3. Использование значений по умолчанию для избранных полей формы
Значения по умолчанию используются также для скрытых полей, которые применяются тогда, когда вы хотите наряду с данными, введенными пользователем, отправить из веб-страницы в адрес программы какую-нибудь дополнительную информацию. Скрытые поля будут рассмотрены в этой главе чуть позже.
Типы элементов ввода данных
HTML-формы обладают завидной универсальностью, позволяя отправлять данные из довольно широкого диапазона различных типов элементов ввода, начиная с текстовых полей и текстовых областей и заканчивая флажками, переключателями и т. п.
Текстовое поле
Наверное, самым распространенным типом элемента, применяемого для ввода данных, является текстовое поле. Оно воспринимает широкий диапазон буквенно-цифрового текста и других символов в пределах однострочного окна. Типовой формат текстового поля для ввода информации имеет следующий вид:
<input type="text” namе="имя" size= "размер” maxiength="длина" value="значение" />
Параметры name (имя) и val ue (значение) мы уже рассматривали, но здесь представлены еще два параметра: size (размер) и maxlength (максимальная длина). Параметр size определяет ширину поля в символах текущего шрифта, каким оно появится на экране, a maxlength определяет максимальное количество символов, которое пользователю разрешено вводить в это поле.
Единственными обязательными параметрами являются type (тип), сообщающий веб-браузеру ожидаемый тип элемента ввода данных, и name (имя), дающий вводимым данным имя, которое используется в дальнейшем для обработки поля после получения отправленной формы.
Текстовая область
Когда нужно принять вводимые данные, превышающие по объему короткую строку текста, используется текстовая область. Она похожа на текстовое поле, но поскольку в нее разрешает вводить сразу несколько строк, имеет несколько иные параметры. Ее типовой формат выглядит следующим образом:
<textarea пате="имя" cols-“ширина" rows="высота" жгар="тип">
</textarea>
Первое, на что следует обратить внимание, — использование текстовой областью собственного тега <textarea>, который не является подвидом тега <input>, поэтому для него нужен закрывающий тег </textarea>, чтобы закрыть элемент ввода данных.
Если есть какой-нибудь текст, который нужно отобразить по умолчанию, то вместо использования параметра, позволяющего задавать подобное значение, нужно поместить этот текст перед закрывающим тегом </textarea>, и тогда он будет отображен и сможет редактироваться пользователем:
<textarea name="имя" cols="ширина" rows="высота" жар="тип">
Это текст, отображаемый по умолчанию.
</textarea>
Для управления шириной и высотой текстовой области используются параметры col s (графы) и rows (строки). Для задания размеров области в обоих параметрах в качестве единицы измерения применяются пространства, занимаемые символом текущего шрифта. Если эти значения опустить, будет создана текстовая область
с размерами по умолчанию, которые зависят от используемого браузера, поэтому, чтобы точно знать, в каком виде должна появиться ваша веб-форма, нужно всегда задавать значения этих параметров.
И наконец, при помощи параметра wrap (перенос) можно управлять порядком переноса вводимого в область текста (и тем, как этот перенос будет отправляться на сервер). В табл. 11.1 показаны доступные типы переноса. Если не указывать значение параметра wrap, будет задействован мягкий перенос (soft wrapping).
Таблица 11.1. Типы переноса, доступныIе в области ввода <textarea>
Тип
Действие
off
Текст не переносится, и строки появляются в строгом соответствии с тем, как их вводит пользователь
soft
Текст переносится, но отправляется на сервер одной длинной строкой без символов возврата каретки и перевода строки
hard
Текст переносится и отправляется на сервер в формате переноса с «мягким» возвратом в начало следующей строки и переводом строки
Флажки
Если пользователю нужно предложить выбор из нескольких вариантов данных, при котором он может остановиться на одном или нескольких вариантах, то для этого всегда используются флажки. Формат флажков выглядит следующим образом:
<input type="checkbox" namе="имя" valuе="значение" checked=,,checkedn />
Если в этот формат включается параметр checked (установлен), флажок появляется в браузере в уже установленном виде (строка, присваиваемая этому параметру, не имеет никакого значения, он просто должен присутствовать в формате). Если данный параметр не включать в формат, флажок будет отображен в неустановленном виде. Примером задания такого флажка может послужить следующий код:
Я согласен <input type="checkbox" name-'agree" />
Если пользователь не установит флажок, значение передано не будет. Но если флажок будет установлен, то для поля по имени agree будет передано значение on. Если вы предпочитаете вместо on отправить собственное значение (например, число 1), можно воспользоваться следующим синтаксисом:
Я согласен <input type="checkbox" name="agree" value="l" />
В то же время, если вы хотите при отправке формы предложить своим читателям информационный бюллетень, то может появиться желание отобразить флажок установленным по умолчанию:
Подписаться? <input type="checkbox" name="news" checked="checked" />
Если есть потребность в одновременном выборе группы элементов, то всем им нужно присвоить одинаковые имена. Но при этом нужно иметь в виду, что если в качестве имени не будет передано имя массива, будет отправлен только последний
отмеченный элемент формы. Код из примера 11.4 позволяет пользователю выбрать любимые сорта мороженого (результат работы этого кода в браузере показан на рис. 11.4).
Пример 11.4. Предложение сделать выбор, установив сразу несколько флажков
Ванильное <input type="checkb.ox” name="ice" value="Vanilla" /> Шоколадное <input type="checkbox" name="ice" value="Chocolate" /> Земляничное <input type="checkbox" name="ice" value="Strawberry" />
" ' Mozilld FirefoxU p £ i' X j
Файл Правка Вид Дурная Закладки Инструменты Справка
^ С Ж( D http://locafhost/web/formtest.php*1
_ _-
Ванильное D Шоколадное 0 Земляничное 0
Готово
Рис. 11.4. Использование флажков для быстрого выбора
Если установлен только один флажок, например второй, то будет передан только этот элемент (полю с именем i се будет присвоено значение Шоколадное). Но если будут выбраны два и более флажка, будет отправлено только последнее значение, а все предыдущие будут проигнорированы.
Если нужно добиться исключающего поведения, то есть передачи только одного элемента, следует воспользоваться переключателями (см. следующий подраздел), но чтобы позволить отправку сразу нескольких значений, нужно немного изменить код HTML, как показано в примере 11.5 (обратите внимание на добавление квадратных скобок [] после значений ice):
Пример 11.5. Отправка нескольких значений с помощью массива
Ванильное <input type="checkbox"name="ice[]n value="Vanilla" />
Шоколадное <input type="checkbox"name="ice[]" value="Chocolate" /> Земляничное <input type=”checkbox"name=Mice[]" value="Strawberry" />
Теперь, если при отправке формы установлены какие-нибудь из этих флажков, будет отправлен массив по имени i се, содержащий любые значения. В любом случае можно извлечь в переменную либо отдельное значение, либо массив значений:
Sice = $_P0ST['i се']:
Если поле ice было отправлено в виде отдельного значения, переменная Sice будет содержать отдельную строку, например Земляничное. Но если в форме под именем i се был определен массив (как в примере 11.5), переменная Si се будет массивом и номера элементов этого массива будут номерами отправленных значений.
В табл. 11.2 показаны семь возможных наборов значений, которые могут быть переданы этим HTML-кодом для одного, двух или трех установленных флажков. В каждом из случаев будет создаваться массив из одного, двух или трех элементов.
Таблица 11.2. Семь возможных наборов значений для массива $ice
При отправке одного значения
При отправке двух значений
При отправке трех значений
$ice[0] => Ванильное
$ice[0] => Ванильное
$ice[0] => Ванильное
$ice[0] => Шоколадное
$ice[l] => Шоколадное
$ice[l] => Шоколадное
$ice[0] => Земляничное
$ice[2] => Земляничное
$ice[0] => Ванильное
$ice[l] => Земляничное
$ice[0] => Шоколадное
$ice[l] => Земляничное
Если переменная Sice является массивом, то для отображения ее содержимого можно использовать очень простой РНР-код:
foreach(Sice as Sitem) echo "Sitem<br />":
В нем используется стандартная PHP-конструкция foreach, осуществляющая последовательный перебор элементов массива Sice и передающая значение каждого элемента переменной Sitem, содержимое которой затем отображается с помощью команды echo. Тег <Ьг /> служит только для HTML-форматирования, чтобы после отображения каждого сорта осуществлялся перевод на новую строку По умолчанию поля флажков имеют квадратную форму.
Переключатели
Переключатели (radio buttons — кнопки радиоприемника) названы так по аналогии с утапливаемыми кнопками настройки на фиксированные частоты, которые встречались на многих старых радиоприемниках, где любая ранее утопленная кнопка при нажатии другой кнопки возвращалась в первоначальную позицию. Переключатели используются в тех случаях, когда нужно из двух и более вариантов выбрать и вернуть только один. Все кнопки группы должны использовать одно и то же имя, и поскольку возвращается только одно значение, массив передавать не требуется.
К примеру, если веб-сайт предлагает выбор времени доставки покупок из вашего магазина, то для этого можно воспользоваться HTML-кодом, показанным в примере 11.6 (результат его работы изображен на рис. 11.5).
Пример 11.6. Использование переключателей
8.00-12.00<input type="radio" гname="time" value="l" />| I
12.00-16.00<input type="radio" гname-’time" value=n2" checked=,,checked"' //>|
16.00-20.00<input type="radio" гname="time" value="3" />
Рис. 11.5. Выбор единственного значения с помощью переключателейРис. 11.5. Выбор единственного значения с помощью переключателей
Рис. 11.5. Выбор единственного значения с помощью переключателей
Рис. 11.5. Выбор единственного значения с помощью переключателей
Здесь по умолчанию задается второй вариант: 12.00-16.00. Наличие значения по умолчанию гарантирует, что пользователь выберет хотя бы одно время доставки, которое затем может быть изменено на любое из двух оставшихся в соответствии с их предпочтениями. Если бы не был заранее выбран один из этих вариантов, пользователь мог забыть сделать свой выбор и для времени доставки не было бы передано никакого значения. По умолчанию переключатели имеют форму окружностей.
Скрытые поля
Иногда бывает удобно пользоваться скрытыми полями формы, чтобы получить возможность отслеживать состояние ее ввода. Например, может потребоваться узнать, отправлена форма или нет. Эти сведения можно получить, добавив к РНР- коду фрагмент кода HTML:
echo ' <input type="hidden" name="submitted" value="yes" />'
Это простая РНР-инструкция echo, добавленная к полю ввода HTML-формы. Предположим, что форма была создана вне программы и показана пользователю. При первом получении PHP-программой введенных данных эта строка кода не будет запущена, поэтому поля с именем submi tted не будет. Программа РНР воссоздает форму, добавляя к ней поле ввода.
Поэтому, когда пользователь отправит форму еще раз, PHP-программа получит ее с полем submitted, имеющим значение yes. Существование этого поля можно легко проверить с помощью следующего кода:
i f (isset($_P0ST[’submitted']))
{•••
Скрытые поля могут пригодиться также для хранения других сведений, например идентификационной строки сеанса, которая может быть создана для идентификации пользователя, и т. д.
Скрытые поля нельзя считать безопасными, поскольку они этим свойством не обладают. Код HTML, которым задаются эти поля, может бьггь легко просмотрен с помощью свойства браузера, позволяющего просматривать исходный код страницы.
Список
Тег select позволяет создавать раскрывающийся список, предлагающий выбор одного или нескольких значений. Для его создания используется следующий синтаксис:
<select пате="иА/я" sizе="размер" тиШр1е="тиШр1еп>
Параметром size (размер) задается количество отображаемых строк. Щелчок на кнопке отображения приводит к раскрытию списка, показывающего все варианты. Если применяется параметр multiple (множественный выбор), из списка путем удерживания во время щелчка клавиши Ctrl могут быть выбраны сразу несколько вариантов. Чтобы спросить у пользователя, какой из пяти овощей он любит больше всего, можно воспользоваться кодом, предлагающим единичный выбор (пример 11.7).
Пример 11.7. Использование поля со списком
Овощи <select name="veg" size=" I r>
<option value="Горох">Гopox</option>
<option valие="Фасоль">Фасоль</ор^on>
<option value="M0pK0 Bb">M0pK0 Bb</0ptti0n>
<option value="KanycTa">KanycTa</option>
<option уа1ие="Брокколи">Брокколи</ор^оп>
</select>
Этот HTML-код предлагает пять вариантов, на первый из которых, Горох, выпадает предварительный выбор (благодаря тому что он стоит первым в списке). На рис. 11.6 показан внешний вид раскрытого щелчком списка, после того как был выделен вариант Морковь. Если требуется выбрать другой вариант, предлагаемый по умолчанию (например, Фасоль), нужно воспользоваться параметром selected:
<option selected="selected" уа1ие=,,Фасоль">Фасоль</ор^оп>
X;
Рис. 11.6. Создание раскрывающегося списка с помощью тега select
Можно также дать пользователям возможность выбрать более одного варианта (пример 11.8).
Пример 11.8. Использование select с параметром multiple Овощи <select name="veg” size=n5" multi pie="multipiе"> <option value="Горох">Гopox</option> <option value="Фacoль">Фacoль</option> <option value="MopKOBb">MopKOBb</option>
<option. value="KanycTa">KanycTa</option> <option уа1ие="Брокколи">Брокколи</ор^оп> </select>
Код HTML не претерпел при этом значительных изменений, было лишь изменено значение параметра size на 5 и добавлен параметр multiple. Но теперь, судя по рис. 11.7, можно выбрать более одного варианта, удерживая при щелчке клавишу Ctrl. При желании можете отказаться от параметра size, внешний вид от этого не изменится, но для более длинного списка может понадобиться намного больше места на экране, поэтому я рекомендую, чтобы вы подобрали подходящее количество строк и придерживались своего выбора. Я также советую не делать поля со списком, работающие в режиме множественного выбора, меньше двух строк в высоту, поскольку некоторые браузеры могут при этом некорректно отображать полосы прокрутки, необходимые для доступа к данным.
Рис. 11.7. Использование поля со списком с параметром m ultiple
При определении поля со списком, работающего в режиме множественного выбора, можно также воспользоваться параметром selected для задания в случае необходимости более одного заранее выбранного варианта.
Теги label
За счет использования тегов <1 abel > можно сделать работу пользователя еще удоб нее. В эти теги можно заключить элемент формы, обеспечивая его выбор щелчком на любой видимой части, содержащейся между открывающим и закрывающим тегами <1 abel >.
Возвращаясь к примеру выбора времени доставки, можно позволить пользователю щелкать как на самом переключателе, так и на связанном с ним тексте:
<1 abel>8.00-12.00<input type="radio" name="time" value=,,r ’ /></1 abel>
При этом текст не будет подчеркиваться при прохождении над ним указателя мыши, как это происходит с гиперссылкой, но указатель мыши из текстового курсора будет превращаться в стрелку, показывая, что щелкать можно на всем тексте.
Кнопка отправки
Чтобы согласовать текст на кнопке отправки с разновидностью отправляемой формы, его можно изменить по своему усмотрению, воспользовавшись параметром value:
<input type-'submit" уа1ие="Поиск" />
Можно также заменить стандартный текст на кнопке выбранным вами графическим изображением, используя следующий код HTML:
<input type="image" name="submit" src="image.gif" />
Обезвреживание введенных данных
Вернемся к программированию на РНР. Нелишне будет еще раз напомнить, что обработка данных, введенных пользователем, представляет большую угрозу для безопасности системы, и поэтому очень важно научиться с самого начала работать с этими данными предельно осторожно. На самом деле очистить введенные пользователем данные от потенциальных попыток взлома не так уж сложно, но сделать это совершенно необходимо.
Прежде всего нужно запомнить, что, невзирая на те ограничения, которые были наложены на HTML-форму в отношении типов элементов и размеров вводимых данных, взломщику ничего не стоит воспользоваться свойством браузера, позволяющим просмотреть исходный код страницы, извлечь форму и внести в нее изменения для отправки на ваш веб-сайт вредоносного кода под видом введенных данных.
Поэтому не следует доверять какой-либо переменной, извлеченной из массива $_GET или $_P0ST, до тех пор, пока она не пройдет соответствующую обработку. Если эту обработку не провести, пользователи могут предпринять попытку внедрить в данные кодJavaScript, мешающий работе ваших сайтов, или даже добавить команды MySQL, подвергающие угрозе содержимое вашей базы данных.
Следовательно, нужно не только считывать введенные пользователем данные с помощью следующего кода:
Svariable = $_POST[,user_input'];
но также воспользоваться еще одной или несколькими строками кода. К примеру, чтобы предотвратить внедрение escape-символов в строку, которая будет представлена MySQL, можно применить следующий код (следует напомнить, что эта функция учитывает текущий набор символов, используемый при подключении к MySQL, поэтому она может использоваться только с открытым подключением):
Svariable = mysql_real_escape_string(Svariable):
Чтобы избавиться от нежелательных слеш-символов, например вставленных с помощью уже устаревшей директивы magic quotes gpc, применяется следующий код:
Svariable = stri pslashes(Svari able):
А для удаления из строки любого HTML-кода используется такой код РНР:
Svariable = htmlenti ti es(Svari able):
Например, этот код интерпретируемого HTML <b>hi</b> заменяется строкой &11 : b> hi&11 : /b>:, которая отображается как простой текст и не будет интерпретироваться как теги HTML.
И наконец, если нужно полностью очистить введенные данные от HTML, используется следующий код:
Svariable = stri p_tags(Svari able);
А пока вы не решите, какое именно обезвреживание требуется для вашей программы, рассмотрите показанные в примере 11.9 две функции, в которых собраны вместе все эти ограничения, обеспечивающие довольно высокий уровень безопасности.
Пример 11.9. Функции sanitizeString и sanitizeMySQL
<?php
function sanitizeString(Svar)
{
if (get_magic_quotes_gpc()) Svar = stripslashes(Svar):
Svar = htmlentities(Svar):
Svar = strip_tags($var):
return Svar:
}
function sanitizeMySQL(Svar)
{
Svar = mysql_rea1_escape_string($var);
$var = sanitizeString($var):
return Svar;
}
?>
Добавьте этот код в последние строки своих программ, и тогда вы сможете вызвать его для обезвреживания всех вводимых пользователями данных:
Svariable = saniti zeStri ng(S_P0ST['user_input’D):
или, если имеется открытое подключение к MySQL:
Svariable = sani ti zeMySQK S_P0ST['user_i nput']):
Пример программы
Рассмотрим, как происходит настоящее объединение PHP-программы с HTML- формой, длящего создадим программу convert.php, код которой показан в примере 11.10. Наберите этот код и проверьте его работу.
Пример 11.10. Программа перевода значений между шкалами Фаренгейта и Цельсия
<?php // convert.php
$f = $с =
if (isset($_P0ST['f'])) $f = sanitizeString($_POST['f']);
if (isset($_P0ST[’с ’])) $c = samtizeStnng($_POST['c']);
if ($f != "))
{
$c = intval((5 / 9) * ($f - 32));
$out = "$f °f равно $c °c";
}
elseif(I$c != ''))
{
$f = intval((9 / 5) * $c + 32):
$out = ”$c °c равно $f °f";
}
else $out =
echo <« _ END
<html><head><title>nporpaMMa перевода TeMnepaiypbi</title>
</head><body><pre>
Введите температуру по Фаренгейту или по Цельсию и щелкните на кнопке Перевести
<b>$out</b>
<form method="post" action="convert.php">
По Фаренгейту <input type="text” name="f" size=,,7" />
По цельсию <input type="text" name="c" size="7" />
<input type*"submit" уа1ие="Перевести" />
</form></pre></body></html>
_END;
function sanitizeString(Svar)
{
Svar = stripslashes($var);
$var = htmlentities(Svar);
Svar = strip_tags($var);
return Svar;
} ?>
Когда программа convert. php будет вызвана в браузере, результат будет похож на копию экрана, показанную на рис. 11.8.
Если проанализировать эту программу, то в первой строке инициализируются переменные Sc $f на тот случай, если их значения не были отправлены программе. В следующих двух строках извлекаются значения либо из поля f, либо из поля с. Эти поля предназначены для ввода значений температуры по Фаренгейту или по Цельсию. Если пользователь введет оба значения, то значение по Цельсию будет проигнорировано, а переведено будет значение по Фаренгейту. В качестве меры безопасности в программе также используется новая функция sani ti zeStri ng из примера 11.9.
Рис. 11.8. Работающая программа перевода температурыI
Итак, располагая либо отправленными значениями, либо пустыми строками в обеих переменных $f и $с, следующая часть кода использует структуру if. .. e1lseif.. .else, которая сначала проверяет, имеет ли значение переменная $f. Если эта переменная не имеет значения, проверяется переменная $с. Если переменная $с также не имеет значения, переменной $out присваивается пустая строка (к этому месту мы еще вернемся).
При обнаружении того, что у переменной $f есть значение, переменной $с присватается простое математическое выражение, которое переводит значение переменной $f со значения по Фаренгейту в значение по Цельсию. Для этого используется формула По_Целъсию = (5/9) х (По_Фаренгейту - 32). Затем переменной $out присваивается строковое значение, в котором содержится сообщение о результатах перевода.
Если же окажется, что у переменной $f есть значение, а у переменной $с его нет, выполняется обратная операция по переводу значения $с из значения по Цельсию в значение по Фаренгейту с присвоением результата переменной $f. При этом используется следующая формула: По Фаренгейту = (9 / / 5) х По Цельсию + 32. Как и в предыдущем разделе, переменной Sout затем присваивается строковое значение, в котором содержится сообщение о результатах перевода.
Для превращения результатов перевода в целое число в обоих переводах вызывается PHP-функция intval. В этом нет особой необходимости, но результат выглядит лучше.
Теперь, после выполнения всех арифметических вычислений, программа выдает HTML-код, который начинается с базовых элементов head и title и содержит вводный текст, предшествующий отображению значения переменной $out. Если перевода температуры не осуществлялось, переменная Sout будет иметь значение NULL и выводиться на экран ничего не будет, что, собственно, до тех пор, пока не будут отправлены данные формы, нам и нужно. Но если перевод состоялся, переменная Sout содержит результат, который отображается на экране.
Затем следует форма, настроенная на отправку данных файлу convert. php (то есть самой программе) с использованием метода POST. Внутри формы содержатся два поля для ввода температуры как по Фаренгейту, так и по Цельсию. Затем отображается кнопка отправки данных, имеющая надпись Перевести, и форма закрывается.
297297Проверьте ваши знания
297
297
После вывода HTML-кода, закрывающего документ, программа завершается функцией sani ti zeStri ng из примера 11.9.
Все примеры, показанные в данной главе, используют для отправки данных формы метод POST. Я рекомендую применять именно этот метод как наиболее подходящий и безопасный. Разумеется, формы можно легко перестроить под использование метода GET, тогда значения нужно будет извлекать не из массива $_POST, а из массива $_GET. Причины применения
другого метода могут заключаться в предоставлении возможности создания закладок или непосредственных ссылок из другой страницы на результаты поиска.
В следующей главе будет показано, как можно задействовать механизм шаблонов Smarty для реализации среды, позволяющей отделить код вашего приложения от способа представления вашим пользователям всего информационного наполнения.
Проверьте ваши знания
Данные, содержащиеся в форме, могут быть отправлены с использованием одного из двух методов — POST или GET. Какие ассоциативные массивы используются для передачи этих данных РНР-программе?
Что представляет собой свойство regi ster gl obal s и почему его использование не приветствуется?
Чем отличаются друг от друга текстовое поле и текстовая область?
Если форма предлагает пользователю три варианта выбора, каждый из которых исключает все оставшиеся (то есть выбран может быть только один из вариантов), то какой тип элемента ввода данных нужно использовать в этом случае, если есть выбор между флажками и переключателями?
Как из веб-формы отправить группу значений из поля со списком, используя только одно имя поля?
Как отправить данные поля формы, не отображая их на экране браузера?
В какой HTML-тег можно поместить элемент формы, чтобы весь его текст или изображение превратились в область выбора этого элемента по щелчку кнопкой мыши?
Какая PHP-функция используется для преобразования кода HTML в формат, который может быть отображен на экране, но не может интерпретироваться браузером в качестве кода HTML?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 11».
J4L ЖшJ4L ЖшCookie, сессииCookie, сессиии аутентификацияи аутентификация
J4L Жш
J4L Жш
Cookie, сессии
Cookie, сессии
и аутентификация
и аутентификация
По мере укрупнения и усложнения вашего проекта будут возрастать и потребности в учете его пользователей. Даже если не предлагается ввод имени пользователя и пароля, то довольно часто возникает необходимость в хранении сведений о ходе текущей сессии пользователя и, возможно, в том, чтобы узнать его при возвращении на ваш сайт.
Подобное взаимодействие с пользователем поддерживается с помощью нескольких технологий, от простых браузерных cookie до обработки сессий и НТТР- аутентификации. В совокупности они позволяют настроить сайт на пользовательские предпочтения, обеспечивая комфортное путешествие по его страницам.
Использование cookie в РНР
Cookie представляет собой элемент данных, который веб-сервер с помощью браузера сохраняет на жестком диске вашего компьютера. Этот элемент может содержать практически любую буквенно-цифровую информацию (объемом не более 4 Кбайт) и может быть извлечен из вашего компьютера и возвращен на сервер. Чаще всего cookie используются для отслеживания хода сессий, обобщения данных нескольких визитов, хранения содержимого корзины покупателя, хранения сведений, необходимых для входа в систему, и т. д.
В силу своей закрытости cookie могут быть считаны только из создавшего их домена. Иными словами, если cookie, к примеру, был создан на oreilly.com, он может быть извлечен только веб-сервером, использующим этот домен. Это не позволяет другим веб-сайтам получить доступ к сведениям, на владение которыми у них нет разрешения.
Из-за особенностей работы Интернета многие элементы веб-страницы могут быть вставлены из нескольких доменов, каждый из которых может создавать свои собственные cookie. Они называются сторонними cookie. Чаще всего они создаются рекламными компаниями с целью отслеживания пользователей на нескольких веб-сайтах.
Большинство веб-браузеров позволяют пользователями отключать cookie либо от домена текущего сервера, либо от сторонних серверов, либо и от тех и от других. К счастью, большинство пользователей, отключающих cookie, делают это только в отношении сторонних веб-сайтов.
Обмен cookie осуществляется во время передачи заголовков еще до того, как будет отправлен код HTML веб-страницы. Отправить cookie после передачи HTML-кода уже невозможно. Поэтому четкое планирование использования cookie приобретает особую важность. На рис. 12.1 показан типичный диалог с передачей cookie в форме «запрос — ответ» между веб-браузером и веб-сервером.
Заголовки запроса веб-браузера Заголовки, возвращаемые webserver.com
Рис. 12.1. Диалог браузера и сервера в режиме «запрос — ответ» с использованием cookie
В этом обмене данными показан браузер, получающий две страницы.
Браузер выдает запрос на извлечение главной страницы index.html с веб-сайта http://www.webserver.com. В первом заголовке указывается файл, а во втором — сервер.
Когда веб-сервер на webserver.com получает эту пару заголовков, он возвращает несколько своих заголовков. Во втором заголовке определяется тип отправляемого содержимого (text/html), а в третьем отправляется cookie по имени пате, имеющий значение value. И только после этого передается содержимое вебстраницы.
После того как браузер получит cookie, он должен возвращать его с каждым последующим запросом, сделанным в адрес сервера, создавшего cookie, пока у cookie не истечет срок действия или этот cookie не будет удален. Поэтому, когда браузер запрашивает новую страницу /news.html, он также возвращает cookie пате со значением value.
Поскольку на момент отправки /news.html cookie уже был установлен, сервер не должен заново посылать этот cookie и возвращает только запрошенную страницу.
Установка cookie
Установка cookie в РНР осуществляется довольно просто. До передачи кода HTML нужно вызвать функцию setcookie, для чего используется следующий синтаксис (табл. 12.1):
setcookie(name. value, expire, path, domain, secure, httponly);
Таблица 12.1. ПараметрыI функции setcookie
Параметр
Описание
Пример
name
Имя cookie. Это имя ваш сервер будет использовать для доступа к cookie при последующих запросах браузера
username
value
Значение cookie или его содержимое. Объем может составлять до 4 Кбайт буквенно-цифрового текста
Hannah
expire
(Необязательный.) Время истечения срока действия в формате метки времени Unix. Как правило, для установки этого параметра будет использоваться функция time(), к которой будет прибавляться количество секунд. Если параметр не установлен, срок действия cookie заканчивается с закрытием браузера
time() + 2592000
path
(Необязательный.) Путь к cookie на сервере. Если в качестве пути используется прямой слеш (/), cookie доступен для всего домена, например для домена www.webserver.com. Если указан подкаталог, cookie доступен только в пределах этого подкаталога. По умолчанию путь указывает на текущий каталог, где был установлен cookie, и, как правило, используется именно такая настройка
/
domain
(Необязательный.) Интернет-домен, которому принадлежит cookie. Если это webserver.com, то cookie доступен для всего домена webserver.com и его поддоменов, например www. webserver.com и для images.webserver.com. Если это images. webserver.com, то cookie доступен только для images. webserver.com и его поддоменов, например sub.images. webserver.com, но не для www.webserver.com
.webserver.com
secure
(Необязательный.) Определяет, должен ли cookie использовать безопасное подключение (https://). Если значение параметра установлено в TRUE, cookie может быть передан только по безопасному подключению. По умолчанию устанавливается значение FALSE
FALSE
httponly
(Необязательный; реализован в РНР, начиная с версии 5.2.0.) Определяет, должен ли cookie использовать протокол HTTP. Если значение параметра установлено в TRUE, то такие языки сценариев, как JavaScript, не могут получить доступ к cookie. (Это свойство поддерживается не во всех браузерах). По умолчанию задается значение FALSE
FALSE
Для создания cookie-файла по имени username со значением Hannah, к которому имеется доступ со всего веб-сервера текущего домена и который будет удален из браузерного кэша через семь дней, используется следующая строка кода:
setcookie( 'username', 'Hannah'. timeO + 60 * 60 * 24 *7 ,. V):
Доступ к cookie
Для чтения значения cookie нужно просто обратиться к системному массиву $_С00К1Е. Например, если нужно посмотреть, хранится ли на текущем браузере cookie по имени username, и, если хранится, прочитать его значение, используется следующая строка кода:
if (isset($_C00KIE['username']))
Susername = $_C00KIE['username']:
Учтите, что прочитать значение cookie можно только после того, как он был отправлен веб-браузеру. Это означает, что при установке cookie его нельзя прочитать до тех пор, пока браузер не перезагрузит страницу (или не совершит какое- нибудь другое действие с доступом к cookie) с вашего веб-сайта и не передаст cookie в ходе этого процесса обратно на сервер.
Удаление cookie
Для удаления cookie его нужно повторно установить с настройкой даты истечения срока действия на прошедшее время. При этом важно, чтобы все параметры нового вызова функции setcook i е, за исключением ti mestamp, точно повторяли параметры, указываемые при создании cookie, в противном случае удаление не состоится. Поэтому для удаления ранее созданного cookie нужно воспользоваться следующей строкой кода: setcookie('username'. 'Hannah', timeO - 2592000.
Поскольку указано уже прошедшее время, cookie будет удален. Здесь я использовал время, равное 2 592 ООО с в прошлом (что соответствует одному месяцу). Это сделано в расчете на неправильную установку даты и времени на компьютере клиента.
НТТР-аутентификация
HTTP-аутентификация использует веб-сервер для управления пользователями и паролями при работе приложения. Ее можно применять во многих приложениях, требующих от пользователей входа в приложение, хотя для некоторых приложений нужны особые меры или соблюдение более строгих требований безопасности, для чего следует обратить&д к другим технологическим приемам.
Чтобы воспользоваться HTTP-аутентификацией, РНР отправляет заголовок запроса, инициирующий аутентификационный диалог с браузером. Чтобы эта технология заработала, на сервере должно быть включено соответствующее свойство, но, скорее всего, в силу востребованности это свойство на вашем сервере уже включено.
—————
Хотя, как правило, НТТР-аутентификация устанавливается вместе с Apache, это 'еще не и5 А * означает, что она установлена на используемом вами сервере. Поэтому при попытке запус- —**?■ ка представленных здесь примеров может быть сгенерирована ошибка и выдано сообщение о том, что это свойство недоступно. В таком случае нужно установить соответствующий модуль, изменить для загрузки модуля файл конфигурации или попросить своего системного администратора внести все эти изменения.
Пользователи при вводе в браузер URL-адреса или при переходе по ссылке видят окно с требованием пройти аутентификацию Требуется аутентификация, в котором выводится приглашение заполнить два поля: для имени пользователя и пароля (на рис. 12.2 показано, как это выглядит в браузере Firefox).
Код, обеспечивающий аутентификацию, показан в примере 12.1.
Рис. 12.2. Приглашение войти в систему в режиме НТТР-аутентификации
Пример 12.1. РНР-аутентификация
<?php
if (isset($_SERVER['PHP_AUTH_USER']) &&
i sset($_SERVER[*PHP_AUTH_PW' ]))
{
echo "Добро пожаловать, пользователь: " . $_SERVER['PHP_AUTH_USER'] .
" . имеющий пароль: " . $_SERVER['PHP_AUTH_PW'];
} else
{
header('WWW-Authenticate: Basic rea1m="Restricted Section"'):
header('HTTP/1.0 401 Unauthorized');
die("Пожалуйста, введите имя пользователя и пароль");
} ?>
Сначала программа ищет значения конкретных элементов массива: $_SERVER[ ’ РНР_ AUTHJJSER' ] и $_SERVER[' PHP_AUTH_PW' ]. В этих элементах в случае их существования содержатся имя пользователя и пароль, введенные пользователем при приглашении пройти аутентификацию.
Если какое-нибудь из значений отсутствует, значит, пользователь еще не прошел аутентификацию, и появляющееся окно с приглашением, показанное на рис. 12.2, отображается с выдачей следующего заголовка, составной частью которого является имя защищенного раздела — Basic realm:
WWW-Authenticate: Basic realm=”Restricted Section"
Если пользователь заполнит поля, PHP-программа будет запущена снова с самого начала. Но если пользователь щелкнет на кнопке Отмена, программа перейдет к следующим двум строкам, которые отправят следующий заголовок и сообщение об ошибке:
HTTP/1.О 401 Unauthorized
Инструкция die выведет следующий текст: «Пожалуйста, введите имя пользователя и пароль» (рис. 12.3).
' 1Mozilla Firefox
Файл Правка Вид Журнал Закладки Инструменты Справка
с х &с х &( L3 http:/flocalhost/web/authenticate.php( L3 http:/flocalhost/web/authenticate.php
с х &
с х &
( L3 http:/flocalhost/web/authenticate.php
( L3 http:/flocalhost/web/authenticate.php
Пожалуйста, введите имя пользователя и пароль
Готово
Рис. 12.3. Результат щелчка на кнопке Отмена
После прохождения пользователем аутентификации заставить появиться диалоговое окно Для аутентификации уже не удастся до тех пор, пока пользователь не закроет и не откроет снова все окна браузера, поскольку веб-браузер будет постоянно возвращать РНР-про- грамме имя пользователя и пароль. Чтобы при изучении этого раздела испытать все возможные режимы работы, может потребоваться несколько раз закрыть и снова открыть ваш браузер.
Теперь проверим допустимость пользовательского имени и пароля. Для добавления этой проверки вносить большие изменения в код примера 12.1 не придется: нам нужно лишь изменить прежнее приветственное сообщение и превратить его в проверку правильности имени пользователя и пароля, за'которой последует приветствие. Неудачная аутентификация вызовет отправку сообщения об ошибке (пример 12.2).
Пример 12.2. PHP-аутентификация с проверкой вводимой информации
<?php
$username = 'admin':
$password = 'letm ein':
if (isset($_SERVER['PHP_AUTH_USER']) &&
i sset($_SERVER[' PHP_AUTH_PW']))
{
i f ($_SERVER[1PHP_AUTH_USER'] == $username && $_SERVER['PHP_AUTH_PW'] == Spassword) echo "Регистрация прошла успешно":
else dieC'HeBepHafl комбинация "имя пользователя - пароль""): } else
{
header('WWW-Authenticate: Basic realm="Restricted Section"’):
header('HTTP/1.0 401 Unauthorized'):
die ("Пожалуйста, введите имя пользователя и пароль"):
} ?>
Кстати, обратите внимание на формулировку сообщения об ошибке: «Неверная комбинация “имя пользователя — пароль”». В ней не сообщается, что именно было введено неправильно: имя пользователя, пароль или же и то и другое —чем меньше информации попадет к потенциальному взломщику, тем лучше.
Теперь у нас есть механизм аутентификации пользователей, но только для одного имени пользователя и пароля. К тому же пароль появляется в файле РНР в виде простого текста, и если кому-нибудь удастся взломать ваш сервер, он тут же вскроет и пароль. Поэтому рассмотрим более подходящий способ работы с именами пользователей и паролями.
«if •*
"^ I
Безопасность в современных браузерах становится все строже и достигла такого уровня, M?' * « при котором проверить HTTP-аутентификацию на локальной файловой системе, пока не —— будут изменены настройки вашего браузера, не так-то просто. Это связано с необходимостью защиты от потенциально вредоносных файлов, которые могут быть скачаны из Интернета (поскольку локальные файлы обычно представляют собой больший риск с точки зрения безопасности). Если нужно написать код, использующий этот тип аутентификации, то лучше провести тестирование на удаленном сервере, используя интернет-соединение.
Сохранение имен пользователей и паролей
Безусловно, самым естественным способом сохранения имен пользователей и паролей будет задействование MySQL. Но опять-таки хранить пароли в виде простого текста не хочется, поскольку, если база данных будет взломана, наш веб-сайт подвергнется опасности. Вместо этого будет использован тонкий прием с применением так называемой односторонней функции.
Функции этого типа просты в использовании и способны превращать строку текста в строку, напоминающую набор произвольных символов. Односторонние функции практически невозможно применять в обратном направлении, поэтому производимая ими выходная информация может безопасно храниться в базе данных и ее похититель ничего не узнает об используемых паролях.
Задействуемая нами функция называется md5. Ей передается строка для хеширования, а она возвращает 32-символьное шестнадцатеричное число. Код, в котором она используется, имеет следующий вид:
Stoken = md5(’мой гпароль');
При запуске этого кода переменная Stoken может получить следующее значение:
34819d7beeabb9260a5c854bc85b3e44
Можно также воспользоваться схожей функцией shal, которая считается более безопасной, имеет более удачный алгоритм и возвращает 40-символьное шестнадцатеричное число.
Добавление произвольных данных
К сожалению, применения одной только функции md5 для защиты базы данных с паролями недостаточно, поскольку пароль можно вскрыть путем перебора, в котором используется другая база данных, состоящая из известных 32-символьных шестнадцатеричных лексем, выдаваемых функцией md5. В существовании подобных баз данных можно убедиться, воспользовавшись поисковой системой Google.
К счастью, любой поисковой атаке можно поставить заслон путем добавления произвольных данных ко всем паролям перед их отправкой функции md5. Этот прием, ассоциирующийся с «посыпанием солью» (salting), заключается в простом добавлении некоего известного только нам текста к любому кодируемому параметру:
Stoken = md5( ’Дополнительная^трока^юй^ароль’);
В данном примере перед паролем был набран текст Дополнительная_строка. Конечно, чем малоизвестнее будет эта дополнительная строка, тем лучше. Я предпочитаю добавлять строку, похожую на эту:
$token = md5( 'hqb£$tMCM_nap(mcg*l'):
Здесь произвольные символы помещены как до, так и после пароля. На основе лишь тех данных, которые хранятся в базе, не имея доступа к вашему РНР-коду, вскрыть хранящиеся пароли практически невозможно.
Учитывая то, как растет скорость компьютерной обработки, MDS-строки, использующие , короткие исходные строки, могут быть потенциально взломаны в течение недель (а не лет). ^ Поэтому был разработан алгоритм SHA1, взломать который значительно сложнее, чем MD5, и который возвращает шестнадцатеричную строку, состоящую из 40 символов.
Чтобы защитить ваш код от будущих перемен, может потребоваться вместо функции md5 воспользоваться PHP-функцией shal. (Если хранить значения SHA1 в MySQL, нужно обязательно установить длину поля равной 40 символам.) Или же, если требуется более стойкое кодирование, я рекомендую обратить внимание на PHP-функцию crypt, использующую алгоритм CRYPT_BLOWFISH, описание которой дано на веб-странице http://tinyurl.com/ phpcrypt.
Для проверки принадлежащего кому-нибудь пароля при входе на сайт нужно будет лишь добавить те же самые произвольные строки в начало и конец пароля, а затем сравнить полученную от функции md5 лексему с той, которая была сохранена в базе данных для данного пользователя.
Создадим таблицу MySQL, в которой будут храниться сведения о пользователе, и добавим к ней пару учетных записей. Наберите код, показанный в примере 12.3, и сохраните его в файле setupusers. php, после чего откройте этот файл в своем браузере.
Пример 12.3. Создание таблицы users и добавление к ней двух учетных записей
<?php // setupusers.php require_once 'login.php';
$db_server = mysql_connect($db_hostname. $db_username. $db_password);
if ( !$db_server) die("Невозможно подключиться к MySQL: " . mysql_error()): mysql_select_db($db_database)
or die("Невозможно выбрать базу данных: " . mysql_error());
Squery = "CREATE TABLE users (
forename VARCHAR(32) NOT NULL,
surname VARCHAR(32) NOT NULL,
username VARCHAR(32) NOT NULL UNIQUE,
password VARCHAR(32) NOT NULL )";
Sresult = mysql_query(Squery);
if (!Sresult) die ("Сбой при доступе к базе данных: " . mysql_еггог());
Ssaltl = "qm&h*";
Ssalt2 = ”pg!@":
Sforename = 'B ill':
Ssurname = 'Smith':
Susername = 'bsmith':
Spassword = ’mysecret':
Stoken = md5("$saltl$passwordSsalt2")^':
add_user(Sforename. Ssurname. Susername. Stoken):
Sforename = 'Pauline':
Ssurname = 'Jones';
Susername = 'pjones':
Spassword = 'acrobat';
Stoken = md5("SsaltlSpasswordSsalt2"):
add_user(Sforename. Ssurname. Susername, Stoken):
function add_user($fn, Ssn, Sun, Spw) {
Squery = "INSERT INTO users VALUESCSfn', 'Ssn'. 'Sun'. 'Spw')";
Sresult = mysql_query(Squery);
if (!Sresult) die ("Сбой при доступе к базе данных: " . mysql_error());
} ?>
Эта программа создаст в вашей базе данных publications (или в той базе данных, на которую вы настроились в файле 1 ogi n. php в главе 10) таблицу users. В этой таблице будут созданы два пользователя, Bi 11 Smith и Paul i ne Jones, с именами пользователей и паролями bsmith — mysecret и pjones — acrobat соответственно.
Теперь, используя данные, имеющиеся в этой таблице, мы можем модифицировать код примера 12.2 для вполне приемлемой аутентификации пользователей.
Необходимый для этого код показан в примере 12.4. Наберите этот код, сохраните его в файле authenticate.php и вызовите эту программу в своем браузере.
Пример 12.4. PHP-аутентификация с использованием MySQL
<?php // authenticate.php
requi re_once '1ogi n.php’ ;
$db_server = mysql_connect($db_hostname. $db_username, $db_password);
if ( !$db_server) die("HeB03M0>KH0 подключиться^ MySQL: " . mysql_error());
mysql_select_db($db_database)
or die("HeB03M0>KH0 выбрать базу данных: " . mysql_error());
if (isseIt($_SERVER['PHP_AUTH_USER']) &&
i sset($_SERVER[’PHP_AUTH_PW' ]))
{
$un_temp = mysql_entities_fix_string($( SERVER[,PHP_AUTH_USER,]):
$pw_temp = mysql_entities_fix_stгing($_SERVER[,PHP_AUTH_PW,]);
$query = "SELECT * FROMusers WHERE username=,$un_teпlp,":
$result = mysql_query($query):
if (!$resu1t) die("C6oii при доступе к базе данных: " mysql_еггог());
elseiif (mysql_num_ rows($result))
{
$row = mysql_fetch_row($result);
Isa ltl = "qm&h*";
$salt2 = "рд!Г;
$token = md5(n$saltl$pw_temp$salt2’’) ;
if ($token = $row[3]) echo "$row[0] $row[l] :
Привет. $row[0], теперь вы зарегистрированы под именем '$row[2]"';
else сИеСНеверная комбинация "имя пользователя - пароль'"’):
}
else dieCHeeepHafl комбинация имя пользователя-пароль"):
else
{
header('WWW-Aj~fierf:cate: Basic realm="Restricted Section'");
header('HTTP/l.Q 401 Unauthorized’);
die ("Пожалуйста, введите имя пользователя и пароль"):
}
function mysql_entities_fix_sttring(Sstring) >
{
return htmlenti 11es Сmysql_fix_stri ng($string));
}
function mysql_fi x_str"ng{Sstri ng)
if (get_magic_quotes_g?c()) :$string = stripslashes(Sstring);
return mysql_real_escaoe_string($string):
}
?>
Вы, наверное, ожидали, что настало время для некоторых весьма больших по объему примеров кода (таких как этот пример). Но по этому поводу не стоит переживать. Последние десять строк — не что иное, как пример 10.22, который уже встречался в главе 10. Эти строки присутствуют здесь для выполнения очень важной задачи — обезвреживания введенных пользователем данных.
На данный момент практический интерес для вас должны представлять только те строки, которые выделены жирным шрифтом и начинаются с присваивания значений двум переменным, $un_temp и $pw_temp, с использованием отправленных имени пользователя и пароля. Затем выдается запрос к MySQL на поиск пользователя с именем $un_temp, и, если будет возвращен результат, значение его первой строки присваивается переменной $row. (Поскольку имя пользователя уникально, возвращена будет только одна строка.) Потом создаются переменные $sal tl и $sa 112 с двумя произвольными строками, которые затем добавляются до и после отправленного пароля Spw temp. После этого получившаяся в результате строка передается функции md5, которая возвращает 32-символьное шестнадцатеричное значение, присваиваемое переменной $token.
Теперь остается лишь сравнить значение переменной Stoken со значением, хранящимся в базе данных в четвертой графе, которая при начале отсчета с нуля соответствует графе 3. То есть $row[3] содержит предыдущую лексему, вычисленную для «посыпанного солью» пароля. Если значения совпадают, выдается строка приветствия, в которой содержится обращение к пользователю по его настоящему имени (рис. 12.4). В противном случае выдается сообщение об ошибке. Как уже упоминалось, это сообщение всегда содержит одну и ту же информацию независимо от того, существует такое имя пользователя или нет, поскольку потенциальный взломщик или тот, кто подбирает пароли, получает в результате этого минимум полезной для себя информации.
5Mozilia FirefoxB E ®к]
файл Правка Вид Журнал Закладки Инструменты Справка
^ О/С .i Q http://locdhost/web/authenticate.php^2? ^
Bill Smith: Привет, Bill, теперь Вы зарегистрированы под Именем 'bsmith'
Готово
Рис. 12.4. Аутентификация пользователя Bill Smith прошла успешно
Вы можете самостоятельно испытать эту программу в работе, вызвав ее в браузере и набрав имя пользователя bsmith и пароль mysecret (или набрав пару pjones и acrobat), то есть те значения, которые были сохранены в базе данных программой из примера 12.3.
Использование сессий
Поскольку ваша программа не может сообщить о том, какие значения были присвоены переменным в других программах, или даже о том, какие значения были
присвоены переменным при ее предыдущем запуске, иногда возникает потребность в отслеживании действий пользователя, переходящего со страницы на страницу. Это можно сделать за счет установки в форме скрытых полей, как было показано в главе 10, и проверки значений этих полей после отправки формы, но РНР предоставляет более простое и действенное решение — сессии. Сессии представляют собой группы переменных, которые хранятся на сервере, но относятся только к текущему пользователю. Чтобы обеспечить обращение нужных пользователей к нужным переменным, для уникальной идентификации этих пользователей их веб-браузерами сохраняются файлы cookie.
Эти cookie имеют значение только для веб-сервера и не могут быть использованы для извлечения какой-либо информации о пользователе. Вы можете спросить о том, как быть с теми пользователями, которые отключили cookies. Это не проблема, поскольку в РНР, начиная с версии 4.2.0, такие случаи выявляются и cookie помещаются в область GET-запроса каждого URL-адреса. В любом случае сессии предоставляют надежный способ отслеживания действий ваших пользователей.
Начало сессии
Чтобы инициировать работу сессии, нужно перед выводом на экран любого кода HTML вызвать PHP-функцию session start точно так же, как это делалось при отправке cookie в процессе обмена заголовками. Затем, чтобы приступить к сохранению переменных сессии, им нужно присвоить значения как элементам массива $_SESS ION:
$_SESSI0N[' имя_переменной’] = $переменная_со_значением;
При последующих запусках программы их значения можно будет снова прочитать, воспользовавшись следующим кодом:
$имя_переменной = $_SESSI0N[' имя_переменной'];
Предположим, у вас есть приложение, которому всегда нужен доступ к пользовательскому имени и паролю, а также к настоящему имени и фамилии каждого пользователя в том виде, в котором они сохранены в базе данных в таблице users, созданной совсем недавно. Выполним еще одну модификацию программы authenti cate. php из примера 12.4, чтобы инициировать работу сессии сразу же после идентификации пользователя.
Все необходимые изменения показаны в примере 12.5. Единственное отличие касается раздела i f (Stoken == $row[3]), который теперь начинается с открытия сёссии и сохранения в ней четырех переменных. Наберите код этой программы (или измените код примера 12.4) и сохраните его в файле authenti cate2. php. Но пока не запускайте эту программу в браузере, поскольку нужно будет создать еще и вторую программу.
Пример 12.5. Открытие сессии после успешной аутентификации
<?php //autthenticate2.php
requi re_once ' 1og1in.php';
$db_server = mysql_connect($db_hostname, $db_username. $db_password);
if ( !$db_server) die("Невозможно подключиться к MySQL: " . mysql_error()): mysql_select_db(Sdb_database)
or die(,,Heвoзмoжнo выбрать базу данных: " . mysql_error()):
if (isset($_SERVER['PHP_AUTH_USER']) &&
i sset($_SERVER[’PHP_AUTH_PW']))
{
$un_temp = mysql_entities_fix_string(S_SERVER[,PHP_AUiH_USLR’j) :
$pw_temp = mysql_entitiesJrix_string(SJ>ERVER[,PHP_AUTH_[V ] : :
Squery = "SELECT * FROM users WHERE username='Sun_temp'":
Sresult = mysql_query(Squery);
if (!Sresult) die("Cбoй при доступе к базе данных: " . n-.ysqi_er'r‘:r;. elseif (mysql num_rows(Sresult))
{
$row = mysql_fetch_row(Sresult):
Ssaltl = "qm&h*";
$salt2 = "pg!@”;
$token = md5("$saltl$pw_temp$salt2”);
if ($token = $row[3]) {
sessionstartO;
$_SESSION['username'] = Suntemp;
$_SESSION['password'] = $pw_temp;
$_SESSION['forename'] = $row[0];
$_SESSION['surname'] = $row[l];
echo "$row[0] $row[l] : Привет, $row[0],
теперь вы зарегистрированы под именем ’$row[2],e;
die ("<р><а href=continue.php>llleлкнитe здесь для продолжения</а></р>");
>
else dieC'HeeepHafl комбинация "имя пользсвз^егя - ~5::гг
}
else ё1е("Неверная комбинация "имя пользователя - ^эосг= :
}
else
{.
header('WWW-Authenticate: Basic realm="Restr'C'ec iecv : r_' header(’HTTP/1.0 401 Unauthorized');
die ("Пожалуйста, введите имя пользователя и газсгь";:
functi on mysql_enti ti es_fi x_string(Sstring)
{
return htmlentities(mysql_fix_string(Sstr' rc;;:
function mysql_fix_string(Sstring)
{.
if (get_magic_quotes_gpc()) Sstring = str-'pslashes(Sstring):
return mysql_real_escape_string($string):
} ?>
К программе также добавлена ссылка Щелкните здесь для продолжения с URL-адресом conti nue. php. Она будет использована для иллюстрации того, как сессия будет перенесена на другую программу или веб-страницу PHP-программы. Поэтому создайте файл conti nue. php, набрав и сохранив в нем программ^ из примера 12.6.
Пример 12.6. Извлечение переменных сессии
<?php // continue.php session_start();
if (isset($ t SESSI0N['username’]))
{
Susername = $_SESSION[,username'];
Spassword = S_SESSION[,password'];
Sforename = S_SESSI0N[’forename'];
Ssurname = $_SESSI0N['surname'];
echo "С возвращением, Sforename.<br />
Ваше полное имя Sforename Ssurname.<br />
Ваше имя пользователя 'Susername'
и Ваш пароль 'Spassword'.":
}
else echo "Пожалуйста, для входа <а href=authenticate2.php>u^KHHTe здесь</а>."; ?>
Теперь можно вызвать в браузере authenticate2.php, после появления приглашения ввести имя пользователя bsmith и пароль mysecret (или pjones и acrobat) и щелкнуть на ссылке для загрузки программы conti nue. php. Когда браузер вызовет эту программу, появится результат, аналогичный показанному на рис. 12.5.
О Mozilla Firefoxш JS т^1
А5иА5иФайл Правка Вид Журнал Закладки Инструменты, Справка
А5и
А5и
▼ С «К ■JL/ ЁЗи 1□3 http://localhost/web/continue.php
С возвращением, Bill
Ваше полное имя Bill Smith
Ваше имя пользователя 'bsmith* и Ваш пароль 'mysecret'.
Готово
Рис. 12.5. Поддержка пользовательских данных с помощью сессий
Сессии искусно ограничивают одной программой весь объемный код, необходимый для аутентификации и регистрации пользователя. После аутентификации пользователя и создания сессии весь остальной программный код действительно упрощается. Нужно лишь вызвать функцию sessi on_start и найти любые переменные, к которым нужен доступ из массива S SESSI0N.
В примере 12.6быстрой проверки наличия значения у элемента $_SESSI0N[' username ’ ] вполне достаточно для того, чтобы узнать об аутентификации текущего пользователя, потому что переменные сессии хранятся на сервере (в отличие от cookie, которые хранятся на машине веб-браузера) и им можно доверять.
Если элементу $_SESSI0N['username'] значение присвоено не было, значит, активная сессия отсутствует, и поэтому последняя строка кода в примере 12.6 перенаправляет пользователей на страницу регистрации на сайте, которая находится в программе authenticate2.php.
Программа continue.php выводит значение пользовательского пароля, чтобы показать, как « работают переменные сессии. На практике вам будет известно, что пользователь уже зарегистрировался, поэтому отслеживать (или выводить) любые пароли нет необходимое™, тем более что подобные действия угрожают безопасности системы.
Завершение сессии
Обычно когда пользователю нужно уйти с вашего веб-сайта, наступает момент завершения работы сессии, для чего, как показано в примере 12.7. вместе с функцией unset можно воспользоваться функцией sessi cn_ces"r':у. В этом примере предоставляется полезная функция для полного уничтожения сессии, выхода пользователя и очистки всех переменных сессии.
Пример 12.7. Полезная функция уничтожения сессии и ее
<?php
function destroy_session_and_data()
{
session_start();
$_SESSI0N = arrayO:
if (session_id() != "" || isset($JOT<IE[sess^cr._r.arne';];) setcookie(session_name().timeO - 259231С.
session_destro I y();
}
?>
Чтобы увидеть этот код в действии, можно модифицировать программу ccnti nue. php, как показано в примере 12.8.
Пример 12.8. Извлечение переменных сессии перед ее уничтожением
<?php
session_start();
if (isset($_SESSI0N[’username']))
{
$username = $_SESSI0N['username'];
Spassword = $_SESSI0N['password']:
$forename = $_SESSI0N['forename']:
Ssurname = $_SESSI0N['surname']:
destroysessioгnanddaItaO;
echo "С возвращением, $forename.<br />
Ваше полное имя Sforename $surname.<br />
Ваше имя пользователя ’$username' и ваш пароль ' $password';
}
else echo "Пожалуйста, для входа <а href=authenticate2.php>u^«HHTe здесь</а>.";
function destroy_session_and_data()
{
$_SESSI0N = аггауО;
if (session idO != ,,,, || isset($_COOKIE[session_name()])) setcookie(session_name(), " , timeO - 2592000. V);
sessiondestroyO;
} ?>
При первом переходе из authenticate2.php в continue.php будут выведены все переменные сессии. Но если после этого щелкнуть в браузере на кнопке обновления страницы, в результате предшествующего этому вызова функции destroy^session_ and data сессия уже будет*унйчтожена и будет выведено приглашение вернуться на страницу регистрации.
Установка времени ожидания
Есть и другие причины, по которым может потребоваться самостоятельное закрытие пользовательской сессии, например, если пользователь забыл зарегистрироваться или проигнорировал этот процесс и нужно, чтобы программа закрыла его сессию в целях собственной безопасности. Это можно сделать, установив время ожидания, по истечении которого, если не предпринять активных действий, произойдет автоматическое завершение работы.
Для этого используется функция ini_set. В данном примере время ожидания устанавливается ровно на сутки:
ini_set('session.gcjnaxlifetim e', 60 * 60 * 24):
Если нужно узнать текущее время ожидания, его можно отобразить, воспользовавшись следующим кодом:
echo ini_get(’session.gc_maxlifetime’): %
Безопасность сессии
Все мои прежние заверения в том, что после аутентификации пользователя и начала сессии можно спокойно предположить, что переменные сессии заслуживают доверия, не вполне соответствуют действительности. Дело в том, что для вскрытия идентификаторов сессий, передаваемых по сети, можно организовать анализ пакетов — packet sniffi I ng (перехват набора данных). Кроме того, если идентификатор (ID) сессии передается в области GET-запроса URL-адреса, он может появиться в файлах
регистрации внешних сайтов. Единственный по-настоящему безопасный способ предотвращения вскрытия заключается в использовании протокола защищенный сокетов — Secure Socket Layer (SSL) и в запуске веб-страниц, использующих вместо протокола HTTP протокол HTTPS. Эта тема выходит за рамки данной книги, но за подробностями настроек безопасности веб-сервера можно обратиться по адресу http://www.a pache-ssl.org.
Предупреждение хищения сессии
Когда применение SSL не представляется возможным, можно продолжить аутентификацию пользователей за счет хранения наряду с остальными сведениями их IP-адресов. Для этого нужно при сохранении их сессии добавить следующую строку кода:
$_SESSI0N[’1р'] = $_SERVER['REMOTE_ADDR']:
Затем в качестве дополнительной меры контроля при любой загрузке страницы и доступности сессии проводится следующая проверка, которая при несоответствии текущего IP-адреса сохраненйому вызывает функцию di fferent_user:
if ($_SESSI0N[’ip '] != $_SERVER['REMOTE_ADDR']) different_user();
Какой код будет у функции di fferent_user — решать вам, но я рекомендую просто удалить текущую сессию и попросить пользователя пройти повторную регистрацию вследствие технической ошибки. Больше ни о чем сообщать не нужно, иначе произойдет утечка потенциально ценной информации.
Разумеется, нужно принимать в расчет, что пользователи, работающие через один и тот же прокси-сервер или использующие одинаковые общие IP-адреса в домашней или офисной сети, будут иметь один и тот же IP-адрес. Если это вызовет проблему, нужно опять обратиться к протоколу SSL. Можно также сохранить копию браузерной строки агента пользователя (той самой строки, которую разработчики помещают в свои браузеры, для того чтобы идентифицировать их по типу и версии), с помощью которой также возможно отличить пользователей друг от друга благодаря существованию широкого выбора типов, версий и компьютерных платформ. Для сохранения агента пользователя можно воспользоваться следующим кодом:
$_SESSION['ua'] = $_SERVER[’HTTP_USER_AGENT’J:
А для сравнения текущей строки агента с сохраненной можно воспользоваться следующим кодом:
if ($_SESSI0N['ua’] != $_SERVER['HTTP_USER_AGENT']) different_user():
Или, что еще лучше, можно объединить эти две проверки и сохранить их комбинацию в виде шестнадцатеричной строки, получаемой от функции md5:
$_SESSI0N['check'] = md5($_SERVER[’REMOTE_ADDR'] .
$_SERVER[1HTTP_USER_AGENT']):
Затем для сравнения текущей и сохраненной строк можно применить такой код:
if ($_SESSI0N['check'] 1= md5($_SERVER[,REM0TE_ADDR'] .
$ SERVERC'HTTP USER AGENT'])) different userO:
Предотвращение фиксации сессии
При фиксации сессии злоумышленник пытается навязать серверу ее идентификационный номер (ID), не позволяя ему самостоятельно присвоить этот номер. Это происходит в том случае, если пользователь злоупотребляет возможностью передачи ID сессии в области GET-запроса URL-адреса:
http://yourserver.com/authenti cate.php?PHPSESSID=123456789
В данном случае серверу будет передан вымышленный ID сессии 123456789. А теперь рассмотрим пример 12.9, код которого восприимчив к фиксации сессии. Чтобы увидеть его в действии, наберите текст примера и сохраните его в файле sessiontest.php.
Пример 12.9. Сессия, восприимчивая к фиксации сессии
<?php // sessiontest.php
session_start():
if (!isset($_SESSION['count'])) $_SESSI0N['count’] = 0;
else ++$_SESSI0N['count'];
echo $_SESSION['count']: ?>
После того как код будет сохранен, вызовите программу в вашем браузере, используя следующий URL (предваряя его правильным путевым именем, например http ://local host/web/):
sessiontest.php?PHPSESSID=1234
Через некоторое время, щелкнув на кнопке обновления страницы, вы увидите увеличение значения счетчика. Теперь попробуйте ввести в браузер следующий URL-адрес:
sessiontest.php?PHPSESSID=5678
Щелкните несколько раз на кнопке обновления страницы и увидите, что счет опять начался с нуля. Оставьте показания счетчика на номере, отличающемся от его показаний при использовании первого URL-адреса, вернитесь на первый URL- адрес и посмотрите на то, как показания счетчика вернулись к первоначальному значению. Вы по собственному усмотрению создали две разные сессии и без особого труда можете создать их в любом количестве.
Особая опасность такого подхода состоит в том, что затеявший атаку злоумышленник может попытаться распространить такие URL-адреса среди ничего не подозревающих пользователей, и если кто-нибудь из них перейдет по ссылкам с этими адресами, атакующий сможет вернуться и перехватить любую сессию, которая не была удалена или срок действия которой еще не истек!
Для предотвращения фиксации сессии нужно для изменения ID сессии воспользоваться функцией session_regenerate_id. Эта функция сохраняет значения всех переменных текущей сессии, но заменяет ID сессии новым, о котором не может знать атакующий.
Теперь при получении запроса можно проверить факт существования специальной переменной сессии, которую вы выдумали произвольным образом. Если
ее не существует, вы будете знать, что создана новая сессия, поэтому вы просто меняете ID сессии и устанавливаете значение ее специальной переменной, позволяющей заметить изменение.
В примере 12.10 показано, как может выглядеть код, использующий переменную сессии initiated.
Пример 12.10. Регенерация сессии
<?php
session_start();
if (!isset($_SESSI0N['initiated']))
{
session_regenerate_id();
$_SESSI0N['init I iated *] = 1;
}
if (!isset($_SESSI0N['count'])) $_SESSI0N[*count1] = 0;
else ++$_ SESSION[’count1];
echo $_SESSI0N[’count’];
?>
Атакующий может вернуться на ваш сайт, используя любые сгенерированные им ID-номера сессии, но ни один из них не приведет к вызову другой пользовательской сессии, поскольку все они будут заменены регенерированными ID-номерами. Если вы склонны к крайним мерам, то можете даже регенерировать ID сессии при каждом запросе.
Преднамеренная настройка на сессии, использующие исключительно cookie
Если вы собираетесь потребовать от своих пользователей включить cookies при просмотре ваших веб-сайтов, то можете воспользоваться функцией ini set:
ini_set( ’session.use_only_cookies’, 1);
С такой настройкой трюк с ?PHPSESSID= будет полностью проигнорирован. Если вы воспользуетесь этой мерой безопасности, я также рекомендую проинформировать ваших пользователей о том, что для работы с вашим сайтом требуются cookie, чтобы они знали причину, по которой им не удается получить требуемых результатов.
Использование общего сервера
Если вы делите общий сервер с владельцами других учетных записей, то вам наверняка не захочется, чтобы все данные ваших сессий хранились в том же каталоге, где хранятся данные других пользователей. Вместо этого нужно выбрать для хранения ваших сессий каталог, доступный только пользователю с вашей учетной записью (и поэтому невидимый в сетевом окружении). Для этого нужно ближе к началу программы поместить вызов функции ini set:
ini_sett(’session.save_path’, ’/home/user/myaccount/sessions1):
В результате такой настройки конфигурации новое значение будет сохраняться только на время выполнения программы, а как только она завершит свою работу, будут возвращены исходные настройки.
Папка с данными сессий будет быстро заполняться, и в зависимости от занятости вашего сервера может потребоваться ее периодическая очистка от данных устаревших сессий. Чем чаще она будет использоваться, тем реже будет возникать желание хранить данные сессий.
Следует помнить о том, что ваши веб-сайты могут и будут подвергаться попыткам взлома, * Существуют так называемые боты (bots), или сетевые автоматические программы, будоражащие Интернет попытками отыскать уязвимые для атак сайты. Поэтому, что бы вы ни делали, если в своей программе вы проводите какую-либо обработку данных, не имеющих стопроцентной гарантии безопасности, к ним всегда нужно относиться с предельной осторожностью.
Теперь, когда вы в достаточной степени усвоили и РНР, и MySQL, настало время в следующей главе предложить вам введение в язык JavaScript — третью основную технологическую составляющую, рассматриваемую в данной книге.
Проверьте ваши знания
Почему cookie должны быть переданы в начале работы программы?
Какая PHP-функция сохраняет cookie на машине веб-браузера?
Как можно удалить cookie?
Где в PHP-программе сохраняются имя пользователя и пароль при использовании НТТР-аутентификации?
Почему функция md5 считается мощным средством защиты?
Что подразумевается под «посыпанием солью» (salting) строки?
Что такое РНР-сессия?
Как инициируется РНР-сессия?
Что такое хищение сессии?
Что такое фиксация сессии?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 12».
11Изучение JavaScriptИзучение JavaScript
1
1
Изучение JavaScript
Изучение JavaScript
JavaScript придает вашим веб-сайтам динамическую функциональность. Когда вы видите, как при прохождении указателя мыши над каким-нибудь элементом браузера что-нибудь выделяется, появляется новый текст, изменяется цветовое оформление или изображение, то должны понимать, что все это делается с помощью JavaScript. Этот язык предлагает такие эффекты, которых нельзя достичь никакими другими средствами, поскольку он запускается внутри браузера и имеет непосредственный доступ ко всем элементам веб-документа.
Впервые JavaScript появился в браузере Netscape Navigator в 1995 году наряду с добавлением в браузер поддержки Java-технологии. Поскольку изначально сложилось неверное представление о том, что JavaScript был побочным продуктом Java, возникло устойчивое заблуждение об их взаимосвязанности. Но такое название было всего лишь удачным маркетинговым ходом, призванным помочь новому языку сценариев получить преимущества за счет той популярности, которой пользовался язык программирования Java.
Когда HTML-элементы веб-страницы обрели более четкое, структурированное определение в так называемой объектной модели документа — DOM (Document Object Model), язык JavaScript получил еще большие возможности. Объектная модель документа позволила относительно просто добавлять новый абзац или сфокусироваться на какой-нибудь части текста и внести в нее изменения.
Поскольку как вJavaScript, так и в РНР поддерживаются многие элементы синтаксиса структурного программирования, используемые в языке программирования С, эти два языка очень похожи друг на друга. Оба они относятся к языкам высокого уровня. К примеру, у них весьма слабая типизация, позволяющая легко приводить переменную к новому типу данных всего лишь за счет применения ее в новом контексте.
После знакомства с РНР язык JavaScript должен восприниматься еще проще. И его изучение принесет вам несомненное удовольствие, поскольку этот язык является основой технологии Web 2.0 Ajax, которая предоставляет гибко подстраивающийся пользовательский интерфейс, востребованный в наши дни опытными веб-пользователями.
JavaScript и текст HTML
JavaScript является языком сценариев, который работает исключительно на стороне клиента внутри веб-браузера. Для вызова этого языка его код помещается между открывающим и закрывающим HTML-тегами <script> и </script>.
Типовой документ Hello World, созданный на HTML 4.01 с применением JavaScript, может иметь вид, показанный в примере 13.1.
Пример 13.1. Фраза Hello World, отображаемая с помощью JavaScript
<html>
<head><ti11e>Hel1 о World</ti11e></head>
<body>
<script type=,,text/ja\rascript"> document.writeГ Hell о World") </script> <noscript>
Ваш браузер не поддерживает JavaScript, или его поддержка отключена </noscript>
</body>
</html>
Вам могут встретиться веб-страницы, в которых используется не рекомендуемый в наши дни HTML-тег <script language="javascript">. В данном примере применяется более современный и предпочтительный тег <script type="textyjavascript">.
Внутри тегов <script> находится всего одна строка кода JavaScript, в которой используется команда document .write, являющаяся эквивалентом РНР-команды echo или команды print. Как и следовало ожидать, она просто выводит предоставленную ей строку в текущий документ при его отображении на экране.
Можно было также заметить, что, в отличие от РНР, в этой команде отсутствует замыкающая точка с запятой (;). Причина в том, что в JavaScript действием, эквивалентным действию точки с запятой, обладает символ новой строки. Тем не менее, если потребуется разместить в одной строке более одной инструкции, то после каждой инструкции, кроме последней, нужно ставить точку с запятой. Разумеется, при желании можете ставить точку с запятой после каждой инструкции. Это нисколько не помешает работе JavaScript.
В этом примере следует также обратить внимание на пару тегов <noscript> и </noscript>. Они используются в том случае, когда вам хочется предоставить альтернативный HTML пользователям, на чьих браузерахJavaScript не поддерживается или отключен. Эти теги применяются по вашему усмотрению и не являются обязательными, но будет лучше, если вы ими воспользуетесь, поскольку предоставить альтернативу в виде статичного HTML тем операциям, для которых применяется JavaScript, обычно не составляет большого труда. Но в последующих примерах, приводимых в данной книге, теги <noscript> будут опускаться, поскольку основное внимание будет уделяться тому, что можно сделать с использованием JavaScript, а не тому, что можно сделать без него.
Когда будет загружен код примера 13.1, на экран веб-браузера с включенным JavaScript будет выведен следующий текст (рис. 13.1):
Hello World
А те браузеры, на которых JavaScript отключен, выведут следующее сообщение (рис. 13.2):
Ваш браузер не поддерживает JavaScript, или его поддержка отключена
■ ‘ Mozilla Firefox
Файл Правка Вид Журнал Закладки Инструменты Справка
■- ▼ ' С X Ш ГСЦ» I 0 htp://localhost/wreb/helloworld^^
Hello World
Готово
Рис. 13.1. Включенный и работающий JavaScript
Рис. 13.2. JavaScript отключенРис. 13.2. JavaScript отключен
Рис. 13.2. JavaScript отключен
Рис. 13.2. JavaScript отключен
Использование сценариев в заголовке документа
Сценарий можно вставить не только в тело документа, но и в его раздел <head>, являющийся идеальным местом, если нужно выполнить сценарий при загрузке страницы. Присутствие в этом разделе какого-нибудь очень важного кода и функций обеспечивает также их немедленную готовность к использованию в других разделах, имеющих сценарии, в том документе, который зависит от их применения.
Другой причиной для вставки сценария в заголовок документа может быть способность JavaScript записывать в раздел <head> такие элементы, как мета-теги, поскольку то место, куда вставляется сценарий, становится по умолчанию частью документа, куда он осуществляет вывод информации.
Устаревшие и нестандартные браузеры
Если нужно поддерживать браузеры, не допускающие выполнения сценариев, следует воспользоваться HTML-тегами комментариев (<! - - и - ->), препятствующими их встрече с кодом сценария, который они не должны видеть. В примере 13.2 показано, как эти теги добавляются к коду сценария.
Пример 13.2. Пример «Hello World», измененный в расчете на использование браузеров, не поддерживающих JavaScript
<html>
<head><ti tle>Hel1 о World</titl e></head>
<body>
<script type="text/javascript"><!-- document.write("Hello World") // --></script>
^/body>
</html>
В данном примере открывающий HTML-тег комментария (<!--) был добавлен сразу же после открывающего тега <script . . . >, а тег, закрывающий комментарий, - непосредственно перед тегом </scri pt>, который закрывает сценарий.
Двойной прямой слеш (//) используется в JavaScript, чтобы показать, что вся остальная строка является комментарием. Он присутствует здесь для того, чтобы браузеры, поддерживающиеJavaScript, проигнорировали следующий за ним тег - ->, а браузеры, не поддерживающие JavaScript, проигнорировали идущие вначале символы // и обработали тег -->, закрывая тем самым HTML-комментарий.
Хотя такое решение имеет не самый изящный вид, вам при желании поддерживать устаревшие или нестандартные браузеры нужно лишь запомнить используемые в нем две строки, в которые помещается код JavaScript:
<script type="text/javascript"><!--
(Здесь должен быть ваш код JavaScript...)
// --></script>
Разумеется, пройдет еще несколько лет, и эти комментарии станут не нужны для любых выпускаемых браузеров.
Есть еще два языка сценариев: VBScript, разработанный корпорацией Microsoft и основанный н**' J ■ « на языке программирования Visual Basic, и Tel, представляющий собой язык для быстрой раз—3?* работки прототипов. Их можно вызвать тем же способом, что и JavaScript, за исключением того, что при объявлении типа нужно указывать соответственно text/vbscript и text/tcl. Язык VBScript работает только в Internet Explorer, его использование в других браузерах требует загрузки дополнительного модуля. Для применения Tel дополнительный модуль нужен всегда. Поэтому оба этих языка считаются нестандартными и в данной книге не рассматриваются.
Включение файлов JavaScript
В дополнение к внесению кодаJavaScript непосредственно в HTML-документы вы можете включать в них файлы с кодом JavaScript или со своего веб-сайта, или из любого места в Интернете. Для этого используется следующий синтаксис:
<script type="text/javascript" src="script.js"></script>
А для извлечения файла из Интернета применяется этот синтаксис:
<script type*"text/javascript" src="http://someserver.com/script.js">
</script>
В самих файлах сценариев не должно быть никаких тегов <scri pt> или </scri pt>, поскольку они там не нужны: браузеру и так известно, что будет загружаться файл JavaScript. Применение этих тегов в файлах JavaScript приводит к возникновению ошибки.
& -*& -*Включение файлов сценариев является предпочтительным способом использования на вашем веб-сайте файлов JavaScript, принадлежащих сторонним производителям.
& -*
& -*
Параметры type="text/javascript" можно не указывать, поскольку все современные браузеры по умолчанию предполагают, что сценарий содержит код JavaScript.
Отладка кода JavaScript
При изучении JavaScript очень важно иметь возможность отслеживать набранный код или выявлять не связанные с ним ошибки программирования. В отличие от РНР, который отображает сообщения об ошибках в браузере, JavaScript обрабатывает сообщения об ошибках по-другому, и способ их обработки имеет свои особенности, зависящие от используемого браузера. В табл. 13.1 перечислены способы
. доступа к сообщениям об ошибках JavaScript в каждом из пяти самых распространенных браузеров.
Таблица 13.1. Доступ к сообщениям об ошибках JavaScript в различных браузерах
Браузер
Способ доступа к сообщениям об ошибках JavaScript
Apple Safari
В Safari нет консоли ошибок, включенной по умолчанию, но вы можете включить эту функцию, выбрав в меню пункты Safari ► Настройки ► Дополнения и установив флажок Показывать меню "Разработка" в строке меню. Кроме того, предпочтение можно отдать JavaScript-модулю Firebug Lite, который многие считают более простым в использовании: <script src='http ://tinyurl.com/fbl rte' >
Google Chrome
Щелкните на значке меню, который похож на страницу с загнутым углом, и выберите пункт Разработчикам ► Консоль JavaScript. Можно также воспользоваться сочетанием клавиш Ctrl+Shift+J на PC или Command+Shift+J на Мае
Microsoft Internet Explorer
Выберите команду Сервис ► Свойства обозревателя ► Дополнительно, снимите флажок Отключить отладку сценариев и установите флажок Показывать уведомление о каждой ошибке сценария
Mozilla Firefox
Выберите команду Инструменты ► Консоль ошибок или воспользуйтесь сочетанием клавиш: Ctrl+Shift+J на PC или Command+Shift+J на Мае
Opera
Выберите команду Инструменты ► Дополнительно ► Консоль ошибок
Для пользователей Mac OS X: хотя здесь и указан способ использования в Safari консоли ошибок для JavaScript, вы можете отдать предпочтение использованию Google Chrome (для Intel OS X 10.5 или более новой версии), который, на мой взгляд, предлагает разработчикам более широкие функциональные возможности.
Чтобы испытать консоль ошибок, которую вы используете, создадим сценарий, содержащий небольшую ошибку. Пример 13.3 очень похож на пример 13.1, но в нем пропущены закрывающие двойные кавычки в строке Не"! 1о World, что является довольно распространенной синтаксической ошибкой.
Пример 13.3. JavaScript-сценарий Hello World, содержащий ошибку
<html>
<head><title>Hello World</title></head>
<body>
<script type=”text/javascrip I t"> document. writeC'Hello World) </script>
</body>
</html>
Наберите этот пример и сохраните его в файле test. html, а затем вызовите в своем браузере. Он сможет лишь вывести название страницы, а в основном окне браузера будет пусто. Теперь вызовите в вашем браузере консоль ошибок и увидите сообщение о незакрытом строковом литерале (в случае использования Firefox): unterminated string literal document.writeC'Hello World)
Обратите внимание на то, что вспомогательная стрелка указывает на начало дефектной части кода. Кроме того, будет сообщено, что дефектный код был обнаружен в строке 5.
В Microsoft Internet Explorer сообщение об ошибке будет иметь вид:
Незавершенная строковая константа
Здесь нет вспомогательной стрелки, но будет выведено сообщение, что ошибка найдена в строке 5, в позиции 32.
Google Chrome выдаст сообщение о неожиданной недопустимой лексеме:
Uncaught SyntaxError: Unexpected token ILLEGAL
Будет также сообщено, что ошибка находится в строке 5, без указания ее точного местоположения.
Браузер Opera предоставит сообщение о синтаксической ошибке в строке 2 загружаемого встроенного сценария:
Syntax error while loading line 2 of inline script expected statement
document.writeC'Hello World)
Обратите внимание на то, что, в отличие от других браузеров, Opera сообщает об ошибке в строке 2 встроенного сценария, а не ссылается на номер строки всего HTML-файла.
Opera также пытается указать на начальную позицию проблемного участка, близкую к первой двойной кавычке.
И все же два браузера позволяют обнаружить точное место, где допущена ошибка. Firefox указывает на открывающую двойную кавычку, дающую хорошую отправную точку для поиска, a Internet Explorer сообщает об ошибке в позиции 32, что соответствует точному месту, куда следовало поставить закрывающую двойную кавычку, но из-за отсутствия стрелки, указывающей на эту позицию, для ее поиска нужно подсчитывать символы.
Как видите, в целом, наверное, только Firefox предоставляет самые простые и наиболее точные сообщения, поэтому я рекомендую этот браузер в качестве лучшего для отладки кода JavaScript.
При изучении данной книги вы узнаете о наличии некоторых существенных проблем совместимости, связанных с браузером Microsoft Internet Explorer, который по-прежнему является выбором существенной части веб-серферов. Поэтому, задумываясь о распространении своего продукта, вам перед его выпуском на рабочий сервер следует тестировать свои программы в различных версиях этого браузера.
df мdf мВам также следует присмотреться к дополнительному модулю Firebug, разработанному для браузера Firefox (http://getfirebug.com), который пользуется большой популярностью среди разработчиков JavaScript.
df м
df м
Если вам захочется набрать приводимый далее код, чтобы испытать его в работе, не забудь- « те заключить его в теги <script> и </script>.
Использование комментариев
В силу общих наследственных черт, приобретенных у языка программирования С, языки РНР и JavaScript имеют много общего и между собой, в частности приемы комментирования кода. В первую очередь это касается однострочных комментариев:
// Это комментарий
В этой технологии используется пара прямых слешей (//), информирующая JavaScript о том, что все остальные символы должны быть проигнорированы. А затем наступает черед многострочных комментариев:
/* Это раздел
многострочного комментария,
не подвергаемого
интерпретации */
Многострочный комментарий начинается с последовательности символов /* и заканчивается символами */. Нужно лишь запомнить, что использовать вложенные многострочные комментарии не допускается, поэтому нужно убедиться в отсутствии большого закомментированного участка кода, в котором уже имеются многострочные комментарии.
Точка с запятой
В отличие от РНР, точка с запятой коду JavaScript, имеющему в строке одну инструкцию, не требуется. Поэтому следующая строка вполне имеет право на существование:
х += 10
Но при необходимости иметь в строке более одной инструкции, их нужно разделить точками с запятыми:
х += 10: у -= 5: z = 0
Последнюю точку с запятой можно опустить, поскольку последняя инструкция будет завершена символом новой строки.
В правилах использования точки с запятой есть исключения. При создании микросценариев в виде URL-закладок (bookmarklet) или при завершении инструкции ссылкой на переменную или функцию, когда первым символом расположенной ниже строки будет левая круглая или фигурная скобка, нужно не забыть добавить точку с запятой, иначе JavaScript даст сбой. Поэтому ставьте точку с запятой при любых сомнениях.
Переменные
В JavaScript нет никаких идентификационных символов переменных, таких как знак доллара ($) в РНР. Вместо этого в отношении имен переменных действуют следующие правила.
О Имена переменных могут включать только буквы a-z, A-Z, цифры 0-9, символ $ и символ подчеркивания (_).
О Никакие другие символы, включая пробелы или знаки пунктуации, использовать в именах переменных не допускается.
О Первым в имени переменной может быть символ из диапазонов a-z, A-Z, символ $ или символ подчеркивания _ (и никаких цифр).
О Имена чувствительны к регистру. Имена Count, count и COUNT принадлежат трем разным переменным.
О Ограничений на длину имени переменной не существует.
И вы наверняка обратили внимание на присутствие в этом перечне символа $. JavaScript допускает его использование, и он может быть первым символом в имени переменной или функции. Такая возможность означает, что благодаря этому можно переносить на JavaScript большие объемы кода РНР значительно быстрее, хотя я не рекомендую его применять в этом качестве.
Строковые переменные
В JavaScript значения строковых переменных должны быть заключены либо в одиночные, либо в двойные кавычки:
greeting = "Привет! " warning = 'Осторожно!'
В строку в двойных кавычках можно включить одиночную кавычку или же в строку в одинарных кавычках можно включить двойную кавычку. Но кавычка того же типа должна быть отключена путем использования символа обратного слеша:
greeting = "\"Привет!\” является приветствием"
warning = 'VОсторожной' является предупреждением'
Чтобы прочитать значение из строковой переменной, его можно присвоить другой переменной:
newstring = oldstring
Или использовать его в функции:
status = "Все системы работают успешно" document.wri te (status)
Числовые переменные
Создание числовой переменной сводится к простому присваиванию значения, как в следующих примерах:
count = 42
temperature = 98.4
Значения числовых переменных могут быть точно так же, как и значения строковых переменных, прочитаны и использованы в выражениях и функциях.
Массивы
Массивы JavaScript очень похожи на массивы в РНР тем, что они могут содержать строковые или числовые данные, а также другие массивы. Чтобы присвоить массиву значения, используется следующий синтаксис (с помощью которого в данном случае создается строковый массив):
toys = [ 'bat'. 'bal1l', 'whistle', 'puzzle', 'doll']
Для создания многомерного массива более мелкие массивы вкладываются в более крупный массив. Для создания двумерного массива цветных квадратов, расположенных на одной из сторон кубика Рубика (в котором используются следующие цвета: красный (R), зеленый (G), оранжевый (0), желтый (Y), синий (В) и белый (W), представленные прописными буквами, указанными в скобках), можно воспользоваться следующим кодом: face = [
[' R'. ’ G’, ' Y' ],
[ '1лГ , 'R', 'О'].
[ 'Y', ¥ , ’G’]
]
Предыдущий пример был отформатирован так, чтобы было понятно, что именно происходит, но его можно переписать и в следующем виде:
face = [ ['R'. 'G'. ' Y' ]. [ *W*. 'R', '0']. [' Y'. 'W. ’G' ]]
или даже так:
top =
['R\
'G' .
' Y' ]
mid =
['W' .
'R' .
‘O']
bot =
[ ’ Y' .
'W' ,
•G']
face =
[top.
mid.
bot]
Для доступа к элементу, расположенному в этой матрице во второй сверху и в третьей слева ячейке, нужно воспользоваться следующим кодом (нужно помнить, что позиционирование элементов массива начинается с нуля):
document.wri te(face[l][2])
Эта инструкция выведет букву 0, означающую оранжевый цвет.
Массивы JavaScript являются мощной структурой, предназначенной для хранения данных, поэтому в главе 15 они рассматриваются более подробно.
Операторы
Как и в РНР, операторы в JavaScript могут использоваться в математических операциях, для внесения изменений в строки, а также в операциях сравнения и логических операциях (И, ИЛИ и т. д.). Математические операторы JavaScript во многом похожи на обычные арифметические операторы. Например, следующая инструкция выводит число 15:
document.write(13 + 2)
Все разнообразие операторов будет рассмотрено в следующих разделах.
Арифметические операторы
Арифметические операторы предназначены для осуществления математических операций. Из можно использовать для осуществления четырех основных операций (сложения, вычитания, умножения и деления), а также для нахождения модулей (остатков от деления) и инкремента (увеличения на единицу) или декремента (уменьшения на единицу) значения (табл. 13.2).
Таблица 13.2. Арифметические операторы
Оператор
Описание
Пример
+
Сложение
J + 12
-
Вычитание
j - 22
*
Умножение
1*7
/
Деление
j / 3.13
%
Деление по модулю (остаток от деления)
j% 6
++
Инкремент
++j
--
Декремент
-j
Операторы присваивания
Эти операторы используются для присваивания значений переменным. Их линейка начинается простым знаком равенства (=) и продолжается сочетаниями +=, -= и т. д. Оператор += добавляет значение, находящееся справа, к переменной, находящейся слева, вместо того чтобы целиком заменить значение переменной в левой части.
Поэтому, если изначально значение переменной count было 6, то оператор:
count += 1
установит для нее значение 7 точно так же, как и более привычный оператор присваивания:
count = count + 1
В табл. 13.3 перечислены различные операторы присваивания, доступные в JavaScript.
Таблица 13.3. Операторы присваивания
Оператор
Пример
Эквивалентен оператору
=
j = 99
j = 99
+=
j += 2
j = j + 2
+=
j += 'string'
j = j + 'string'
-=
j-= 12
j = j - 12
*=
j *= 2
j = j * 2
/=
j /= 6
j = j/6
%=
j %= 7
j = j % 7
Операторы сравнения
Операторы сравнения обычно используются с такими конструкциями, как инструкция i f, в которой требуется сравнивать два элемента. Например, может потребоваться узнать, достигло ли значение переменной, подвергаемой автоприращению, определенной величины, или меньше ли значение другой переменной установленной величины и т. д. (табл. 13.4).
Таблица 13.4. Операторы сравнения
Оператор
Описание
Пример
==
Равно
j == 42
;=
He равно
j != 17
>
Больше
j > 0
<
Меньше
j < 100
>=
Больше или равно
j >= 23
<=
Меньше или равно
j <= 13
===
Равно (и того же типа)
j === 56
!==
Не равно (и того же типа)
j !== 'I'
Логические операторы
У логических операторов JavaScript, в отличие от РНР, нет эквивалентов and и ог для &&и | | и отсутствует оператор хог (табл. 13.5).
Таблица 13.5. Логические операторы
Оператор
Описание
Пример
&&
И
j == 1 Ш к == 2
II
ИЛИ
j < 100 || j >0
!
НЕ
! (j role="strong">== k)
Инкремент и декремент переменной
Следующие используемые в РНР и уже изучавшиеся вами формы инкремента (приращения) и декремента (отрицательного приращения), осуществляемые как после операции сравнения, так и перед ней, поддерживаются также и в JavaScript:
++х
У
х += 22
У -= 3
Объединение строк
Объединение (конкатенация) строк в JavaScript осуществляется немного иначе, чем в РНР. Вместо оператора . (точка) используется знак «плюс» (+):
document.write("У вас " + messages + " сообщения.")
Если предположить, что переменной messages присвоено значение 3, эта строка кода выведет следующую информацию:
У вас 3 сообщения.
Оператор += точно так же, как и при добавлении значения к числовой переменной, позволяет добавить одну строку к другой:
name = "James" name += " Dean"
Управляющие символы
Управляющие символы, использование которых вы видели при вставке в строку кавычек, могут применяться также для вставки различных специальных символов: табуляции, новой строки и возврата каретки. В следующем примере символы табуляции используются для разметки заголовка; они включены в строку исключительно для иллюстрации использования управляющих символов, поскольку в вебстраницах существую'!* более подходящие способы разметки:
heading = "NameUAgeULocation"
В таблице 13.6 приведены управляющие символы, доступные в JavaScript.
Таблица 13.6. Управляющие символы JavaScript
Символ
Назначение
\b
Забой
\f
Перевод страницы
\n
Новая строка
\r
Возврат каретки
\t
Табуляция
V
Одиночная кавычка
\"
Двойная кавычка
\\
Обратный слеш
\xxx
Восьмеричное число в диапазоне от 000 до 377, представляющее эквивалент символа Latin-1 (например, \251 для символа ©)
\xXX
Шестнадцатеричное число в диапазоне от 00 до FF, представляющее эквивалент символа Latin-1 (например, \хА9 для символа ©)
\uXXXX
Шестнадцатеричное число в диапазоне от 0000 до FFFF, представляющее эквивалент символа Unicode (например, \u00A9 для символа ©)
Типизация переменных
Как и РНР, JavaScript относится к весьма слабо типизированным языкам; тии переменной определяется только при присваивании ей значения и может изменяться в случае появления переменной в другом контексте. Как правило, о типе переменной волноваться не приходится: язык JavaScript сам определяет, что именно вам нужно, и просто делает это.
Посмотрите на пример 13.4, в котором выполняются следующие действия.
Переменной п присваивается строковое значение 838102050, в следующей строке осуществляется вывод ее значения, а чтобы посмотреть на ее тип, используется инструкция typeof.
Переменной п присваивается значение, получаемое при перемножении чисел 12 345 и 67 890. Это значение также равно 838 102 050, но оно является числом, а не строкой. Затем определяется и выводится на экран тип переменной.
К числу п добавляется текст, и результат отображается на экране.
Пример 13.4. Установка типа переменной путем присваивания ей значения <script>
п = ’838102050'// Присваивание ’п' строкового значения
document.writeUn = ' + n + '. и иимтт гти ' + typeof п + '<Ьг />')"
п = 12345 * 67890;// Присваивание 'п' числа
document.write('n ='+ n + '. и имеет тип ' + typeof n + '<br />')
n += ’ плюс текст' //Изменение типа 'n* с числового на строковое document .wri te( 'n ='■+ n + '. и имеет тип ’ + typeof n + ’<br />'
</script>
Этот сценарий выведет следующую информацию:
п = 838102050. и имеет тип string п = 838102050. и имеет тип number п = 838102050 плюс текст, и имеет тип string
Если в отношении типа переменной есть какие-то сомнения или нужно обеспечить, чтобы переменная относилась к определенному типу, вы можете принудительно привести ее к этому типу, используя операторы, показанные в следующем примере (которые превращают строку в число и число в строку соответственно): п = ”123" n *= 1// Превращение 'п' в число
п = 123 п +=// Превращение V в строку
Или же тип переменной можно всегда определить с помощью инструкции typeof.
Функции
Как и в РНР, в JavaScript функции используются для выделения фрагментов кода, выполняющих конкретную задачу. Для создания функции ее нужно объявить, как показано в примере 13.5.
Пример 13.5. Объявление простой функции
<script>
function product(a. b)
return a*b
"'Vscript
Эта функция принимает два переданных ей параметра, перемножает их и возвращает произведение.
Глобальные переменные
К глобальным относятся переменные, определенные за пределами любых функций (или внутри функций, но без использования ключевого слова var). Они могут быть определены следующимиспособами:
а = 123var b = 456а = 123var b = 456/// гГлобальная/// гГлобальная/// гГлобальная/// гГлобальнаяобласть Евидимости область видимости
а = 123
var b = 456
а = 123
var b = 456
/// гГлобальная
/// гГлобальная
/// гГлобальная
/// гГлобальная
i f (а == 123) V var с= 789 /// ГГлобальная область видимости
Независимо от применения ключевого слова var, если переменная определена за пределами функции, она приобретает глобальную область видимости. Это означает, что к ней может быть получен доступ из любой части сценария.
Локальные переменные
Параметры, переданные функции, автоматически приобретают локальнуюобласть видимости, то есть к ним можно обращаться только из тела этой функции. Но есть одно исключение. Массивы передаются функции по ссылке, поэтому, если вы внесете изменения в любые элементы массива, переданного в качестве параметра, то элементы исходного массива также будут изменены.
Для определения локальной переменной, имеющей область видимости только внутри текущей функции и не переданной ей в качестве параметра, используется ключевое слово var. В примере 13.6 показана функция, которая создает одну переменную с глобальной и две переменные с локальными областями видимости.
Пример 13.6. Функция, создающая переменные с: глобальной и локальной областями видимости
"'■script^
function testO
{
а - 123// Глобальная область видимости
var b = 456// Локальная область видимости
if (а == 123) var ( = /89 // Локальная область видимости
'-■/script>
В РНР, чтобы проверить работоспособность установки области видимости, можно воспользоваться функцией i sset. Но в JavaScript такой функции нет, поэтому в примере 13.7 применяется инструкция typeof, возвращающая строку undefined, если переменная не определена.
Пример 13.7. Проверка области видимости переменных, определенных в функции test <script> test()
if(typeofa !=’undefined') document.writo(’a=+a+' ”<br/>')
if(typeofb !='undefined') document.write(’b=+b+'”<br/>')
if(typeofc ! ='undee^ined') document.write('t= "'+ с+ '<^brr/>')
function lestO
{
a - 123
var b = 456
if (a == 123) var с = 789
Этот сценарий выведет только одну строку:
а = ’'123"
Это свидетельствует о том, что, как и предполагалось, глобальная область видимости была определена только для одной переменной, потому что для перемен-
ных b и с установкой перед ними ключевого слова va г была задана локальная область видимости.
Если ваш браузер выведет предупреждение о том, что переменная b не определена, то, при всей своей справедливости, оно может быть проигнорировано.
Объектная модель документа
Разработчики JavaScript были очень умными людьми. Вместо того чтобы просто создать еще один язык написания сценариев (который имел бы на момент создания весьма неплохие усовершенствования), они проявили дальновидность и построили его вокруг объектной модели документа, или DOM (Document Object Model). Эта модель разбивает части HTML-документа на отдельные объекты, у каждого из которых есть собственные свойства и методыI и каждым из которых можно управлять с помощью JavaScript.
В JavaScript объекты, свойства и методы разделяются с помощью точек (это одна из причин того, что в качестве оператора объединения строк в JavaScript используется знак +, а не точка). Рассмотрим, к примеру, в качестве объекта с именем card визитную карточку. Этот объект содержит такие свойства, как имя —name, адрес — address, номер телефона — phone и т. д. В синтаксисеJavaScript эти свойства будут иметь следующий вид:
card.name
card.phone
card.address
Его методами будут функции, занимающиеся извлечением, изменением и другими действиями со свойствами. Например, для вызова метода, отображающего свойства объекта card, можно воспользоваться следующим синтаксисом:
card.display()
Взгляните на некоторые из представленных ранее в этой главе примеров и обратите внимание на те из них, в которых используется инструкция document.write. Уяснив, что JavaScript основан на работе с объектами, вы поймете, что write —это метод объекта document.
Внутри JavaScript выстраивается иерархия из родительских и дочерних объектов. Эта иерархия и называется объектной моделью документа — DOM (рис. 13.3).
На этом рисунке используются уже знакомые вам HTML-теги, иллюстрирующие родительско-дочерние взаимоотношения между различными объектами документа. Например, URL-адрес внутри ссылки является частью тела (body) HTML-документа. В JavaScript на него можно сослаться следующим образом:
url = document.links.linkname.href
Обратите внимание на то, как эта ссылка идет сверху вниз по центральному столбцу. Первая часть, document, ссылается не теги <html > и <body>, 1 i nks. 1 i nkname — натег<а .. .>, a href — на элемент href=___
i| Heading.. "!i| Heading.. "!< <hl> \*< <hl> \*<F*«- >)<F*«- >)1 <imf ... > \1 <imf ... > \ac I tionsac I tions
i
| Heading.. "!
i
| Heading.. "!
< <hl> \*
< <hl> \*
<F*«- >)
<F*«- >)
1 <imf ... > \
1 <imf ... > \
ac I tions
ac I tions
Рис. 13.3. Пример иерархии объектов DOM
Давайте превратим это в некий код HTML и в сценарий для чтения свойств ссылки. Наберите код примера 13.8 и сохраните его в файле с именем 1 i nktest. html, а затем вызовите в своем браузере.
Если в качестве основного разработочного браузера вы используете Microsoft Internet Explorer, пожалуйста, ограничьтесь только чтением этого подраздела, затем прочитайте следующий подраздел «Но не все так просто», вернитесь к данному подразделу и воспользуйтесь в примере модификацией getElementByld, рассмотренной в следующем подразделе. Без нее код примера работать не будет.
Пример 13.8. Чтение ссылки на URL-адрес с помощью JavaScript
<html>
<head>
<Ш1е>Тестирование ca>mKH</title>
</head>
<body>
<a id="mylink" href="http://mysite.com">ll^KHH</a><br />
<script>
url = document.links.mylink.href
document.write('URL-адрес - ’ + url)
</script>
</body>
</html>
Обратите внимание на краткую форму тегов script, в которой для экономии времени на набор текста был опущен параметр type="text/JavaScri pt". При желании ради проверки этого (и других примеров) можете также опустить все, кроме тегов <script> и </script>. Код этого примера выведет следующую информацию:
Щелкни
URL-адрес - http://mysite.com
Вторая строка выведенной информации появилась благодаря работе метода document.write. Обратите внимание на то, как код следует сверху вниз по дереву
документа от document к links, к mylink (идентификатору, присвоенному ссылке) и к href (значению, содержащему URL-адрес назначения).
Есть также краткая форма, работающая не менее успешно, которая начинается со значения, присвоенного атрибуту id: my 1 ink. href. Поэтому следующую строку:
url = document.links.mylink.href
можно заменить строкой
url = mylink.href
Но не все так просто
Код примера 13.8 будет великолепно работать в Safari, Firefox, Opera или Chrome, но только не в Internet Explorer, поскольку реализация языка JavaScript, созданная в Microsoft под именем J Script, имеет множество коварных отличий от общепризнанных стандартов. Добро пожаловать в мир современной веб-разработки!
А как же справиться с этой проблемой? В данном случае вместо использования дочернего объекта 1 inks, принадлежащего родительскому объекту document, который Internet Explorer отказывается воспринимать таким образом, нужно применять метод, извлекающий элемент по его идентификатору. Поэтому следующую строку:
url = document.links.mylink.href
можно заменить строкой:
url = document.getElementById('mylink') .href
и теперь сценарий будет работать на всех основных браузерах. Кстати, когда не нужно искать элемент по его идентификатору, следующая краткая форма будет работать и в Internet Explorer, как, собственно, и во всех остальных браузерах:
url = mylink.href
Еще одно использование знака $
Как уже упоминалось, символ $ разрешено использовать в именах переменных и функций JavaScript. По этой причине иногда можно встретить код, имеющий довольно странный вид:
url = $('mylink').href
Некоторые изобретательные программисты решили, что метод getElementById слишком часто применяется в JavaScript, и написали взамен него свою функцию, показанную в примере 13.9, присвоив ей имя $.
Пример 13.9. Функция, заменяющая метод getElementById
<script> function $(id)
{
return document.getElementById(id)
}
</script>
Поэтому, как только функция $ будет вставлена в ваш код, синтаксис:
$(('mylink').href
может заменить следующий код:
document.getElementById('mylink') .href
Использование DOM
На самом деле объект 1 i nks является массивом, состоящим из URL-адресов, поэтому на URL myl ink в примере 13.8 можно спокойно ссылаться во всех браузерах, используя для этого следующий код (поскольку это первая и единственная ссылка):
url = document.11nks[0].href
Если нужно узнать, сколько ссылок содержится во всем документе, можно запросить свойство length объекта 1 inks:
numlinks = document.links.length
Благодаря этому свойству можно извлечь и отобразить все имеющиеся в документе ссылки:
for (j=0 : j < document.links.length : ++j)
document.write(document.links[j].href + '<br />')
Длина —length является свойством каждого массива, а также многих других объектов. Например, можно запросить количество записей в истории вашего веббраузера:
document.wri te(hi story.1ength)
Но чтобы исключить перехват ваших веб-сайтов с помощью истории веб-браузера, в объекте hi story хранится только количество сайтов в массиве и вы не можете прочитать или записать значения, относящиеся к этим сайтам. Но вы можете заменить текущую страницу одной из тех, что хранятся в истории, если знаете, на какой позиции она там находится. Это может пригодиться в тех случаях, когда вам известны конкретные страницы, попавшие в историю при переходах с вашей страницы, или вы просто хотите вернуть браузер назад на одну или несколько страниц, что делается с помощью метода до объекта history. Например, чтобы отправить браузер назад на три страницы, нужно выдать следующую команду:
history.до(-З)
Для перехода вперед и назад на одну страницу можно воспользоваться также следующими методами:
hi story.back О
history.forwardО
Подобным способом можно заменить только что загруженный URL-адрес другим по вашему выбору:
document.location.href = 'http://google.com'
Конечно, DOM можно использовать для решения куда более широкого круга задач, чем чтение и модификация ссылок. По мере изучения следующих глав, посвященных JavaScript, знакомство с DOM и с доступом к этой модели станет еще более тесным.
Проверьте ваши знания
Какие теги используются для заключения в них кода JavaScript?
К какой части документа будет по умолчанию добавлена информация, выводимая кодом JavaScript?
Как в ваши документы может быть включен код JavaScript из другого источника?
Какая функция JavaScript является эквивалентом PHP-команд echo или pri nt?
Как можно создать комментарий в JavaScript?
Какой оператор используется в JavaScript для объединения строк?
Какое ключевое слово можно применять внутри функции JavaScript для определения переменной, имеющей локальную область видимости?
Покажите два метода, работающие на всех браузерах и позволяющие отобразить присвоенный ссылке URL-адрес на основе id этой ссылки.
Какие две команды JavaScript заставят браузер загрузить предыдущую страницу, содержащуюся в его массиве history?
Какой командой JavaScript вы воспользуетесь дляJзамены текущегосдокументаa главной страницей веб-сайта oreilly.com?
Ответы на эти вопросы можно найти в приложении А, влразделе «Ответы на вопросы главы 13».
Выражения 14 и управление процессом выполнения
сценариев в JavaScript
В предыдущей главе были изложены основы JavaScript и DOM. А теперь настало время рассмотреть порядок построения в JavaScript сложных выражений и способов управления процессом выполнения ваших сценариев с помощью условных инструкций.
Выражения
Выражения в JavaScript очень похожи на выражения в РНР. В главе 4 мы выяснили, что выражение представляет собой сочетание значений, переменных, операторов и функций, в результате вычисления которого получается значение, могущее быть числовым, строковым или булевым (вычисляемым либо как истина — true, либо как ложь — fal se).
В примере 14.1 показаны несколько простых выражений. Код каждой строки выводит буквы от а до d, за которыми следуют двоеточие и результат вычисления выражения (тег <br /> служит для перехода на новую строку и разделения выводимой информации на четыре строки).
Пример 14.1. Четыре примера булевых выражений
<script>
document.write("а: " +(42?. >■ 33) + "<br //>")■ document. writeCb: " +(91L<44) + "<br //>") document.writeC'c: " + (83 == = 22) + "<br //>") document.writeC'd: " + (4I < 17)) + ”<br //>") /script>
Этот код выведет следующую информацию:
true
false с: false d: true
Заметьте, что выражения а: и d: вычисляются как true, а выражения b: и с: — как fal se. В отличие от языка РНР (который вывел бы соответственно число 1 и пустое место), JavaScript выводит строки true и false.
В JavaScript при проверке значения на истинность или ложность все выражения вычисляются как true, за исключением следующих, которые вычисляются как fal se: самой строки fal se, 0, -0, пустой строки, nul 1, неопределенного значения (undefined) и NaN (Not a Number — «не число», понятие в вычислительной технике для недопустимых операций с числами с плавающей точкой, таких как деление на ноль).
Учтите, что, в отличие от РНР, в JavaScript значения true и false пишутся в нижнем регистре. Поэтому из следующих двух инструкций информацию будет отображать только первая, выводя на экран слово true в нижнем регистре, поскольку вторая инструкция вызовет сообщение об ошибке, где будет сказано, что переменная TRUE не определена:
if (1 == true: ccc^ent.writeC’true ') // Истина
Следует помнить, что любые фрагменты кода,;которые вам захочется набрать и опробовать,Следует помнить, что любые фрагменты кода,;которые вам захочется набрать и опробовать,if (1 == TRUE; :::^ent.write('TRUE') // Приводит к ошибке
Следует помнить, что любые фрагменты кода,;которые вам захочется набрать и опробовать,
Следует помнить, что любые фрагменты кода,;которые вам захочется набрать и опробовать,
<Л' 4 . запустив их в HTML-файле, нужно заключат* теги <script> и </script>.
«К 1
Литералы и переменные
Простейшей формой выражения является литерал, означающий нечто, вычисляемое само в себя, например, число 22 или строка «Нажмите клавишу Enter». Выражение может также быть переменной, которая вычисляется в присвоенное ей значение. И литералы, и переменные относятся к типам выражений, поскольку они возвращают значение.
В примере 14.2 показаны пять разных литералов, и все они возвращают значения, хо'Гя и разных типов.
Пример 14.2. Гят2 <script>myname = "Pete^ myage = 24 document.write;”5 document.write("3 document.write.": document.write("c document.writeГ e </script>Пример 14.2. Гят2 <script>myname = "Pete^ myage = 24 document.write;”5 document.write("3 document.write.": document.write("c document.writeГ e </script>ипов литераловипов литералов" - 42+"<brr - "Hi” + "<br- true + "<br” + myname + "<br" - 'луаде + "<br" - 42+"<brr - "Hi” + "<br- true + "<br” + myname + "<br" - 'луаде + "<br/>") // Числовой литерал/>") // Строковый литерал/>") // Литерал константы/>") // Литерал строковой переменной/>")■// Литерал числовой переменной/>") // Числовой литерал/>") // Строковый литерал/>") // Литерал константы/>") // Литерал строковой переменной/>")■// Литерал числовой переменной
Пример 14.2. Гят2 <script>
myname = "Pete^ myage = 24 document.write;”5 document.write("3 document.write.": document.write("c document.writeГ e </script>
Пример 14.2. Гят2 <script>
myname = "Pete^ myage = 24 document.write;”5 document.write("3 document.write.": document.write("c document.writeГ e </script>
ипов литералов
ипов литералов
" - 42+"<br
r - "Hi” + "<br
- true + "<br
” + myname + "<br
" - 'луаде + "<br
" - 42+"<br
r - "Hi” + "<br
- true + "<br
” + myname + "<br
" - 'луаде + "<br
/>") // Числовой литерал
/>") // Строковый литерал
/>") // Литерал константы
/>") // Литерал строковой переменной
/>")■// Литерал числовой переменной
/>") // Числовой литерал
/>") // Строковый литерал
/>") // Литерал константы
/>") // Литерал строковой переменной
/>")■// Литерал числовой переменной
Как и можно было ожидать, в выводимой информации будут показаны значения, возвращаемые всеми этими литералами:
а:
42
Ь:
Hi
с:
true
d:
Peter
е:
24
Операторы позволяют создавать более сложные выражения, вычисляемые в полезные результаты. При объединении присваивания или управляющей конструкции с выражениями получается инструкция.
В примере 14.3 показано по одной инструкции каждого вида. В первой результат выражения 366 - day_number присваивается переменной days_to_new_year, а во второй приятное сообщение выводится только в том случае, если выражение days_to_new_year < 30 вычисляется как true.
Пример 14.3. Две простые инструкции JavaScript
<script>
days_to_new_year = 366 - day_number:
if (days_to_new_year < 30) document.writeCCKopo Новый год!")
</script>
Операторы
JavaScript предлагает большое количество мощных операторов, начиная с арифметических, строковых и логических и заканчивая операторами присваивания, сравнения и т. д. (табл. 14.1).
Таблица 14.1. Типы операторов JavaScript
Оператор
Описание
Пример
Арифметический
Основные математические операции
а + b
Для работы с массивами
Работа с массивами
а + b
Присваивания
Присваивание значений
а = b + 23
Поразрядный
Обработка битов в байтах
12 А 9
Сравнения
Сравнение двух значений
а < b
Инкремента и декремента
Прибавление или вычитание единицы
а++
Логический
Булевы операции
а && b
Строковый
Объединение
а + 'строка'
Различные типы операторов воспринимают разное количество операндов.
О Унарные операторы, к примеру операторы инкремента (а++) или изменения знака числа (-а), воспринимают один операнд.
О Бинарны!е операторы, представленные основной массой операторов JavaScript (включая операторы сложения, вычитания, умножения и деления), воспринимают два операнда.
О И один трехкомпонентный (ternary) оператор, имеющий форму ? х : у, является краткой однострочной формой инструкции i f, которая выбирает одно из двух выражений на основе значения третьего выражения. Этот условный оператор воспринимает три операнда.
Приоритетность операторов
Как и в РНР, в JavaScript используется приоритетность операторов, благодаря которой некоторые операторы в выражении считаются более важными, чем другие, и поэтому вычисляются в первую очередь.
В табл. 14.2 перечислены операторы JavaScript, расположенные по уровню их приоритетности.
Таблица 14.2. Операторы JavaScript, расположенные по уровню их приоритетности (сверху вниз)
Оператор(ы)
Тип(ы)
on.
Скобки, вызов и составляющая объекта
++ —
Инкремент и декремент
+ - ~ !
Унарные, поразрядные и логические
*/%
Арифметические
+ -
Арифметические и строковые
<< >> >»
Поразрядные
<> <= >=
Сравнения
==1= === !==
Сравнения
& А |
Поразрядные
&&
Логический
II
Логический
? ;
Трехкомпонентный
= += -= *= /= %= <<= >>= >>>= &= л= 1=
Присваивания
t
Последовательного вычисления
Взаимосвязанность
Большинство операторов JavaScript обрабатываются в уравнении слева направо. Но для некоторых операторов требуется обработка справа налево. Направление обработки обусловливается взаимосвязанностью операторов.
Эта взаимосвязанность вступает в силу в отсутствие явно заданной приоритетности. Рассмотрим, например, следующие операторы присваивания, благодаря которым трем переменным присваивается значение 0:
level = score = time = 0
Это множественное присваивание становится возможным только благодаря тому, что самая правая часть выражения вычисляется первой, а затем обработка
продолжается справа налево. В табл. 14.3 перечислены операторы, имеющие взаимосвязанность справа налево.
Таблица 14.3. Операторы, имеющие взаимосвязанность справа налево
Оператор
Описание
New
Создание нового объекта
+ + --
Инкремент и декремент
+ -!
Унарные и поразрядные операции
? ;
Условный оператор
= *= /= о/о= += _= <<= >> = >>> = &= л= 1=
Присваивание
Операторы отношения
Операторы отношения проверяют значения двух операндов и возвращают логический результат, равный либо true, либо false. Существует три типа операторов отношения: операторы равенства, сравнения и логические.
Операторы равенства
Оператор равенства имеет вид == (его не следует путать с оператором присваивания =). В примере 14.4 первая инструкция присваивает значение, а вторая проверяет это значение на равенство. В данных условиях на экран ничего не будет выведено, потому что переменной month присвоено значение July и его сравнение со значением October будет неудачным.
Пример 14.4. Присваивание значения и проверка на равенство
<script>
month = "July"
if (month == "October") document.writeC'It’s the Fall")
</script>
Если два операнда выражения равенства принадлежат к разным типам данных, JavaScript приведет их к тому типу, который имеет для него наибольший смысл. Например, любые строки, полностью состоящие из цифр, при сравнении с числом будут преобразованы в числа. В примере 14.5 у переменных а и b два разных значения (одно из них является числом, а второе — строкой), и поэтому вряд ли стоило ожидать, что какая-нибудь из инструкций i f выведет результат.
Пример 14.5. Операторы равенства и тождественности
<script>
а = 3.1415927
b = "3.1415927"
if (а == b) document.writeCT')
if (a == b) document .write("2")
</script>
Тем не менее, если запустить код этого примера, вы увидите, что он выведет цифру 1, что будет означать, что первая инструкция if была вычислена как t rue.
Это произошло благодаря тому, что строковое значение переменной b сначала было временно преобразовано в число, и поэтому обе половины уравнения получили числовое значение 3.1415927.
В отличие от этого вторая инструкция if использует оператор тождественности —три знака равенства подряд, который препятствует автоматическому преобразованию типов в JavaScript. Этот означает, что в данном случае значения а и b считаются разными, поэтому на экран ничего не выводится.
Точно так же, как при принудительном задании приоритета операторов, если у вас появятся сомнения, связанные с преобразованиями типов операндов, производимыми JavaScript, то для отключения этого поведения можно воспользоваться оператором тождественности.
Операторы сравнения
Используя операторы сравнения, можно проверить не только равенство или неравенство. JavaScript предоставляет в ваше распоряжение также операторы > (больше), < (меньше), >= (больше или равно) и <= (меньше или равно). Код примера 14.6 показывает эти операторы в действии.
Пример 14.6. Четыре оператора сравнения
<script>
а = 7; b = 11
if(а>)b) document.write("абольшеbb<br //>")
if(а<)b) document.write("аменьшеbb<br //>”)
i f(a>= b) document.write("абольшекили fравноb<br//>"
if(a<= b) document.write("аменьшекили fравноb<br//>"
</script>
Если а равно 7, a b равно 11, то код этого примера выведет следующую информацию (потому что 7 меньше 11, а также меньше или равно*11):
а меньше b
а меньше или равно b
Логические операторы
Логические операторы выдают истинные или ложные результаты, поэтому их также называют булевыми. В JavaScript используются три логических оператора (табл. 14.4).
Таблица 14.4. Логические операторы JavaScript
Логический оператор
Описание
&&
(И) Возвращает истинное значение (true), если оба операнда имеют истинные значения
II
(ИЛИ) Возвращает истинное значение (true), если любой из операторов имеет истинное значение
!
(НЕ) Возвращает истинное значение (true), если операнд имеет ложное значение, или ложное значение (false), если операнд имеет истинное значение
Код примера 14.7 показывает возможности использования этих операторов, при которых возвращаются 0,1 и true.
Пример 14.7. Использование логических операторов
<script> а = 1: b = О document.write((а && b) + "<Ьг //>") document.write((а || b) + "<br //>") document.write(( !b ) + ”<br //>") </script>
Оператору &&для возвращения значения true нужно, чтобы оба операнда имели значение true. Оператору 11 для возвращения значения true необходимо, чтобы любой операнд имел значение true, а третий оператор применяет к значению переменной b операцию НЕ, превращая ее значение из 0 в true.
При использовании оператора 11 могут возникнуть непредвиденные проблемы, поскольку второй операнд не ббудетвычисляться,, еесли при вычислении Iпервого будет получен результат true. В примере14.8 функция getnext никогда не будет вызвана, если переменная finished имеет значение 1.
Пример 14.8. Инструкция, использующая оператор ||
<script>
if (finished == 1 || getnextО == 1) done = 1 </script>
Если нужно, чтобы getnext была вызвана при каждом выполнении инструкции i f, следует переписать код, как показано в примере 14.9.
Пример 14.9. Инструкция i f . . . ИЛИ. измененная для гарантированно!о вызова getnext <script> gn = getnextO if (finished == 1 || gn == 1) done - I </script>
В данном случае код функции getnext будет выполнен, и он вернет значение, которое будет сохранено в переменной gn до выполнения инструкции i f.
В табл. 14.5 показаны все допустимые варианты использования логических операторов. Следует заметить, что выражение ! true эквивалентно fa 1 se, а выражение !false эквивалентно true.
Таблица 14.5. Все логические выражения, допустимые в JavaScript
Входные данные
Операторы и результаты
А
b
&&
II
true
true
true
true
true
false
false
true
false
true
true
true
false
false
false
false
Инструкция with
Инструкция with в предыдущих главах, посвященных РНР, не встречалась, поскольку она имеется только в JavaScript. Используя эту инструкцию (если вы понимаете, что я под этим подразумеваю), можно упростить некоторые типы инструкций JavaScript, сократив множество ссылок на объект до всего одной ссылки. Предполагается, что ссылки на свойства и методы внутри блока with должны применяться к указанному объекту.
Рассмотрим код примера 14.10, в котором функция document .write нигде не ссылается на переменную string по имени.
Пример 14.10. Использование инструкции with
<script>
string = "Шустрая бурая лисица перепрыгивает через ленивую собаку"
with (string)
{
document.write("В строке " + length + " символов <br />") document.write("В верхнем регистре: " + tollpperCaseO)
}
</script>
Даже при том, что в document .write нет никакой непосредственной ссылки на string, этот код все равно справляется с выводом следующей информации:
В строке 55 символов
В верхнем регистре: ШУСТРАЯ БУРАЯ ЛИСИЦА ПЕРЕПРЫГИВАЕТ ЧЕРЕЗ ЛЕНИВУЮ СОБАКУ
Код примера работает следующим образом: интерпретаторJavaScript распознает, что свойство length и метод tollpperCaseO должны быть применены к какому-то объекту. Поскольку они указаны только сами по себе, интерпретатор предполагает, что они применяются к объекту string, указанному в инструкции with.
Использование события onerror
Рассмотрим еще одну конструкцию, недоступную в РНР. Используя либо событие onerror, либо сочетание ключевых слов try и catch, можно перехватить ошибки JavaScript и самостоятельно справиться с ними.
Событиям и называются действия, которые могут бцг> обнаружены JavaScript. Каждый элемент на веб-странице имеет конкретные события, которыми могут быть приведены в действие функции JavaScript. Например, событие щелчка — onclick, принадлежащее элементу button (кнопка), может быть настроено на вызов функции, которая будет запущена, если пользователь щелкнет на кнопке. В примере 14.11 показано, как можно воспользоваться событием onError.
Пример 14.11. Сценарий, использующий событие onerror
<script>
onerror = errorHandler
document.writ("Добро пожаловать на зтот веб-сайт!") // Преднамеренная ошибка
function errorHandler(message, ur1l. line)
{
out ="K сожалению, обнаружена ошибка.\n\n":
.•out += "Ошибка: " + message + "\n";
out += "URL: "+ url + "\n";
out += "Строка: " + line + "\n\n";
out += "Щелкните на кнопке OK для продолжения работы. п,п": alert(out): return true:
}
</script>
В первой строке сценария событию ошибки предписывается впредь использовать новую функцию errorHandler. Эта функция воспринимает три параметра: сообщение, URL-адрес и номер строки, — поэтому остается лишь отобразить все это в появляющемся окне метода alert.
Затем для проверки работы новой функции в коде сценария преднамеренно допускается ошибка: вызывается document .writ вместо document, write (не ставится последняя буква «е»). На рис. 14.1 показан результат запуска этого сценария в браузере. Подобное использование события onerror может пригодиться при отладке сценария.
Рис. 14.1. Использование события onerror с методом alert для вывода информации
Использование конструкции try—catch
Технология, в которой используются ключевые слова try и catch, считается более стандартной и гибкой, чем обработка события onerror, показанная в предыдущем
разделе. Эти ключевые слова позволяют перехватывать ошибки для избранного раздела кода, а не для всех сценариев, имеющихся в документе. Но данная технология не позволяет перехватывать синтаксические ошибки, для чего приходится применять обработку события onerror.
Конструкция try.. .catch поддерживается всеми основными браузерами и задействуется в тех случаях, когда нужно перехватить управление при наступлении определенных условий, о которых известно то, что они могут сложиться в определенной части вашего кода.
Например, в главе 17 будет рассматриваться технология Ajax, в которой используется объект XMLHttpRequest. К сожалению, в браузере Internet Explorer этот объект недоступен (хотя во всех основных браузерах он присутствует). Поэтому для обеспечения совместимости нужно применять try и catch, чтобы перехватить управление в случае отсутствия этого объекта и задействовать какие-нибудь другие технологии. Как это делается, показано в примере 14.12.
Пример 14.12. Перехват ошибки с помощью ключевых слов try и catch
<script>
try
{
request = new XMLHTTPRequestO
}
catch(err)
{
// Использование другого метода для создания запроса
// XML HTTP Request object
}
</script>
Я не хочу здесь вникать в подробности реализации отсутствующего в Internet Explorer объекта, но вы можете увидеть, как работает эта система. Со словами try и catch связано еще одно ключевое слово — f1 nal 1у. Блок кода, следующий за этим словом, выполняется всегда, независимо от того, возникла или не возникла ошибка при выполнении блока кода под ключевым словом try. Чтобы воспользоваться этим ключевым словом, нужно после инструкции catch просто добавить что-нибудь, похожее на следующий пример:
finally
{
alertC'Bbm обнаружен блок кода 'try'."))
}
Условия
Условия изменяют процесс выполнения программы. Они позволяют задавать конкретные вопросы и по-разному реагировать на полученные ответы. Существуют три типа условий, не связанных с циклами: инструкция i f, инструкция switch и оператор ?.
Инструкция if
Инструкции if уже использовались в нескольких примерах данной главы. Код внутри этой инструкции выполняется только в том случае, если заданное выражение вычисляется как true. Многострочные инструкции if заключаются в фигурные скобки, но, как и в РНР, для однострочных инструкций скобки можно опустить. Поэтому допустимы все следующие инструкции:
if (а > 100)
{
Ь=2
document.write("а больше 100")
}
if (b == 10) document.writeC'b равно 10")
Инструкция else
Если условие не было соблюдено, то с помощью инструкции el se может быть выполнен альтернативный блок кода:
if (а > 100)
{
document.write("а больше 100")
}
else
{
document .writeCa меньше или равно 100")
}
В JavaScript, в отличие от РНР, нет инструкции elseif, но ее отсутствие компенсируется возможностью использования инструкции el se, за которой следует еще одна инструкция i f, чем создается эквивалент инструкции el sei f:
if (a > 100)
{
document.write("а больше 100") ■
}
else if(a < 100)
{
document.write("a меньше 100")
}
else
{
* document.write("а равно 100")
}
За инструкцией el se после новой инструкции i f точно так же может следовать еще одна инструкция i f и т. д. Несмотря на то что в этих инструкциях использованы фигурные скобки, наличие внутри каждой пары этих скобок всего одной строки кода позволяет переписать весь пример следующим образом:
if (а > 100) document.write("а больше 100")
else if(а < 100) document.write("а меньше 100")
else document.write("а равно 100")
Инструкция switch
Инструкция switch применяется в том случае, когда одиночная переменная или результат вычисления выражения может иметь несколько значений, для каждого из которых нужно применить свою функцию. Например, в следующем коде за основу берется система меню РНР, составленная в главе 4, которая переводится в инструкции JavaScript. Эта система работает путем передачи одного строкового значения, соответствующего пожеланию пользователя, коду основного меню. Предположим, что пользователю доступны следующие варианты: Home, About, News, Login и Links — и переменной page присваивается одно из этих значений, соответствующее тому, что ввел пользователь.
Код, созданный для этого с использованием конструкции if. . .e1lseiIf..., может иметь вид, показанный в примере 14.13.
Пример 14.13. Многострочная конструкция if...e ls e if
<script>
if
(p(page =
= "Home")
document.write("Вы
выбрали
H Home")
else if
(p(page =
= "About")
document.write("Вы
выбрали
About")
else if
(p(page =
= "News")
document.write("Вы
выбрали
N News")
else if
(p(page =
= "Login")
document.write("Вы
выбрали
Login")
else if
(p(page =
= "Links")
document.writeC'Bbi
выбрали
Links")
</script>
А при использовании конструкции switch код может иметь вид, показанный в примере 14.14.
Пример 14.14. Конструкция switch
<script> switch (page)
{
case "Home": document .writer Вы выбрали Ноте") break
case "About": document. writeC Вы выбрали About") break
case "News": document, writer Вы выбрали News") break
case "Login": document .writer Вы выбрали Login") break
case "Links": document .writer Вы выбрали Links") break
}
</script>
Переменная page здесь присутствует только в самом начале инструкции swi tch. После чего совпадения проверяются командой case. При совпадении выполняется
условная инструкция. Разумеется, в настоящей программе здесь будет код для отображения страницы или для перехода на нее, а не простое сообщение пользователю о том, что он выбрал.
Прекращение работы инструкции switch
Рассматривая код примера 14.14, можно заметить, что, как и в РНР, команда break позволяет сценарию прекратить работу инструкции switch в случае соблюдения условия. Если вы не хотите, чтобы выполнение всех инструкций, начиная со следующей case, продолжилось, не забудьте поставить команду break.
Действие по умолчанию
С помощью ключевого слова defau1lt для инструкции switch можно определить действие по умолчанию на тот случай, когда не будет выполнено ни одно из условий. В примере 14.15 показан фрагмент кода, который может быть вставлен в код примера 14.14.
Пример 14.15. Инструкция default, предназначенная для.кода примера 14.14
default: document.write("Нераспознанный выбор") break
Оператор ?
Трехкомпонентный оператор, состоящий из вопросительного знака (?), применяемого в сочетании с символом двоеточия (:), является упрощенной формой теста if. . .el se. Используя этот оператор, можно поставить за вычисляемым выражением знак ? и код, выполняемый в том случае, если выражение вычисляется как true. После этого кода ставится знак : и код, который будет выполнен, если выражение будет вычислено как fal se.
В примере 14.16 показан трехкомпонентный оператор, используемый для вывода сообщения о том, что значение переменной а меньше или равно 5, или для вывода другого сообщения, если это утверждение не соответствует действительности.
Пример 14.16. Использование трехкомпонентного оператора
<script>
document.write( a <= 5 ?
"а меньше или равно 5" :
"а больше 5"
)
</script>
Для более понятного представления этот оператор был разбит на несколько строк, но вы, скорее всего, воспользуетесь его однострочной формой:
size = а <= 5 ? "короткий" : "длинный"
Циклы
При рассмотрении циклов нам опять встретится множество параллелей между JavaScript и РНР. В обоих языках поддерживаются циклы whi 1 е, do... whi 1 е и for.
Циклы while
В JavaScript в циклах whi 1 е сначала проверяется значение выражения, а выполнение инструкций внутри цикла начинается лишь в том случае, если выражение вычисляется как true. Если выражение вычисляется как fal se, управление переходит к следующей инструкции Javascript (если таковая имеется).
После завершения итерации цикла выражение опять проверяется на истинность, и процесс продолжается до тех пор, пока не наступит момент, когда выражение будет вычислено как false, или пока выполнение не будет остановлено по какой- нибудь другой причине. Этот цикл показан в примере 14.17.
Пример 14.17. Цикл while
<script>
counter=0
while (counter < 5)
{
document.writeCCneTHHK: " + counter + "<br />") ++counter
}
</script>
Этот сценарий выведет следующую информацию:
Счетчик: О
Счетчик: 1
Счетчик: 2
Счетчик: 3
Счетчик: 4
Если бы переменная counter не увеличивалась на единицу внутри цикла, то вполне возможно, что некоторые браузеры перестали бы откликаться из-за входа в бесконечный цикл и работу со страницей было бы трудно остановить даже с помощью клавиши Esc или кнопки остановки загрузки страницы. Поэтому к циклам в JavaScript нужно относиться с большой осторожностью.
Циклы do...while
Когда нужен цикл, в котором еще до того, как будет проверено выражение, должна пройти хотя бы одна итерация, используется цикл do... whi 1е, который похож на цикл whi 1 е, за исключением того, что проверка выражения осуществляется только после каждой итерации цикла. Поэтому для вывода первых семи результатов таблицы умножения на 7 можно воспользоваться кодом, показанным в примере 14.18.
Пример 14.18. Цикл do...while
<script> count = 1 do
{
document.write(count + " умножить на 7 равно ” + count * 7 + "<br />") } while (++count <= 7)
</script>
Как и ожидалось, этот цикл выведет следующую информацию:
1 умножить на 7 равно 7
2 умножить на 7 равно 14
3 умножить на 7 равно 21
4 умножить на 7 равно 28
5 умножить на 7 равно 35
6 умножить на 7 равно 42
7 умножить на 7 равно 49
ЦИКЛЫ for
Цикл for объединяет все лучшие качества организации цикла в одной конструкции, которая позволяет передать каждой инструкции три параметра:
О выражение инициализации;
Q выражение условия;
О выражение модификации.
Эти параметры отделяются друг от друга точками с запятыми: for (выражение1; выражение2: выражениеЗ). С началом первой итерации цикла выполняется выражение инициализации. Для кода вывода таблицы умножения на 7 переменная count будет инициализирована значением 1. Затем после каждого прохождения цикла будет проверено выражение условия (в данном случае count <= 7), и новое вхождение в цикл произойдет только в том случае, если выражение условия будет вычислено как true. И в завершение в конце каждой итерации будет вычислено выражение модификации. В случае с таблицей умножения на 7 значение переменной count увеличивается на единицу. В примере 14.19 показан код такого цикла.
Пример 14.19. Использование цикла for
<script>;
for (count = 1 ; count <= 7 ; ++count)
{
document.write(count + " умножить на 7 равно " + count * 7 + "<br />");
}
</script>
Как и в PHP, в первом параметре цикла for можно присваивать значения сразу нескольким переменным, разделяя выражения запятыми:
for (i = 1. j = 1 : i < 10 : i++)
Точно так же в последнем параметре можно осуществлять сразу несколько модификаций:
for (i = 1 : i < 10 : i++. --j)
Или можно одновременно делать и то и другое:
for (i = 1. j = 1 ; i < 10 : i++, --j)
Прекращение работы цикла
Команду break, о важности использования которой в инструкции switch вы уже знаете, можно применять и внутри циклов for. Например, она может пригодиться при поиске совпадений определенного вида. Как только совпадение будет найдено, продолжение поиска станет пустой тратой времени и заставит вашего посетителя ждать его завершения. Использование команды break показано в примере 14.20.
Пример 14.20. Использование команды break в цикле for
<script>
haystack = new ArrayO haystack[17] = "Иголка”
for (j = 0 ; j <* 20 ; ++j)
(
if (haystack[j] == "Иголка")
{ document.write("<br />- найдена в элементе " + j) ) break
}
else document.write(j + ". ")
}
</script>
Этот сценарий выводит следующую информацию:
0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16.
Fнайдена Вв3элементе 1 17
Инструкция continue
Иногда нужно не выйти из цикла, а пропустить выполнение тех инструкций, которые остались в данной итерации. В таких случаях можно воспользоваться командой continue. Ее применение показано в примере 14.21.
Пример 14.21. Использование команды continue в цикле for
<script>
haystack = new ArrayO
haystack[4] = "Иголка"
haystack[ll] = "Иголка"
haystack[17] = "Иголка"
for (j = 0 ; j < 20 ; ++j)
if (haystack[j} == "Иголка")
{
document.write("<br />- найдена в элементе " + j + "<br />") continue
document.write(j + ". ")
}
</script>
Обратите внимание на то, что второй вызов метода document write не нужно помещать в инструкцию else (как было в предыдущем примере), поскольку если будет найдено совпадение, то в результате выполнения команды continue данный вызов будет пропущен. Этот сценарий выводит следующую информацию:
0: 1. 2. 3.
найдена в элементе 4
6. 7. 8. 9, 10.
- найдена в элементе 11
13. 14. 15. 16.
найдена в элементе 17 18. 19.
Явное преобразование типов
В отличие от РНР, в JavaScript нет явного преобразования типов, осуществляемого с помощью операторов (int) или (float). Когда нужно, чтобы значение имело определенный тип данных, используется одна из встроенных функций JavaScript, показанных в табл. 14.6.
Таблица 14.6. Функции изменения типа, используемые в JavaScript
Преобразование в тип данных
Используемая функция
Int, Integer
parselnt()
Bool, Boolean
BooleanQ
Float, Double, Real
parseFloat()
String
String()
Array
splitQ
Например, чтобы преобразовать число с плавающей точкой в целое число, можно использовать следующий код (который выводит значение 3):
п = 3.1415927
i = parselnt(n)
document.write(i)
Или можно воспользоваться составной формой:
document.write(parselnt(3.1415927))
На этом рассмотрение выражений и способов управления процессом выполнения сценариев завершается. В следующей главе будет рассмотрено использование в JavaScript функций, объектов и массивов.
Проверьте ваши знания
Чем отличается обработка булевых значений в РНР от их обработки в JavaScript?
В чем разница между унарными, бинарными и трехкомпонентными операторами?
Как лучше всего принудительно установить собственный приоритет для оператора?
В каком случае следует использовать оператор тождественности (===)?
Какие две формы выражений считаются самыми простыми?
Назовите три типа условных инструкций.
Как в инструкциях i f и whi 1 е интерпретируются условные выражения, в которых используются данные, относящиеся к разным типам?
Почему цикл for считается мощнее цикла whi 1 е?
Для чего предназначена инструкция wi th?
КаккможноозаставитьIJavaScriptitизящнооразобратьсяяссошибкой йввслучаееее е возникновения?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 14».
4 г* Функции, объекты
ХЭ J и массивы JavaScript
В JavaScript предоставляется практически такой же доступ к функциям и объектам, как и в РНР. Язык JavaScript фактически основан на объектах, поскольку, как вы уже поняли, у него есть доступ к DOM —модели, которая реализует доступ к каждому элементу HTML-документа, позволяя работать с ним как с объектом. Способы применения функций и объектов, а также используемый для этого синтаксис очень похожи на все то, что мы видели в РНР, поэтому при изучении этого материала, а также при углубленном рассмотрении массивов JavaScript вы будете чувствовать себя вполне уверенно.
Функции JavaScript
В JavaScript наряду с доступом к десяткам встроенных функций (или методов), среди которых и метод write, который, как мы уже видели, использовался в вызовах document .write, можно создавать и собственные функции. Как только появляется какой-нибудь непростой фрагмент кода с перспективами на многократное использование, он становится кандидатом на оформление в виде функции.
Определение функции
В общем виде синтаксис функции выглядит следующим образом: function имя_функции ([параметр [. ...]]])
{
инструкции
}
Из первой строки синтаксиса следует, что:
О определение начинается со слова function;
О следующее за этим словом имя должно начинаться с буквы или символа подчеркивания, за которым следует любое количество букв, цифр, символов доллара или подчеркивания;
О необходимо использовать скобки;
О дополнительно могут применяться один или несколько параметров, разделенных запятыми (о чем свидетельствуют квадратные скобки, не являющиеся частью синтаксиса функции).
Имена функций чувствительны к регистру букв, поэтому строки get Input, GET I NPUT и getinput ссылаются на разные функции.
В JavaScript для имен функций действует общепринятое соглашение: первая буква каждого слова в имени, за исключением первой буквы всего имени, должна быть прописной. Поэтому в приведенных примерах имен предпочтение следует отдать имени getinput, имеющему формат, используемый большинством программистов. Это соглашение часто называют bumpyCaps (неровностями из прописных букв) или camelCase (как горбы у верблюда).
Инструкции, которые будут выполняться после вызова функции, начинаются с открывающей фигурной скобки, а составляющая ей пару закрывающая фигурная скобка должна завершать перечень этих инструкций. Среди инструкций обязаны присутствовать одна или несколько инструкций return, которые заставляют функцию прекратить выполнение и вернуть управление вызвавшему ее коду. Если к инструкции return прилагается какое-нибудь значение, то вызывающий код может его извлечь.
Массив аргументов
Составной частью каждой функции является массив аргументов — arguments. Благодаря ему можно определить количество переменных, переданных функции, и понять, что они собой представляют. Рассмотрим, например, функцию displayltems. Один из способов ее создания показан в примере 15.1.
Пример 15.1. Определение функции
<script>
displayltems("Собака". "Кошка”. "Пони", "Хомяк". "Черепаха")
function di spl ay Items (vl. м2. v3', v4. v5) {
document.write(vl + "<br />")
document.write(v2 + "<br />")
document.write(v3 + "<br />")
document.write(v4 + "<br />") document.write(v5 + "<br />")
}
</script>
После вызова этого сценария на экране браузера отобразится следующая информация:
Собака Кошка “они *:омяк -ерепаха
А что делать, если функции нужно будет передать больше пяти аргументов? К тому же использование вместо цикла многократного вызова метода document. wri te считается слишком расточительным приемом программирования. К счастью, массив ^gjinents позволяет приспособиться к обработке любого количества аргументов.
В примере 15.2 показана возможность использования этого массива для придания предыдущему примеру более рациональной формы.
Пример 15.2. Модификация функции для использования массива аргументов
<script> function displayltems() {
for (j = 0 : j < displayItems.arguments.length ; ++j) document.write(displayltems.arguments[j] + "<br />") }
</script>
Обратите внимание на использование свойства 1 ength, которое уже встречалось в предыдущей главе, а также на то, как с помощью переменной j, являющейся индексным смещением внутри массива, осуществляется ссылка на элементы di spl ay Items .arguments. Поскольку тело цикла for состоит всего из одной инструкции, я решил не заключать его в фигурные скобки, чтобы не загромождать код функции.
Теперь благодаря данной технологии у вас есть функция, способная принимать любое количество аргументов и делать с каждым аргументом все, что вам захочется.
Возвращение значения
Функции используются не только для отображения информации. Чаще всего они применяются для выполнения вычислений или обработки данных с возвращением полученного результата. Функция fi xNames, показанная в примере 15.3, задействует массив arguments (рассмотренный в предыдущем пункте) для приема переданной ей последовательности строк и возвращения всех этих строк в виде одной строки. Слово fi х (исправление) в ее имени означает, что она переводит каждый символ в аргументах в нижний регистр, делая исключение для первой буквы каждого аргумента, которую она превращает в прописную.
Пример 15.3. Приведение в порядок полного названия
<script>
document.wnte(fixNames(”the ". "DALLAS". "CowBoys"))
function fixNames0) { var s = ""
for (j = 0 ; j < fixNames.arguments.length ; ++j)
s += fixNames.arguments[j].charAt(0).tollpperCaseO + fixNames.arguments[j].substr(l).toLowerCaseO + ” "
return s.substr(0, s.length-1) }
</script>
К примеру, если вызвать эту функцию с параметрами the, DALLAS и CowBoys, то она вернет строку The Dal 1 as Cowboys. Проанализируем работу этой функции. Сначала она инициализирует временную (и локальную) переменную s, присваивая ей значение пустой строки. Затем с помощью цикла for осуществляется последовательный перебор каждого переданного параметра с выделением его первой буквы с помощью метода charAt и переводом ее в верхний регистр с помощью метода tollpperCase. Все методы, показанные в этом примере, встроены в JavaScript и доступны по умолчанию.
После этого для извлечения оставшейся части каждой строки используется метод substr, а для перевода букв этой части строки в нижний регистр — метод toLowerCase. Если здесь применить полную версию вызова метода substr, то в ней вторым аргументом нужно указать, из какого количества символов будет состоять извлекаемая подстрока:
substr(l. (arguments[j].length) - 1 )
Иными словами, в этом вызове метода substr говорится следующее: «Начни с символа, который находится в позиции 1 (это второй символ), и верни оставшуюся часть строки (равную длине строки — 1 ength за вычетом одного символа)». Но что особенно приятно, метод substr заранее предполагает, что если второй аргумент опущен, то вам нужна вся оставшаяся часть строки.
После того как весь аргумент будет переделан для получения нужного результата, к нему добавляется пробел и результат присоединяется к значению временной переменной s.
На завершающей стадии к содержимому переменной s опять применяется метод substr. Поскольку последний пробел нам не нужен, мы используем метод substr, чтобы вернуть всю строку, за исключением ее последнего символа.
Этот пример особенно интересен тем, что в нем показано использование в одном выражении сразу нескольких свойств и методов, например:
fi xNames.a rguments[j ] .substr(1).toLowerCa se()
Чтобы понять суть этой инструкции, ее нужно мысленно разделить на части, используя в качестве разделителей точки. JavaScript вычисляет эти элементы инструкции слева направо.
Берется имя функции: fi xNames.
Из массива аргументов этой функции извлекается элемент j.
К извлеченному элементу применяется метод substr с параметром 1. Благодаря этому следующей части выражения будет передан весь извлеченный элемент, за исключением первого символа.
К той строке, которая только что была передана, применяется метод toLowerCase.
Такие построения часто называют составлением цепочки мет I одов. Если, к примеру, приведенному здесь выражению передать строку mixedCASE, то она пройдет через следующие преобразования:
—XxedCase
' xedCase
*‘edcase
И последнее напоминание: созданная внутри функции переменная s имеет локальную область видимости и не может быть доступна за ее пределами. Возвращая s с помощью инструкции return, мы делаем ее значение доступным вызывавшему функцию коду, который может его сохранить или использовать любым другим способом.
Но с завершением работы функции сама переменная s исчезает. Хотя мы можем заставить функцию работать и с глобальными переменными (что иногда просто необходимо), все-таки лучше просто вернуть те значения, которые нужно сохранить, и дать возможность JavaScript избавиться от всех остальных переменных, которые использовались функцией.
Возвращение массива
В примере 15.3 функция возвращает только один параметр. А что делать, если нужно вернуть сразу несколько параметров? Решить задачу поможет возвращение массива, показанное в примере 15.4.
Пример 15.4. Возвращение массива значений
<script>
words = fixNames("the", "DALLAS”. "CowBoys")
for (j = 0 : j < words.length ; ++j) document.write(words[j] + "<br />")
function fixNamesO {
var s = new ArrayO
for (j = 0 : j < fixNames.arguments.length ; ++j)
s[j] = fixNames.arguments[j].charAt(0).toUpperCase() + fixNames.arguments[j].substr(l).toLowerCaseO
return s
}
</script>
Здесь переменная words автоматически определяется в виде массива и заполняется результатами, возвращенными вызовом функции fixNames. Затем цикл for осуществляет последовательный перебор элементов массива, отображая каждый элемент.
Что касается функции fi xNames, то она практически идентична использованной в примере 15.3, за исключением того, что переменная s теперь является массивом, возвращаемым с помощью инструкции return.
Эта функция позволяет извлекать отдельные параметры из возвращенных ею элементов, например, как в следующем коде (который выводит строку The Cowboys):v.
words = fixNamesC’the”, "DALLAS”, "CowBoys") document.write(words[0] + " " + words[2])
Объекты JavaScript
По сравнению с переменными, которые в каждый конкретный момент могут содержать только одно значение, объекты JavaScript — это значительный шаг вперед, поскольку в них могут содержаться несколько значений и даже функций. Объект группирует вместе данные и функции для работы с ними.
Объявление класса
При создании сценария, в котором используются объекты, необходимо спроектировать структуру из данных и кода, называемую классом. Каждый новый объект, основанный на конкретном классе, называется экземпляром класса. Вам уже известно, что данные, связанные с объектом, называются его свойствами, а функции, которые им используются, называются методами.
Посмотрим, как объявляется класс для объекта по имени User, который будет содержать сведения о текущем пользователе. Для создания класса нужно просто создать функцию с именем этого класса.
Эта функция может воспринимать аргументы (позже будет показано, как она вызывается) и создавать свойства и методы для объектов класса. Такая функция называется конструктIором.
В примере 15.5 показан конструктор для класса User, имеющий три свойства: имя - forename, пользовательское имя —username и пароль —password. В классе также определяется метод демонстрации сведений о пользователе - showUser.
Пример 15.5. Объявление класса User и его метода
<script.>-
function User(forename, username, password)
this.forename = forename this.username = username this.password = password
this. showUser = functionO
{
document.writeC'l/lMfl: " + this.forename + "<br />") document.write("Пользовательское имя: " + this.username + "<br- />") document.write("Пароль: " + this.password + "<br />")
1
script>
От ранее рассмотренных эту функцию отличают две особенности.
Э Она ссылается на объект по имени this. Когда программа благодаря запуску этой функции создает экземпляр класса User, объект thi s является ссылкой на создаваемый экземпляр. Одна и та же функция может быть многократно вызвана с разными аргументами, всякий раз создавая новый экземпляр класса User с разными значениями для свойств forename и т. д.
0 Внутри этой функции создается новая функция по имени showUser. Здесь показан новый, усложненный синтаксис. Его задача - привязать showUser к классу User. Таким образом, showUser становится методом класса user.
Здесь используется соглашение о выборе имен, согласно которому все свойства получают имена, состоящие из букв в нижнем регистре, а в имени метода в соответствии с упомянутым в данной главе соглашением bumpyCaps есть по крайней мере одна прописная буква.
В примере 15.5 соблюдается рекомендуемый способ создания конструктора класса, который заключается в том, что методы включаются в функции конструктора. Но в примере 15.6 показано, что можно также ссылаться и на те функции, которые определены за пределами конструктора.
Пример 15.6. Раздельное объявление класса и метода
<script>
function User(forename, username, password)
{
this.forename = forename
this.username = username
this.password = password this.showUser = showUser 1
function showUserO
{
document.writeC'HMfl: " + this.forename + "<b^'
document .wri te (,,Пoльзoвaтeльcкoe имя: " + this, username + ”<br />") document.write("Пароль: " + this.password + ”<br >")
}
</script>
Эта форма объявления класса показана с расчетом на то, что вам наверняка придется сталкиваться с использованием кода, созданного другими программистами.
Создание объекта
Для создания экземпляра класса User можно воспользоваться следующей инструкцией:
de I tails = new User("Wolfgang”. "w.a.mozart". "composer")
Или же можно создать пустой объект:
details = new UserО
а затем наполнить его содержимым:
details.forename = "Wolfgang"
detail s.username = "w.a.mozart”
detaiIs.password = "composer"
К объекту также можно добавлять новые свойства:
details.greeting = "Привет"
Проверить работу только что добавленного свойства можно с помощью следующей инструкции:
document.write(detai1s .greeti ng)
Доступ к объектам
Для доступа к объекту можно сослаться на его свойства, как показано в следующих не связанных друг с другом примерах инструкций:
name = de I taiil s.forename
if (details.username == "Admin") loginAsAdmin()
А для доступа к методу showUser, принадлежащему объекту класса User, нужно воспользоваться следующим синтаксисом, в котором используется уже созданный и заполненный данными объект detai 1s:
details.showUserO
В соответствии с ранее предоставленными объекту данными этот код отобразит следующую информацию:
Имя: Wolfgang
Пользовательское имя: w.a.mozart
Пароль: composer
Ключевое слово prototype
Использование ключевого слова prototype позволяет добиться существенной экономии оперативной памяти. Каждый экземпляр класса User будет содержать три свойства и один метод. Поэтому, если в памяти содержится тысяча таких объектов, метод showUser также будет растиражирован тысячу раз. Но, поскольку в каждом экземпляре присутствует один и тот же метод, можно предписать новому объекту ссылаться на единственный экземпляр этого метода и не создавать его копию. Итак, вместо использования в конструкторе класса строки кода:
this. showUser = functionO
можно воспользоваться следующей строкой:
User, prototype. showUser = functionO
Код обновленного конструктора показан в примере 15.7.
Пример 15.7. Объявление класса с использованием для метода ключевого слова prototype
<script>
function User(forename. username, password)
this.forename = forename
this.username = username
this.password = password
User, prototype, showllser = functionO
{
document.write("Имя: " + this.forename + ”<br />") document^гНеСПользовательское имя: " + this.username + "<br />") document.write(,,Пapoлb: " + this.password + "<br />")
}
}
</script>
Этот код работает благодаря тому, что у всех функций имеется свойство по имени prototype, разработанное для хранения свойств и методов, не тиражируемых в каждом объекте, создаваемом на основе класса. Вместо этого они передаются объектам данного класса по ссылке.
Это означает, что свойства или методы prototype могут быть добавлены в любое время и они будут унаследованы всеми объектами (даже теми, которые уже были созданы), что можно проиллюстрировать следующими инструкциями:
User.prototype.greeting = "Привет" document,write(detaiIs.greeting)
Первая инструкция добавляет к классу User нрототинное свойство prototype, greeti ng, имеющее значение Привет. Во второй строке уже созданный объект detai 1 s вполне корректно отображает это новое свойство.
Можно также добавлять к классу методы или вносить в них изменения, как показано в следующих инструкциях:
User.prototype.showUser = functionO { document.write("Имя " + this.forename + " Пользователь " + this.username + " Пароль " + this.password) } detaiIs.showUser()
Эти строки можно поместить в свой сценарий, в инструкцию условия (например, в if), чтобы они запускались только в том случае, когда действия пользователя наталкивают на принятие решения о применении другого метода showUser. После запуска этих строк кода даже для уже созданного объекта detai Is при всех последующих вызовах метода detai 1 s . showUser будет запускаться новая версия, а старое определение showUser будет стерто.
Статические методы и свойства
При изучении объектов РНР вы узнали, что у классов могут быть статические свойства и методы, а также свойства и методы, связанные с конкретным экземпляром класса. JavaScript также поддерживает статические свойства и методы, которые легко и просто могут сохраняться в принадлежащие классу прототипы и извлекаться из них. Следующие инструкции устанавливают в класс User и считывают из него статическую строку:
User.prototype.greeting = "Привет”
document.wri te(User.prototype.greeti ng)
Расширение объектов JavaScript
Ключевое слово prototype позволяет даже добавлять функциональные возможности встроенным объектам. Предположим, к примеру, что нужно добавить возможность замены всех пробелов в строке неразрываемыми пробелами, чтобы избежать переноса ее части на новую строку. Это можно сделать добавлением к имеющемуся в JavaScript определению исходного объекта String прототипно- го метода:
String.prototype.nbsp =
function О { return this.replace(/ /g. ’ :‘) }
В коде этого метода для поиска всех одиночных пробелов и замены их строкой  : используются метод replace и регулярное выражение (см. главу 16). Если после запуска этого кода будет введена следующая команда:
document.write("Шустрая бурая лиса",nbsp())
то в результате ее работы будет выведена следующая строка: Шустрая&пЬ$р;бурая&пЬ sp: лиса. Посмотрите также на метод, который можно добавить для удаления всех пробелов, с которых начинается и которыми заканчивается строка (в нем опять используется регулярное выражение):
String.prototype.trim =
function О { return this.replace(/^\s+|\s+$/g. ’ ’) }
Если выдать следующую инструкцию, то на выходе будет получена строка Пожалуйста. избавьте меня от лишних пробелов (то есть из нее будут удалены все начальные и замыкающие пробелы):
document-.writer Пожалуйста, избавьте меня от лишних пробелов ".trimO)
Если разбить выражение на составные части, то два символа / помечают начало и конец выражения, а завершающий символ g задает глобальный поиск. Внутри выражения его часть A\s+ задает поиск одного или нескольких пробельных символов применительно к началу строки, в которой ведется поиск, а его часть \s+$ задает поиск одного или нескольких пробельных символов применительно к концу строки, в которой ведется поиск. Расположенный в середине символ | служит разделителем альтернативных вариантов регулярного выражения.
В результате при соответствии любого из этих выражений соответствующая часть заменяется пустой строкой, возвращая тем самым усеченную версию строки без лидирующих и замыкающих пустых пространств.
Массивы в JavaScript
Работа с массивами в JavaScript очень напоминает работу с ними в РНР, хотя синтаксис имеет несколько иной вид. Тем не менее с учетом уже приобретенных знаний о массивах освоить материал этого раздела будет относительно несложно.
Числовые массивы
Чтобы создать новый массив, нужно воспользоваться следующим синтаксисом: arrayname = new ArrayO
или же его более краткой формой:
arrayname = []
Присваивание значений элементам массива
В РНР можно было добавить к массиву новый элемент простым присваиванием ему значения, без указания смещения элемента относительно начала массива:
$arrayname[] = "Элемент 1";
$arrayname[] = "Элемент 2";
В JavaScript для этих же целей используется метод push:
arrayname.push("Элемент 1") arrayname. ризЬСЭлемент 2")
Он позволяет добавлять к массиву элементы, не отслеживая их количество. Когда потребуется узнать, сколько элементов содержится в массиве, можно будет воспользоваться свойством length:
document.write(arrayname.1ength)
Если нужно будет проконтролировать размещение элементов, расставляя их по конкретным местам, можно воспользоваться другим синтаксисом: arrayname[0] = "Элемент 1" arrayname[l] - "Элемент 2"
В примере 15.8 показан простой сценарий, в котором создается массив, в него загружается несколько элементов, после чего эти элементы отображаются на экране.
Пример 15.8. Создание, построение и вывод массива на зкран
<script>
numbers = []
numbers.push("Один")
numbers.push("Два")
numbers. pushC'Tpn")
for (j = .0 ; j < numbers.length ; ++j)
document.write("Элемент " + j + " = " + numbers[j] + "<br />")
</script>
Этот сценарий выводит следующую информацию:
Элемент 0 = Один
Элемент 1 = Два
Элемент 2 = Три
Присваивание с использованием ключевого слова array
С помощью ключевого слова Array можно также создать массив с несколькими исходными элементами:
numbers = Array("Один". "Два". "Три")
После этого ничто не помешает добавить к данному массиву дополнительные элементы.
Теперь в вашем распоряжении есть несколько способов добавления элементов к массиву и один способ ссылки на них, но JavaScript предлагает куда более обширный арсенал способов, к рассмотрению которых мы скоро перейдем. Но сначала рассмотрим еще один тип массива.
Ассоциативные массивы
К ассоциативным относятся такие массивы, в которых ссылки на элементы осуществляются по именам, а не по числовому смещению. Чтобы создать ассоциативный массив, нужно определить блок элементов, заключенный в фигурные скобки! Для каждого элемента слева от двоеточия (:) указывается его ключ, а справа — содержимое. В примере 15.9 показано, как можно создать ассоциативный массив для хранения данных о товаре в разделе мячей (balls) интернет-магазина спортивного инвентаря.
Пример 15.9. Создание и отображение ассоциативного массива
<script>
balls = {"гольф":"Мячи1 дляя гольфа. 6'гольфа. 6".
"теннис":"Мячи/1 дляя тенниса. тенниса. 3".
"футбол":"Футбольныйа мяч. 1".
"пинг-понг": "Мячиа для я пинг-понга, 12 шт."}
for (ball in balls)
document.write(ball + " = " + balls[bal1] + "<br />")
</script>
Для проверки факта создания и заполнения массива я воспользовался еще одной разновидностью цикла for, в которой применяется ключевое слово in. В этом цикле создается новая переменная, которая задействуется только внутри массива (в данном примере — ball), и вызывается последовательный перебор всех элементов массива, указанных справа от ключевого слова in (в данном примере — bal 1 s). Цикл обрабатывает каждый элемент массива bal 1 s, помещая значение ключа в переменную bal 1.
Используя значение ключа, сохраненное в переменной ball, можно также получить значение текущего элемента массива balls. Результат вызова сценария этого примера в браузере будет иметь следующий вид:
гольф = Мячи для гольфа. 6
теннис = Мячи для тенниса. 3
футбол = Футбольный мяч, 1
пинг-понг = Мячи для пинг-понга, 12. шт.
Чтобы получить значение конкретного элемента ассоциативного массива, нужно в явном виде указать его ключ (в данном случае будет выведено значение Футбольный мяч, 1):
document.write(bal1s[ 'футбол'])
Многомерные массивы
В JavaScript для создания многомерного массива нужно просто поместить массивы внутри других массивов. Например, чтобы создать массив, содержащий сведения о двумерной шахматной доске (8 * 8 клеток), можно воспользоваться кодом примера 15.10.
Пример 15.10. Создание многомерного числового массива
<script>
checkerboard = Array(
Array(
'o'.
'o'.
'o’.
'o').
Array( 'o'.
'o'.
'o'.
'o'.
' ').
Array(
’o'.
'o'.
'o'.
'o') .
Array(
Array(
Array( 'O'.
'O’.
'O'.
'O'.
Array(
'O'.
'O'.
’O'.
'O’).
Array( 'O'.
'O'.
'O'.
'O'.
))
document.wri te(
"<pre>")
for (j = 0 ; j < 8 ; ++j)
{
for (k = 0 ; k < 8 ; ++k)
document.wri te(checkerboa rd[j][k] +")
document.write("<br />")
}
document. wri te( "</pre>")
</script>
В данном примере буквами нижнего регистра обозначены черные, а буквами верхнего регистра — белые фигуры. Два цикла for, один из которых является вложенным, осуществляют последовательный перебор элементов массива и отображают его содержимое.
Внешний цикл содержит две инструкции, поэтому они заключены в фигурные скобки. Внутренний цикл обрабатывает каждую клетку в горизонтали, выводя символ, находящийся в позиции [j ] [к], за которым следует пробел (чтобы придать выводимой информации квадратную конфигурацию). В этом цикле содержится всего одна инструкция, поэтому заключать ее в фигурные скобки не имеет смысла. Теги <рге> и </рге> обеспечивают корректный вывод информации:
0000
0000
0000
0000
0000
00.ОО
Можно также получить непосредственный доступ к любому элементу данного массива, применив для этого квадратные скобки:
document.write(checkerboard[7][2])
Эта инструкция выводит букву 0 верхнего регистра, то есть содержимое восьмой сверху и третьей справа клетки — напоминаю, что индексация элементов в массиве начинается с нуля, а не с единицы.
Использование методов массивов
Реализовать возможности, предоставленные массивами, помогают имеющиеся в JavaScript готовые к использованию методы для работы с ними и с содержащимися в них данными. Рассмотрим подборку, состоящую из наиболее востребованных методов.
Метод concat
Метод concat объединяет два массива или ряд значений в массив. Например, еле- дующий код выведет Банан «Виноград. Морковь. Капуста:
fruit = ["Банан", "Виноград"]
veg = ["Морковь". "Капуста"]
document.wri te (frui I t.concat(veg))
В качестве аргументов можно указать несколько массивов, тогда метод concat добавит все их элементы в порядке указания массивов.
А вот еще один способ использования метода concat, где с массивом pets объединяются простые значения и на экран выводится строка Кошка. Собака. Рыба. Кролик. Хомяк:
pets = ["Кошка". "Собака". "Рыба"]
more_pets = pets.concat("Кролик". "Хомяк") document.wri te(more_pets)
Метод forEach (для браузеров не из семейства IE)
Используемый в JavaScript метод forEach является еще одним способом получения функциональных возможностей, аналогичных тем, которые предоставляются ключевым словом РНР foreach, но он работает только в тех браузерах, которые не относятся к семейству Internet Explorer. Воспользоваться этим методом можно, передав ему имя функции, которая будет вызвана для каждого элемента массива. Как это делается, показано в примере 15.11.
Пример 15.11. Использование метода forEach
<scr~ з~>
pets = I"Кошка". "Собака". "Кролик", "Хомяк"]
pets.~orEach(output)
function output(element. index, array)
{
document.write("Элемент с индексом " + index + " содержит значение " + element + "<br />")
}
</scnpt>
В данном случае функция, передаваемая методу forEach, называется output. Она воспринимает три параметра: элемент, его индекс и массив. Как они используются,
зависит от потребностей вашей функции. В данном примере они просто отображают значения индекса и элемента с помощью метода document .write.
После того как массив будет заполнен, можно вызвать рассматриваемый метод:
pets.forEach(output)
На выходе будет получена следующая информация:
Элемент с ииндексом О сод0ржит знсодержитзначениеКошка
Элемент с ииндексом 1 сод 1ржит знсодержитзначениеСобака
Элемент с ииндексом 2 сод2ржит знсодержитзначениеКролик
Элемент с ииндексом 3 сод3ржит знсодержитзначениеХомяк
Метод forEach (кросс-браузерное решение)
Разумеется, по уже сложившейся традиции Microsoft решила не поддерживать метод forEach, поэтому предыдущий пример будет работать только на браузерах, не принадлежащих семейству Internet Explorer. Чтобы обеспечить кросс-браузерную совместимость, в то время как IE не поддерживает эту функцию, вместо pets. forEach (output) нужно воспользоваться следующей инструкцией:
for (j = 0 : j < pets.length ; ++j) output(pets[j]. j)
Метод join
Метод join позволяет превратить все значения массива в строки, а затем объединить их в одну большую строку, расставляя между значениями необязательные разделители. В примере 15.12 показаны три способа использования этого метода.
Пример 15.12. Использование метода join <script>
'pets = ["Кошка", "Собака". "Кролик". "Хомяк"] document.write(pets.joinO1++ "<br />")
document.write(pets.join(’ ’) ++ "<br />") document.write(pets.join(’ : ') + "<br />") </script>
Если не указывать параметр, метод join использует в качестве разделителя элементов запятую, в противном случае между элементами вставляется переданная методу join строка. Код примера 15.12 выводит следующую информацию:
Кошка.Собака.Кролик,Хомяк
Кошка Собака Кролик Хомяк
Кошка : Собака : Кролик : Хомяк
Методы push и pop
Применение метода push для вставки значения в массив уже было рассмотрено. Противоположным ему по действию является метод pop. Он удаляет последний вставленный элемент из массива и возвращает значение этого элемента. Порядок его использования показан в примере 15.13.
Пример 15.13. Использование методов push и pop
<script>
sports = ["Футбол". "Теннис". "Бейсбол"]
Сс-с^е*: нit:e("Изначально = "+ sports + "<br />")
sports.push("Hockey")
dccy^t.write("После вставки = " + sports + "<br />") removed = sports.pop()
zx/ert _wrlte( "После удаления = " + sports + “<br />") ccc^rert.wrue( "Удаленный элемент = "+ removed + "<br />")
< 5CrlDt>
Три основные инструкции этого сценария выделены полужирным шрифтом. Сначала в сценарии создается массив по имени sports, содержащий три элемента, затем в него вставляется четвертый элемент, после чего сценарий удаляет этот элемент. В процессе этих действий с помощью метода document .write отображаются разные значения массива. Сценарий выводит следующую информацию:
Изнчально = Футбол.Теннис.Бейсбол
После вставки = Футбол.Теннис.Бейсбол.Хоккей
После удаления = Футбол.Теннис.Бейсбол
'Удаленный элемент = Хоккей
Как показано в примере 15.14, методы push и pop применяются в тех случаях, когда нужно отвлечься от каких-нибудь действий на другие, а затем вернуться к прежним действиям.
Пример 15.14. Использование методов push и pop внутри*цикла и за его пределами <script> numbers = []
for (j=0j<3 : ++j)
{
numbers.push(j):
document.write("Вставлен элемент " + j + "<br />") }
// Здесь осуществляются какие-нибудь другие действия document.write(n<br />")
document.write(,’Удaлeн элемент" + numbers.pop() + "<br />") document.write("Удален элемент " + numbers.pop() + "<br />") document.write (,,Удaлeн элемент " + numbers.pop() + "<br />") </script>
Код этого примера выведет следующую информацию:
Вставлен элемент О
Вставлен элемент 1
Вставлен элемент 2
Удален элемент 2
Удален элемент 1
Удален элемент О
Использование метода reverse
Метод reverse осуществляет простую перестановку элементов массива в обратном порядке. Его действие показано в примере 15.15.
Пример 15.15. Использование метода reverse
<script>
sports = ["Футбол". "Теннис". "Бейсбол". "Хоккей"]
sports, reversed
document.wri te(sports)
</script>
Исходный массив подвергается изменению, и сценарий выводит следующую информацию:
Хоккей.Бейсбол.Теннис.Футбол
Метод sort
Метод sort позволяет расставить все элементы массива в алфавитном или в каком- нибудь другом порядке в зависимости от применяемых параметров. В примере 15.16 показаны четыре типа сортировки.
Пример 15.16. Использование метода sort
<script>
// Сортировка по алфавиту
sports = ["Футбол". "Теннис". "Бейсбол". "Хоккей"]
sports.sort О
document.write(sports + "<br />")
// Сортировка по алфавиту в обратном порядке sports = ["Футбол". "Теннис". "Бейсбол", "Хоккей"] sports.sort().reverse()
document.write(sports + "<br />")
// Сортировка чисел по возрастанию
numbers = [7. 23, 6, 74]
numbers.sort(function(a,b){return a - b}) document.write(numbers + "<br />")
// Сортировка чисел по убыванию
numbers = [7, 23. 6. 74]
numbers.sort(function(a.b){return b - a}) document.write(numbers + "<br />")
</script>
В первом из четырех блоков этого примера применяется сортировка по алфавиту, во втором — возвращение к исходному виду, а затем метод reverse, чтобы получить сортировку по алфавиту в обратном порядке.
Третий и четвертый блоки усложнены использованием функции для сравнения взаимоотношений между а и Ь. У нее отсутствует имя, поскольку она используется только при сортировке. Функция по имени function, которая применяется для
создания анонимных функций, уже встречалась при определении метода класса (метода showUser).
Здесь functi on создает анонимную функцию, отвечающую запросам метода sort. Если функция возвращает значение больше нуля, сортировка предполагает, что а ставится перед Ь. Если функция возвращает значение меньше нуля, сортировка предполагает, что b ставится перед а. Если возвращается нуль, порядок следования а и b остается неизменным, поскольку они равны друг другу. Сортировка запускает эту функцию применительно ко всем значениям массива для определения порядка их следования.
За счет манипуляции возвращаемыми значениями (а - b или b - а) в третьем и четвертом блоках примера 15.16 осуществляется выбор между сортировкой чиссл по возрастанию и по убыванию.
И на этом я заканчиваю введение в JavaScript. Теперь у вас должно сложиться представление о трех из четырех основных технологиях, рассматриваемых в данной книге. В следующей главе будут рассмотрены некоторые современные технические приемы, основанные на применении всех этих технологий, в частности проверка соответствия шаблонам и проверка допустимости введенных значений.
Проверьте ваши знания
Обладают ли имена функций и переменных в JavaScript чувствительностью к регистру используемых в них букв?
Как создать функцию, которая воспринимает и обрабатывает неограниченное количество параметров?
Назовите способ возвращения из функции сразу нескольких значений.
Какое ключевое слово для ссылки на текущий объект используется при определении класса?
Должны ли все методы класса определяться внутри определения самого класса?
Какое ключевое слово применяется для создания объекта?
Как обеспечить доступность свойства или метода всем объектам класса без его тиражирования внутри объекта?
Как создать многомерный массив?
Какой синтаксис используется для создания ассоциативного массива?
Создайте€инструкциюСдляЯсортировкиКмассива чиселЛвЕубывающемМпорядке.
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 15».
J1L
f с iПроверка данных
vи обработка ошибок
в JavaScript и РНР
После приобретения основательных знаний по программированию на РНР и JavaScript настало время объединить эти две технологии воедино. В этой главе будет рассмотрено создание максимально удобных для пользователей вебформ.
РНР будет использоваться для создания форм, a JavaScript -- для проверки приемлемости и полноты данных на стороне клиента, насколько это возможно до их отправки на сервер. После чего окончательная проверка приемлемости введенных данных будет выполняться программой РНР, которая при необходимости снова выведет форму, чтобы пользователь мог внести в нее изменения.
В процессе изложения данной главы будут рассмотрены проверка данных и применение регулярных выражений как в JavaScript, так и в РНР.
Проверка данных, введенных пользователем, средствами JavaScript
Проверка данных средствами JavaScript должна рассматриваться в качестве помощи пользователям, а не веб-сайтам, поскольку, как я уже неоднократно подчеркивал, нельзя доверять абсолютно ничему, что отправлено на ваш сервер, даже если предположить, что полученные данные проверены с помощью JavaScript. Дело в том, что взломщики могут без особых усилий создать имитацию ваших веб-форм и отправить любые нужные им данные.
Еще одна причина, не позволяющая полагаться на JavaScript при проверке введенных данных, заключается в том, что некоторые пользователи отключают JavaScript или используют браузеры, не поддерживающие этот язык.
Поэтому лучшее, что можно сделать при проверке данных средствамиJavaScript, это выполнить проверку информационного наполнения тех полей, которые не должны оставаться пустыми, обеспечить приведение адресов электронной почты в надлежащий формат и гарантировать то, что введенные значения находятся в пределах ожидаемых границ.
Документ validate.html (часть первая)
Рассмотрим стандартную регистрационную форму, используемую большинством веб-сайтов, на которых работать можно только зарегистрированным пользователям. В форме будут запрашиваться имя, фамилия, пользовательское имя, пароль, возраст и адрес электронной почты. В примере 16.1 показан шаблон, который можно применять для этой формы.
Пример 16.1. Форма с проверкой данных средствами JavaScript (часть первая) <html><head><ti 11е>Пример формы</1i 11e> <-style>.signup { border: lpx so lid #999999:
font: normal 14px helvetica: col or:#444444: }</style>
<script>
function validate(form) {
fa i1 = validateForename(form.forename.value) fa i l += validateSurnameCform.surname.value) fa i l +« validateUsername(form.username.value) fa i l += validatePassword(form.password.value) fa il += validateAge(form.age.value)
fail -= validateEmaiHform.email .value)
if (fa il = "") return true
else { alert(fa il) : return false }
>
</script></head><body>
<table class=,,signup" border="0" cellpadding="2” cellspacing="5" bgcolor=”#eeeeee">
<th colspan="2" al1дп="сеп1ег">Регистрационная $opMa</th>
<form method=,,post" action="adduser.php”
onSubmit="return validate(this)”>
<tr><td>MMfl</td><td><input type="text" maxlength=!,32”
name^'forename" /></td>
</tr><tr><td>Фaмилия</td><td><input type="text" maxlength="32" name="surname" /></td>
</tr><tr><td>Пoльзoвaтeльcкoe HMfl</td><td><input type="text" maxlength=,'16" name="usernamen /></td>
</tr><tr><td>napoflb</td><td><input type="text" maxlength=,,12" name=”password" /></td>
</tr><tr><td>B03pacT</td><td><input type="textM maxlength^'S" пате=,,адеп /></td>
</tr><tr><td^eKTpoHHbm aflpec</td><td><input type="textM maxlength=n64" name="emai1" /></td>
</tr><tr><td colspan=”2M al ign="center,,>
<input type="submit" уа1ие=,,Зарегистрироваться” /></td>
'/tr></form></table>
В данном виде эта форма будет только отображаться, но не сможет заниматься самопроверкой, поскольку к ней еще не добавлены основные проверочные функции. Но, несмотря на это, если набрать данный код, сохранить его в файле
val i da I te. html, а затем вызвать файл в браузере, будет получен результат, показанный на рис. 16.1.
Рис. 16.1. Форма, выведенная кодом примера 16.1
Порядок работы этого кода
Рассмотрим, из чего состоит этот документ. В первых трех строках осуществляется настройка документа и используется небольшой фрагмент кода CSS, предназначенный для улучшения внешнего вида формы. Затем следует выделенная полужирным шрифтом часть документа, относящаяся к JavaScript.
В теги <script> и </script> заключена всего одна функция по имени validate, которая, в свою очередь, вызывает шесть других функций, проверяющих каждое из имеющихся в форме полей. Все они вскоре будут рассмотрены. А сейчас я ограничусь объяснением того, что они возвращают либо пустую строку, если иоле проходит проверку, либо сообщение об ошибке, если оно эту проверку не проходит. При наличии любых ошибок сообщения о них выводятся в окне предупреждения, появляющемся благодаря последней строке сценария.
Если поле проходит проверку, то проводившая ее функция возвращает значение true, а если не проходит — fal se. Значения, возвращаемые функцией val i date, учитываются при отправке данных формы: если она возвращает false, данные не отправляются. При этом пользователь получает возможность закрыть появившееся окно предупреждения и внести изменения в данные. Если будет возвращено значение true, значит, ошибок в полях формы не найдено и форму можно отправлять па сервер.
Во второй части этого примера показан код HTML для формы, где каждое поле и его имя помещены в отдельную строку таблицы. В этом HTML нет ничего сложного, за исключением инструкции onSubmit="return validate(th1is)”, помещенной в открывающий тег <form>. Использование атрибута onSubmi t позволяет при отправ-
ке формы вызвать избранную вами функцию. Эта функция может выполнить проверку и вернуть значение либо true, либо fal se, для того чтобы известить о том, разрешена или нет отправка формы.
Параметр th is указывает на текущий объект (то<есть на данную форму). Он передается только что рассмотренной функции validate, которая получает этот параметр в виде объекта form.
Как видите, внутри HTML-формы JavaScript используется только для того, чтобы вызвать инструкцию return, помещенную в атрибут onSubmit. Браузеры, у которых JavaScript отключен или не поддерживается, просто проигнорируют атрибут onSubmit и беспрепятственно отобразят HTML.
Документ validate.html (часть вторая)
Теперь обратимся к коду примера 16.2, содержащему набор из шести функций, осуществляющих проверку полей формы. Я предлагаю набрать весь код этой второй части и добавить его к первой половине, которая уже сохранена в файле val i date. html. Включение в один и тот же HTML-файл сразу нескольких <БСПр^-разделов вполне допустимо. Если хотите, можете объединить дополнительный код с первым <5Спр£>-разделом, показанным в примере 16.1.
Пример 16.2. Вторая часть формы, проверяемой средствами JavaScript <script> function validateForenameCfield) {
i f (field = "") return "He введено имя.\п” return ""
}
function validateSurname(field) {
i f (fie ld = "") return "He введена фамилия.\n" return ""
}
function validateUsername(field) {
i f (field == "") return "He введено имя пользователя.\n" else if (field.length < 5)
return "В имени пользователя должно быть не менее 5 символов.\п"
else i f (/[Aa-zA-Z0-9_-]/.test(field))
return "В имени пользователя разрешены только a-z, A-Z, 0-9. - и _.\п" return "”
}-
function validatePassword(field) {
i f (field == "") return "He введен пароль.\n" else i f (field .length < 6) return "В пароле должно быть не менее 6 символов.\п" else if (!/[a-z]/.test(field) || ! /[A-Z]/.testt(field) || !/[0-9]/.test(field))
return "Пароль требует 1 символа из каждого набора a-z, A-Z и 0-9.\п" return ""
}
function validateAge( I field) {
if (isNaN(field)) return "He введен возраст.\n"
else if (field < 18 11 field > 110)
return "Возраст должен быть между 18 и 110.\п" return ""
}■
function va1lidateEmail(field) {
if (field == "") return "He введен адрес электронной почты.\n"
else if (!((fie ld .indexOf(".") > 0) &&
(field.indexOf("@") >0)) || /[Aa-zA-Z0-9.@_-]/.test(field)) return "Электронный адрес имеет неверный формат.\n" return ""
}
</scri pt></body></html>
Чтобы понять, как работает проверка, рассмотрим по очереди все эти функции, начиная с validateForename.
Проверка имени
Предельно лаконичная.функция validateForename воспринимает параметр field, являющийся значением имени (forename), переданным ей функцией val i date. Если это значение является пустой строкой, возвращается сообщение об ошибке, если нет, возвращается пустая строка, свидетельствующая о том, что ошибка не обнаружена.
Если пользователь введет в это поле пробелы, то они будут приняты функцией val i dateForename, хотя в качестве имени они не годятся. Этот просчет можно исправить, добавив еще одну инструкцию, удаляющую из поля пустые пространства перед его проверкой на незаполненность, затем воспользоваться регулярным выражением, чтобы убедиться в том, что в поле находится еще что-нибудь, кроме пробелов, или — как это сделано в данном случае — позволить пользователю допустить эту ошибку и «отловить» ее на сервере.
Проверка фамилии
Код функции validateSurname похож на код функции validateForename, он также возвращает сообщение об ошибке, если в качестве фамилии (surname) была предоставлена пустая строка. Я решил не накладывать ограничений на символы обоих полей, чтобы пользователь мог вводить символы, не входящие в английский алфавит, имеющие дополнительные знаки и т. д.
Проверка имени пользователя
Код функции val i datellsername немного интереснее, поскольку он выполняет более сложную работу. Он должен разрешить использование только тех символов, которые входят в набор a-z, A-Z, 0-9, _ и -, и гарантировать, что имена пользователей состоят не менее чем из пяти символов.
Код структуры if. . .else начинается с возвращения сообщения об ошибке в том случае, если поле не было заполнено. Если значение поля не является пустой строкой, но состоит менее чем из пяти символов, то возвращается другое сообщение об ошибке.
Затем вызывается JavaScript-функция test, которая сравнивает регулярное выражение (соответствующее любому символу, не входящему в перечень разрешенных) с содержимым поля (см. раздел «Регулярные выражения» данной главы). Встретив хотя бы один недопустимый символ, функция test возвращает true, в результате чего функция validateUsername возвращает сообщение об ошибке.
Проверка пароля
Такая же технология используется и в функции val i datePassword. Сначала функция проверяет поле на пустоту, возвращая сообщение об ошибке при незаполненном поле. Затем сообщение об ошибке возвращается в том случае, если пароль короче шести символов.
Одно из требований, предъявляемых к паролям, заключается в том, что в них должно быть хотя бы по одному символу в нижнем и в верхнем регистре, а также хотя бы одна цифра, поэтому функция test вызывается три раза, по одному разу на каждую из этих проверок. Если при любом из таких вызовов будет возвращено значение false, это будет означать, что одно из условий не выполнено, поэтому будет возвращено сообщение об ошибке. В противном случае будет возвращена пустая строка, свидетельствующая о том, что с паролем все в порядке.
Проверка возраста
Функция val idateAge возвращает сообщение об ошибке, если значение поля не является числом (что определяется вызовом функции i sNaN) либо введенный возраст меньше 18 или больше 110 лет. У ваших приложений могут быть иные требования к возрастной категории или вообще не быть никаких требований. В случае успешной проверки также будет возвращена пустая строка.
Проверка адреса электронной почты
И последняя, наиболее сложная проверка — адреса электронной почты — выполняется с помощью функции validateEmail. После проверки на существование каких-нибудь введенных данных и возвращения сообщения об ошибке при отсутствии таковых функция дважды вызывает JavaScript-функцию i ndexOf. При первом вызове проверяется наличие точки (.), начиная со второго символа, а при втором — присутствие символа @, также начиная со второго символа.
Если будут пройдены эти две проверки, вызывается функция test, чтобы проверить поле на наличие недопустимых символов. Если любая из этих проверок не будет пройдена, возвращается сообщение об ошибке. Допустимыми в адресе электронной почты считаются буквы в нижнем и верхнем регистрах, символы подчеркивания, тире, точки и символ @. Все они перечислены в регулярном выражении, передаваемом методу test. Если не будет найдено ни одной ошибки, возвращается пустая строка, свидетельствующая об успешно пройденной проверке. В последней строке примера сценарий и документ закрываются.
На рис. 16.2 показан результат щелчка на кнопке Зарегистрироваться без заполнения полей.
Рис. 16.2. Работа JavaScript-формы с проверкой данных
Использование отдельного файла JavaScript
Конечно, благодаря универсальности своей конструкции и применимости ко многим типам потенциально востребуемых проверок эти шесть функций становятся идеальными кандидатами для выделения их в отдельный файл JavaScript (не забыв при этом удалить все теги <scri pt> или </scri pt>!). Это файл можно назвать val i date_ functions. js и включить сразу же после начального блока сценария в пример 16.1, используя следующую инструкцию:
<script sгс="va1idate_functions.jsn></script>
Регулярные выражения
Давайте более пристально рассмотрим шаблоны соответствия, созданные нами благодаря использованию регулярных выражений, которые поддерживаются как в JavaScript, так и в РНР. Они позволяют выстроить внутри одного выражения более мощные алгоритмы соответствия шаблонам.
Соответствие, закладываемое в метасимволы
Любое регулярное выражение должно быть заключено в слеши (/). Конкретные символы, находящиеся внутри этих слешей, называются метасимволами и имеют специальное предназначение. Например, звездочка (*) имеет аналогичное (но не вполне такое же) значение, как и звездочки, уже встречавшиеся вам в оболочке или
в командной строке Windows. Звездочка означает следующее: «Текст, подвергаемый сравнению, может содержать любое количество указанного перед ней символа или не содержать его вообще».
К примеру, вы ищете имя Le Gui п и знаете, что оно может быть написано как с пробелом, так и без него. Из-за не вполне обычной разметки текста (кто-нибудь, например, мог вставить лишние пробелы, чтобы выровнять строки по правому краю) нужно вести поиск в следующей строке:
The difficulty of classifying Le Guin's works
To есть шаблон должен соответствовать строке LeGui п, а также отдельно строкам Le и Gui п, разделенным любым количеством пробелов. Решением может стать установка после пробела звездочки:
/Le *Guin/
В строке, кроме имени Le Gui п, присутствует множество других символов, но этот шаблон все равно будет работать. Поскольку регулярное выражение соответствует какой-то части строки, проверочная функция вернет истинное значение. А если нужно узнать, что в строке не содержится ничего другого, кроме Le Guin? Как в этом убедиться, будет показано чуть позже.
Предположим, что известно о непременном наличии хотя бы одного пробела. В таком случае можно воспользоваться знаком плюс (+), поскольку этот метасимвол требует присутствия хотя бы одного из предшествующих ему символов:
/Le +Guin/
Нестрогое символьное соответствие
Одним из самых полезных метасимволов является точка (.) I , поскольку она может соответствовать любому символу, за исключением символа новой строки. Предположим, что выполняется поиск HTML-тегов, которые начинаются с символа < и заканчиваются символом >. Проще всего найти тег с помощью следующего регулярного выражения:
/<.*>/
Точка соответствует любому символу, а звездочка (*) расширяет действие точки до соответствия нулевому или любому другому количеству символов, что означает: «Соответствует всему, что заключено между символами < и >, даже если там ничего нет».
Этот шаблон будет соответствовать строкам <>, <em>, <br /> и т. д. Но если не требуется, чтобы он соответствовал отсутствию символов <>, нужно вместо символа * использовать символ +:
/<.+>/
Знак «плюс» расширяет действие точки до соответствия одному или нескольким символам, что означает: «Соответствует всему, что находится между символами < и >, пока между ними есть хотя бы один символ».
Этот шаблон будет соответствовать <ет> и </ет>, <hl> и </hl, и тегам с атрибутами, например:
<а href="www.mozilla.org">
К сожалению, знак «плюс» расширит соответствие вплоть до последнего символа > в строке, поэтому соответствовать шаблону будет и такая строка:
<И1><Ь>Введение</Ь></111>
А в ней содержится больше одного тега! Чуть позже в этом разделе я покажу более подходящее решение.
&
rVr-
Если между угловыми скобками использовать только точку и не ставить за ней знаков + или *, *5? J щ то она будет соответствовать любому одиночному символу, а шаблон будет соответствовать таким тегам, как <Ь> и <i>, но не будет соответствовать тегам <ет> или <textarea>.
Если нужно, чтобы соответствие относилось к символу точки (.) как таковому, его действие нужно отключить, поставив перед ним символ обратного слеша (\), поскольку в противном случае точка будет считаться метасимволом, соответствующим любому символу.
К примеру, если нужен шаблон, соответствующий числу с плавающей точкой 5.0, то в нем можно будет использовать следующее регулярное выражение:
/5\.О/
Символ обратного слеша может отключить действие любого метасимвола, в том числе и еще одного обратного слеша (если в тексте отыскивается соответствие именно обратному слешу). Но слэш может и запутать ситуацию — чуть позже будет показано, как обратные слеши иногда придают следующим за ними символам специальное предназначение.
Только что мы рассмотрели шаблон соответствия числу с плавающей точкой. Но вам наверняка понадобится проверить соответствие не только строке 5.0, но и строке 5., поскольку обе они содержат значение одного и того же числа с плавающей точкой. Нужно будет также проверить соответствие строкам 5.00,5. ОООи т. д., ведь разрешено использовать любое количество нулей. Это можно сделать добавлением звездочки:
/5\.0*/
Группировка с помощью скобок
Предположим, что нужно найти соответствие таким возрастающим степеням, как кило, мега, гига и тера. Иными словами, нужно найти соответствие следующим строкам:
1.000
1.000.000
1 .000.000.000
1 .000.000.000.000
Здесь мог бы пригодиться знак «плюс», но нужно сгруппировать строку ,000 так, чтобы действие этого знака распространялось на нее целиком. Для этого служит следующее регулярное выражение:
/1C,000)+ /
Скобки означают: «При применении какого-нибудь метасимвола наподобие знака “плюс” все это нужно рассматривать как группу». Строки 1,00.000 и 1.000.00 не будут соответствовать шаблону, поскольку в тексте должен быть символ 1, за которым следует одна или несколько групп, состоящих из запятой и трех нулей.
Пробел после знака «плюс» показывает, что соответствие должно закончиться, как только встретится пробел. Без этого пробела строка 1,000.00 будет вычислена соответствующей шаблону, поскольку в расчет будет приниматься только ее первая часть 1.000, а оставшаяся часть ,00 будет проигнорирована. Пробел нужен после остальных символов шаблона, чтобы обеспечить продолжение поиска соответствия шаблону до конца числа.
Символьный класс
Иногда требуется установить нестрогое соответствие, но не настолько пространное, чтобы для этого использовать точку. Нестрогость придает регулярным выражениям огромную мощность: она позволяет регулировать строгость и нестрогость в соответствии с вашими желаниями.
Одним из ключевых элементов поддержки нестрогости соответствия является пара квадратных скобок []. Эта пара, как и точка, соответствует всего одному символу, но в эти скобки помещается перечень всех возможных соответствий. При появлении любого из символов этого перечня текст будет соответствовать шаблону. Например, если нужно, чтобы шаблону соответствовали оба написания — американское gray и английское grey, можно задать следующее регулярное выражение:
/дг[ае]у/
В сравниваемой части текста после дг может быть либо а, либо е. Но должна быть только одна из этих букв: все, что помещается внутри квадратных скобок, соответствует только одному символу. Группа символов внутри скобок называется символьным классом.
Указание диапазона
Для указания диапазона внутри квадратных скобок можно использовать дефис (-). Одной из самых распространенных задач является проверка соответствия отдельной цифре, в которой можно использовать диапазон:
/[0-9]/
Цифры являются настолько распространенным элементом регулярных выражений, что для их представления используется отдельный символ \<± Его можно использовать для проверки соответствия цифре вместо регулярного выражения в квадратных скобках:
Инвертирование
Другим важным свойством квадратных скобок является инвертирование символьного класса. За счет помещения знака вставки (ж) после открывающей квадратной скобки можно превратить весь символьный класс в его противоположность. После этого он будет означать: «Соответствует любому символу, за исключением следующих». Предположим, нужно найти экземпляры строк Yahoo, в которых отсутствует следующий за ними восклицательный знак. (Официальное название компании содержит восклицательный знак!) Для этого можно использовать следующее регулярное выражение:
/Yahoo[A!]/
Символьный класс состоит из одного символа —восклицательного знака, но он инвертируется стоящим перед ним символом ж. Вообще-то это не самое лучшее решение задачи. Например, это выражение не позволяет найти соответствие, если Yahoo находится в конце строки, поскольку тогда за этим словом не следует что-нибудь, а содержимому квадратных скобок должен соответствовать один символ. В более удачном решении используется упреждающее инвертирование (соответствие чему- нибудь, за чем нет ничего другого), но эта тема выходит за рамки данной книги.
Более сложные примеры
После усвоения понятий символьных классов и инвертирования вы уже готовы к изучению более удачных решений задачи поиска соответствия тегу HTML. Рассматриваемое решение позволяет шаблону не пропустить закрывающую угловую скобку отдельного тега, но по-прежнему соответствовать таким тегам, как <ет> и </ет>, а также тегам с атрибутами, таким как:
<а href="www.mozil1a.orgn>
Один из вариантов такого решения выглядит следующим образом: /<[->]+>/
Это регулярное выражение похоже на результат падения чашки на клавиатуру, после которого она «по-прежнему вполне исправна и работоспособна». Давайте разобьем это выражение на части. На рис. 16.3 показан последовательный анализ всех его элементов.
Вот эти элементы:
О / — открывающий слеш, указывающий на то, что это регулярное выражение;
О < — открывающая угловая скобка тега HTML. Требует точного соответствия, поскольку не является метасимволом;
О [ж>] — символьный класс. Сочетание знака вставки и закрывающей угловой скобки ж> означает: «Соответствует всему, кроме закрывающей угловой скобки»;
О + — допускает любое количество символов, соответствующих предыдущему регулярному выражению [Л>], если есть хотя бы один соответствующий ему символ;
О > —закрывающая угловая скобка тега HTML. Требует точного соответствия;
О / закрывающий слеш, указывающий на конец регулярного выражения.
Открывающий слеш.Указывает на то,что это регулярноевыражениеОткрывающий слеш.Указывает на то,что это регулярноевыражениеУгловая скобка,открывающая тег HTML.Точно соответствуетсимволуУгловая скобка,открывающая тег HTML.Точно соответствуетсимволуСимвольный класс.Соответствует всему,кроме закрывающейугловой скобкиСимвольный класс.Соответствует всему,кроме закрывающейугловой скобки
Открывающий слеш.Указывает на то,что это регулярноевыражение
Открывающий слеш.Указывает на то,что это регулярноевыражение
Угловая скобка,открывающая тег HTML.Точно соответствуетсимволу
Угловая скобка,открывающая тег HTML.Точно соответствуетсимволу
Символьный класс.Соответствует всему,кроме закрывающейугловой скобки
Символьный класс.Соответствует всему,кроме закрывающейугловой скобки
Метасимвол.Означает любоеколичество символов,соответствующих [А>]Метасимвол.Означает любоеколичество символов,соответствующих [А>]Угловая скобка,закрывающая тег HTML.Точно соответствуетсимволуУгловая скобка,закрывающая тег HTML.Точно соответствуетсимволуЗакрывающий слеш.Указывает на то,что это конецрегулярного выраженияЗакрывающий слеш.Указывает на то,что это конецрегулярного выражения
Метасимвол.
Означает любоеколичество символов,соответствующих [А>]
Метасимвол.
Означает любоеколичество символов,соответствующих [А>]
Угловая скобка,закрывающая тег HTML.Точно соответствуетсимволу
Угловая скобка,закрывающая тег HTML.Точно соответствуетсимволу
Закрывающий слеш.
Указывает на то,что это конецрегулярного выражения
Закрывающий слеш.
Указывает на то,что это конецрегулярного выражения
Рис. 16.3. Разбор типичного регулярного выражения
Другое решение задачи поиска соответствия тегам HTML связано с использованием так называемых нежадных инструкций. По умолчанию инструкция поиска соответствия шабло ну является жадной, возвращающей наиболее длинное из всех возможных соответствий. При нежадном поиске соответствия ищется соответствующая строка, наиболее короткая из возможных. Применение нежадного поиска соответствия выходит за рамки данной книги, но более подробную информацию об этом можно найти по адресу http://tinyurl.com/ aboutregex.
Теперь рассмотрим одно из выражений из примера 16.1, которое использовалось в функции val idateUsername:
/[Aa-zA-Z0-9J/
На рис. 16.4 показан весь набор элементов этого выражения.
Рассмотрим эти элементы более подробно:
О / —открывающий слеш, указывающий на то, что это регулярное выражение;
О [ —открывающая квадратная скобка, с которой начинается символьный класс;
О * символ инвертирования: инвертирует все, что находится в скобках;
О a z представляет любую букву в нижнем регистре;
О A-Z представляет любую букву в верхнем регистре;
О 0-9 представляет любую цифру;
О _ - символ подчеркивания;
О ] —квадратная скобка, закрывающая символьный класс;
О / обратный слеш, указывающий на конец регулярного выражения.
Открывающий слеш.Указывает на то,что это регулярноевыражениеОткрывающий слеш.Указывает на то,что это регулярноевыражениеОткрывающаяквадратная скобка.Начинает символьныйклассОткрывающаяквадратная скобка.Начинает символьныйклассИнвертирующийсимвол.Инвертирует все,что в скобкахИнвертирующийсимвол.Инвертирует все,что в скобкахA-ZA-ZЛюбая буквав нижнем регистреЛюбая буквав нижнем регистреЛюбая буква вернем регистреЛюбая буква вернем регистреПредставляет любую цифруПредставляет любую цифруЗнак подчеркиванияЗнак подчеркиванияЗакрывающаяквадратная скобка.Завершает символьныйклассЗакрывающаяквадратная скобка.Завершает символьныйклассЗарывающий слеш.Указывает на то,что это конецрегулярного выраженияЗарывающий слеш.Указывает на то,что это конецрегулярного выражения
Открывающий слеш.Указывает на то,что это регулярноевыражение
Открывающий слеш.Указывает на то,что это регулярноевыражение
Открывающаяквадратная скобка.Начинает символьныйкласс
Открывающаяквадратная скобка.Начинает символьныйкласс
Инвертирующийсимвол.
Инвертирует все,что в скобках
Инвертирующийсимвол.
Инвертирует все,что в скобках
A-Z
A-Z
Любая буквав нижнем регистре
Любая буквав нижнем регистре
Любая буква вернем регистре
Любая буква вернем регистре
Представляет любую цифру
Представляет любую цифру
Знак подчеркивания
Знак подчеркивания
Закрывающаяквадратная скобка.Завершает символьныйкласс
Закрывающаяквадратная скобка.Завершает символьныйкласс
Зарывающий слеш.
Указывает на то,что это конецрегулярного выражения
Зарывающий слеш.
Указывает на то,что это конецрегулярного выражения
Рис. 16.4. Разбор регулярного выражения, используемого в функции validateUsername
Есть еще пара весьма важных метасимволов. Они «закрепляют» регулярное выражение, требуя его применения в определенном месте. Если знак вставки (А) присутствует в начале регулярного выражения, то соответствующее выражению строковое значение должно быть в начале строки текста, иначе оно не будет соответствовать шаблону. По аналогии с этим, если знак доллара ($) ставится в конце регулярного выражения, соответствующее выражению строковое значение должно находиться в конце строки текста.
Знак вставки (А) может запутать ситуацию, поскольку внутри квадратных скобок он означает «инвертировать символьный класс», а в начале регулярного выражения — «соот- етствовать началу строки». К сожалению, один и тот же символ служит для достижения
совершенно разных целей, поэтому при его использовании следует быть особенно внимательными.
Закончим изучение основ регулярных выражений ответом на ранее заданный вопрос: предположим, вам нужно убедиться в том, что в строке нет больше ничего, кроме того, что соответствует регулярному вы ражению. Что делать в случае, если нужна строка текста, в которой нет ничего, кроме Le Guin? Можно усов ер- шенствовать ранее рассмотренное регулярное выражение, закрепив его сразу с двух сторон:
/Ч. р *Guin$/
Сводная таблица метасимволов
В табл. 16.1 показаны метасимволы, используемые в регулярных выражениях.
Таблица 16.1. Метасимволы регулярных выражений
Метасимволы
Описание
/
Начало и конец регулярного выражения
Соответствует любому одному символу, кроме символа новой строки
Элемент*
Соответствует появлению элемента от нуля и более раз
Элемент+
Соответствует появлению элемента от одного и более раз
Элемент?
Соответствует появлению элемента от нуля до одного раза
[Символы]
Соответствует одному из тех си м волов, которые содержатся в квадратных скобках
[А символы]
Соответствует одному из тех сим волов, которые не содержатся в квадратных скобках
{regex)
Рассматривает regex (сокращение, означающее регулярное выражение) как группу для вычисления или для рассмотрения с одним из следующих метасимволов: *, + или ?
Левое\ правое
Соответствует либо левому, либо правому
[1-г]
Соответствует диапазону символов между 1и г
А
Требует, чтобы соответствие было в начале строки
$
Требует, чтобы соответствие было в конце строки
\ь
Соответствует границе слова
\в
Соответствует при отсутствии границы слова
\d
Соответствует одной цифре
\D
Соответствует одному символу, не являющемуся цифрой
\n
Соответствует символу новой строки
\s
Соответствует пробелу
\S
Соответствует символу, не являющемуся пробелом
\t
Соответствует символу табуляции
\w
Соответствует символу, используемому в словах (a-z, A-Z, 0-9 и _)
\W
Соответствует символу, не используемому в словах (все, кроме a-z, A-Z, 0-9 и J
\x
Соответствует х (применяется, если х является метасимволом, но нужен символ х как таковой)
{П}
Соответствует в точности л появлениям
{Л,}
Соответствует л и более появлениям
{m i n, max}
Соответствует как минимум m/л и как максимум m ax появлениям
После изучения этой таблицы и повторного исследования выражения /[ жа zA ZU - 9_] / можно понять, что оно легко и просто укорачивается до /[A\w]/, потому что отдельный метасимвол л (с буквой w в нижнем регистре) указывает на символы /, A-Z, 0 9 и
Можно проявить еще большую наблюдательность и заметить, что метасимвол /. (с буквой W в верхнем регистре) указывает на все символы, за исключением
a-z, A-Z, 0-9 и _. Это позволяет избавиться также от метасимвола Аи использовать для выражения только символы /[\W]/.
Чтобы дать вам больше пищи для размышлений о том, что и как работает, в табл. 16.2 показан ряд выражений и описаны ситуации, которым они соответствуют.
Таблица 16.2. Примеры регулярных выражений
Пример
Соответствие
г
Первая г в The quick brown
nec[ei][ei]ve
Либо receive, либо recieve (но также и receeve или reciive)
rec[ei]{2}ve
Либо receive, либо recieve (но также и receeve или reciive)
rec(ei|ie)ve
Либо receive, либо recieve (но не receeve или reciive)
cat
Слово cat в I like cats and dogs
cat| dog
Любое из слов cat или dog в I like cats and dogs
\
Символ «.» (Знак «\» необходим, так как «.» является метасимволом)
5\.0*
«5.», «5.0», «5.00», «5.000» и т. д.
[a-f]
Любой из символов а, Ь, с, d, е или f
cats$
Только последнее слово cats в My cats are friendly cats
Amy
Только первое my в my cats are my pets
\d{2,3>
Любое двух- или трехзначное число (от 00 до 999)
7(,000)+
«7,000»; «7,000,000»; «7,000,000,000»; «7,000,000,000,000» и т. д.
[\w]+
Любое слово из одного или нескольких символов
[\w]{5>
Любое слово из пяти символов
Общие модификаторы
В регулярных выражениях можно применять следующие модификаторы:
О /д — допускает «глобальное» соответствие. Этот модификатор применяется с функцией замены, что позволяет выполнить замену во всех соответствующих местах, а не только в месте первого соответствия;
О /i — отключает в регулярном выражении чувствительность к регистру букв. То есть вместо /[a-zA-Z]/ можно указать / [a-z]/i или /[A-Z]/i;
О /т — допускает многострочный режим работы, в котором знак вставки (Л) и знак доллара ($) соответствуют позициям перед любыми символами новой строки в сравниваемой строковой переменной и после них. Обычно при поиске соответствия в многострочной строковой переменной знак * соответствует только позиции в ее начале, а символ $ —в ее конце.
Например, выражение /cats/g будет соответствовать обоим появлениям слова cats в предложении I like cats and cats like me. Аналогично этому выражение /dogs/ gi будет соответствовать обоим появлениям слова dogs (Dogs и dogs) в предложении Dogs like other dogs, поскольку эти модификаторы допускают совместное использование.
Использование регулярных выражений
в JavaScript
В JavaScript регулярные выражения используются в основном в двух методах: test (который вы уже рассматривали) и replace. Метод test просто сообщает, соответствует ли его аргумент регулярному выражению, а метод repl асе воспринимает второй параметр — строку, которой заменяется текст, соответствующий регулярному выражению. Как и большинство методов, replace генерирует в качестве возвращаемого значения новую строку, входные данные при этом не изменяются.
Если сравнивать эти два метода, то следующая инструкция просто возвращает true, позволяя узнать, что слово cats появляется в строке хотя бы один раз:
document.write(/cats/i .testCCats are fun. I like cats."))
А следующая инструкция заменяет оба имеющихся слова cats словом dogs, выводя результат на экран. Поиск должен быть глобальным (/д), чтобы найти все экземпляры этого слова, и нечувствительным к регистру букв (/i)), чтобы найти слова, начинающиеся с большой буквы (Cats):
document.writeC'Cats are fun. I like cats.".replace(/cats/gi."dogs"))
Если испытать эту инструкцию в работе, то проявятся ограничения функции замены: поскольку текст заменяется строго той строкой, которую предписано использовать, первое слово Cats заменяется словом dogs, а не словом Dogs.
Использование регулярных выражений в РНР
В РНР наиболее часто используются следующие функции, в которых применяются регулярные выражения: pregjnatch, preg_match_al 1 и preg_repl асе.
Чтобы проверить присутствие слова cats в любом месте строки, в любой комбинации букв в нижнем и верхнем регистрах, можно воспользоваться функцией oregjnatch:
Sn = pregjnatchC/cats/i". "Cats are fun. I like cats.");
Поскольку в PHP используется значение 1 для TRUE и значение 0 для FALSE, предыдущая инструкция присвоит переменной $п значение 1. Первым аргументом функции служит регулярное выражение, а вторым — текст, проверяемый на соответствие. Но функция pregjnatch способна выполнять более сложную задачу, поскольку она воспринимает еще и третий аргумент, который показывает, какой именно текст соответствовал регулярному выражению:
$n = pregjnatchC/cats/i". "Cats are fun. I like cats.". Smatch):
echo "Количество соответсвий $n: $match[0]";
Третий аргумент является массивом (здесь ему присвоено имя Smatch). Функция помещает текст, соответствующий регулярному выражению, в первый элемент массива, поэтому, если соответствие будет найдено, то соответствующий регулярному выражению текст может быть найден в элементе Smatch[0]. В данном
примере выводимая на экран информация покажет, что соответствующий текст начинался с прописной буквы:
Количество соответствий 1: Cats
Если нужно определить все соответствия, используется функция preg match a 11:
$n = preg_match_all("/c I ats/r. "Cats are fun. I like cats.". Smatch); echo "Количество соответствий $n: ":
for ($j=0 : $j < $n : ++$j) echo Smatch[0][$j J ."
Как и в предыдущем случае, функции передан массив Smatch и элементу $match[0] присваиваются найденные соответствия, только теперь они представляют собой подмассив. Для отображения содержимого подмассива в этом примере осуществляется последовательный перебор его элементов с помощью цикла for.
Если нужно заменить часть строки, можно воспользоваться функцией preg_ repl асе. В этом примере все встречающиеся слова cats, независимо от регистра букв, заменяются словами dogs:
«У 4м?' 4 ——«У 4м?' 4 ——echo preg_replace("/cats/in. "dogs". "Cats are fun. I like cats."):
«У 4
м?' 4 ——
«У 4
м?' 4 ——
Тема регулярных выражений слишком обширна, и о ней написана целая книга. Если вам « нужна дополнительная информация, я рекомендую статью из «Википедии» по адресу http:// tinyurl.com/wikiregex.
Повторное отображение формы
после проверки данных РНР-программой
Вернемся к проверке формы. На данный момент нами создан HTML-документ validate.html, который будет отправлен PHP-программе adduser.php, но это произойдет только в том случае, если поля пройдут проверку средствами JavaScript или если JavaScript отключен или недоступен.
Теперь настало время создать программу, сохраняемую в файле adduser. php. Эта программа получает отправленную форму и проводит собственную проверку, а затем, если проверка не будет пройдена, снова предоставляет форму визитеру. Код, который нужно будет набрать и сохранить, показан в примере 16.3.
Пример 16.3. Программа adduser.php
<?php // adduser.php
// Сначала следует код РНР
$forename = Isurname = $username = Spassword = Sage = Semail =
if (isset($ _P0ST[’forename']))
$forename = fix_string($_POST['forename']):
if (isset($_POST['surname']))
$surname = fix< string($_POST['surname’]);
if (isset($_P0ST[‘username']))
Susername = fix_string($_POST[,username’] ) : i f (isset($_P0ST['password’]))
Spassword = fix_string($_ POST[*password']);
if (isset($_P0ST['age']))
Sage = fixstr I inigCSPOSTCage']):
if (isset(S_POST['email']))
Semail = fix< string($_POST['eman']):
Sfail = validateforename(S I forename):
$fail .= validatesurname(Ssurname);
$fail .= validate_username(Susername):
Sfail .= validate_password($password):
Sfail .= validate_age(Sage):
Sfail .= vaIlidateemail(Semail):
echo "<html><head><title>npHMep формы</Ш1е>":
if (S I fail = "") {
echo "</head><body>npoBepKa формы прошла успешно: Sforename.
Ssurname. Susername. Spassword. Sage. Semail.</body></html>":
// В этом месте отправленные поля будут вводиться в базу данных exit;
>
// Теперь выводится HTML и код JavaScript
echo <«_END
<!-- Раздел HTML -->
<style>.signup { border: lpx solid #999999;
font: normal 14px helvetica: color:#444444: }</style>
<script type="text/javascript”> function validate(form)
{
fail = validateForename(form.forename.value)
fail += validateSurname(form.surname.value)
fail += val idatellsername( form, username, value)
fail += validatePassword(form.password.value)
fail += validateAge(form.age.value)
fail += validateEmail(form.email.value)
if (fail == "") return true
else { alertt(fail); return false }
}
</script></head><body>
<table class="signupMborder="0" cellpadding="2"
cel 1 spacing=M5" bgcolor=,,#eeeeee">
<th colspan=,,2" align=,,centterM>PerncTpaunoHHafl $opMa</th>
<tr><td colspan="2">K сожалению, в вашей форме <br />
найдены следующие ошибки: <pxfont color=red size=l><i>$fail</i></font></p> </td></tr>
<form method="post" action="adduser.php"
onSubmit="return validate(thiis)">
<trxtd>l/lMfl</td><td><input type="text" maxlength="32" name="forename" value="$forename" /></td>
</tr><trxtd>Фaмилия</td><td><input type="text" maxlength=,,32" name="surname" value="$surname" /></td>
<Дг^г><^>Пользовательское HMfl</td><td><input type="text" maxlength="16" name="username" value="$username" /></td>
</tr><tr><td>Пapoлb</td><td><input type="text" maxiength="12" name="password" value=,,$password,, /></td>
</tr><tr><td>Bo3pacT</td><td><input type="text" maxlength="3" name="age" value="$age" /></td>
<Дг><г><^>Электронный aflpec</td><td><input type="text" maxlength="64" name="email" value="$email" /></td>
</tr><tr><td colspan=M2” align="center">
<input type="submit" уа1ие="Зарегистрироваться" /></td> </tr></form></table>
<!-- Раздел JavaScript -->
<script type="text/javascript">
function validateForename(field) {
if (field == "") return "He введено имя.\\n" return ""
}
function validateSurname(field) {
if (field == "") return "He введена фамилия.\\n" return ""
}
function validatellsername(field) {
if (field == "") return "He введено имя гпользователя.\\n"
else if (field.length < 5)
return "В имени пользователя должно быть не менее 5 символов.\\п"
else if (/[^a-zA-Z0-9_ -]/.test(field))
return "В имени пользователя разрешены только a-z, A-Z, 0-9. - и .\\п" return ""
}
function validatePassword(field) {
if (field — "") return "He введенпароль.\\n"
else if (field.length < 6)
return "В пароле должно быть не менее 6 символов.\\п" else if (!/[a-z]/.test(field) || ! /[A-Z]/.test(field) || ! /[0-9]/.test(fi eld))
return "Пароль требует 1 символа из каждого набора a-z. A-Z и 0-9.\\п" return ""
}
function validateAge(field) {
if (isNaN(field)) return "He введен возраст.\\n"
else if (field < 18 11 field > 110)
return "Возраст должен быть между 18 и 110.\\п" return ""
}
function validateEmai1(field) {
if (fiel i d == "") return "He введен адрес электронной почты.\\n"
else if ( !((field.indexOf(".") > 0) &&
(field.indexOf("@”) >0)) ||
/pa-zA-Z0-9.@_-] / .test (field ))
return "Электронный адрес имеет неверный формат.\\п" return "”
}
</script></body></html>
END:
//Ив$ завершающей части - РНР-функции
function validateforename(lfield) {
i f ($field = "") return "He введено имя<Ьг />": return :
}
function validate_surname($field) {
if (Ifield = "") return "He введена фамилия<Ьг />": return
}
function validate_username($field) {
i f (Ifield = "") return "He введено имя пользователям />".
else if (strlen($field) < 5)
return "В имени пользователя должно быть не менее 5 символов<Ьг />":
else if (pregm з atchC'/Pa-zA-ZO^-]/". Ifieldj))
return "В имени пользователя допускаются только буквы, цифры. - и _<br /> return :
}
function vaIlidate password(Sfield) {
i f (Ifield = "") return "He введен пароль<Ьг />":
else if (strlen(lfield) < 6)
return "В пароле должно быть не менее 6 символов<Ьг />” :
else if ( !preg_match(,7 [a-z]/" . Ifield) ||
!preg match("/[A-Z]/". Ifield) jj
!pregjnatch(7*[0-9]/". $field))
return "Пароль требует 1 символа из каждого набора a-z A-Z и 0-9<br />": return
}
function validate^age($field) {
if ($field = "") return "He введен возраст<Ьг />":
else if (Sfield < 18 11 $field > 110)
return "Возраст должен быть между 18 и 110<br />” return "":
}
function validate_email($ 1 field) {
if ($field = "") return "He введен адрес электронной почты<Ьг />".
else if (!((strpos($field. ".") > 0) &&
(strpos($field. "Г) >0)) 11pregjnatch("/[^a-zA-Z0-99® .]"$field))
return "Электронный адрес имеет неверный формат<Ьг />": return "":
}
function fix string($string) {
if (getmagicquotesgpcO) $string = strips'!ashes(Sstring);
return htmlentities (Sstring):
} ?>
Резу. млат отправки формы мри отключенном JavaScript (и двумя неправильно заполненными нолями) показан на рис. 16.5.
РНР-раздел этого кода и изменения, внесенные в HTML-раздел, выделены полужирным шрифтом, чтобы сделать заметнее все отличия кода этого примера от кода, который был показан в примерах 16.1 и 16.2.
Если вы внимательно изучили этот пример (или, будем надеяться, набрали или загрузили его с веб-сайта http://lpmj.net), то увидели, что код РНР является практически клоном кода JavaScript. Для проверки каждого из полей в очень похожих функциях используются тс же самые регулярные выражения.
Но здесь следует отметить две особенности. Во-первых, для обезвреживания содержимого каждого поля и предотвращения любых попыток внедрения кода используется функция f ix string, которая находится в самом конце примера.
Во-вторых, вы должны были заметить, что код HTML из примера 16.1. повторенный в РНР-кодс внутри структуры " ' END. JNI): отображает форму со значениями, которые посетитель ввел при предыдущей попытке заполнения формы. Это сделано за счет простого добавления еще одного параметра value к каждому тегу <input> (например, valup="$forename"). Проявление такой заботы о пользователе всячески приветствуется, поскольку при этом ему не приходится снова и снова заполнять все поля, а остается лишь отредактировать ранее введенные данные.
Пример формы Mozilla FirefoxI-
Файл Правка Вид Журнал Закладки Инструменты Справка
^СULl ft :0 http://localho5t/v\'eb/addu5er.php1'u? '
Регистрационная форма
к сожапению. в вашей форме найдены следующие ошибки
истрироватьея 1истрироватьея 1В ииеени гтагеклаж&тя допусгэюяси малого букъы. цифра. - ц Пар<v:o шр&бчыг 1 эдмео*гэ и; *3*doto набора a-z. A-Z и 0-9В ииеени гтагеклаж&тя допусгэюяси малого букъы. цифра. - ц Пар<v:o шр&бчыг 1 эдмео*гэ и; *3*doto набора a-z. A-Z и 0-9
истрироватьея 1
истрироватьея 1
В ииеени гтагеклаж&тя допусгэюяси малого букъы. цифра. - ц Пар<v:o шр&бчыг 1 эдмео*гэ и; *3*doto набора a-z. A-Z и 0-9
В ииеени гтагеклаж&тя допусгэюяси малого букъы. цифра. - ц Пар<v:o шр&бчыг 1 эдмео*гэ и; *3*doto набора a-z. A-Z и 0-9
Готово
Рис. 16.5. Форма, отображаемая после того, как она не прошла проверку средствами РНР
При разработке реального проекта вы вряд ли стали бы сначала создавать HTML-форму h V вроде той, что показана в примере 16.1. Скорее всего, вместо этого вы сразу перешли бы к созданию РНР-лрограммы, показанной в примере 16.3, в которую включен весь код HTML. И разумеется, вам потребовались бы небольшие доработки для первого вызова этой программы, для того чтобы заблокировать отображение ошибок при еще незаполненных полях. К тому же следовало бы выделить шесть функций JavaScript в отдельный включаемый файл с расширением JS.
После рассмотрения способа объединения кода РНР, HTML и JavaScript в следующей главе будет представлена технология Ajax (Asynchronous JavaScript And XML —асинхронный JavaScript и XML), в которой используются фоновые JavaScript-вькювы. обращенные к серверу с целью получения плавного обновления фрагментов веб-страницы, при котором не требуется повторная отправка всего ее содержимого с веб-сервера.
Проверьте ваши знания
Каким методом JavaScript можно воспользоваться, чтобы послать данные формы на проверку перед их отправкой па сервер?
Какой метод JavaScript применяется для проверки соответствия строки регул я р ному вы раж е н и к)?
Используя определения синтаксиса регулярных выражений, напишите такое регулярное выражение, которое будет соответствовать любым символам, не использующимся в словах.
Напишите регулярное выражение, которое будет соответствовать как слову fox, так и слову fix.
Напишите регулярное выражение, которое будет соответствовать любому отдельному слову, за которым следует любой символ, не использующийся в словах.
Используя регулярное выражение, напишите функцию JavaScript, проверяющую наличие слова fox в строке The quick brown fox.
Используя регулярное выражение, напишите функцию РНР, заменяющую все экземпляры слова the в строке The cow jumps over the moon словом my.
Какое ключевое слово HTML используется для предварительного заполнения полей формы значениями?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 16».
J4
"J Использование ш технологии Ajax
Термин Ajax был придуман в 2005 году Изначально он означал «асинхронный JavaScript и XML* (Asynchronous JavaScript and XML), хотя это определение несколько устарело. Проще говоря, Ajax является технологией веб-разработки, использующей нас-op методов, которые встроены в JavaScript, для обмена данными между браузером и сервером в фоновом режиме. Превосходным примером применения этой технологии является Google Maps (рис. 17.1), где новый участок карты загружается с сервера по мере необходимости, для чего не требуется обновление всей страницы.
; Щ Google Macs - ttoU гг-
Sion m | HelpSion m | HelpРис. 17.1. Google Maps — превосходный пример использования технологии AjaxРис. 17.1. Google Maps — превосходный пример использования технологии Ajax
Sion m | Help
Sion m | Help
Рис. 17.1. Google Maps — превосходный пример использования технологии Ajax
Рис. 17.1. Google Maps — превосходный пример использования технологии Ajax
Использование Ajax не только приводит к существенному снижению объема обмена данными, но и обеспечивает плавную динамичность веб-страниц, делая их поведение характерным для самостоятельных приложений. В результате значительно улучшается пользовательский интерфейс и ускоряется реакция на действия пользователя.
Что такое Ajax?
Современный Ajax начинался в 1999 году с выпуска Internet Explorer 5, где был представлен новый ActiveX-объект XMLHttpRequest. Разработанная в корпорации Microsoft технология ActiveX предусматривает использование дополнительных программных модулей, устанавливаемых на ваш компьютер. Позже разработчики других браузеров поддержали этот почин, но вместо применения ActiveX они разработали функциональный модуль, ставший неотъемлемой частью интерпретатора JavaScript.
Но к тому времени уже появилась ранняя форма Ajax, использующая на странице скрытые фреймы, которые взаимодействуют с сервером в фоновом режиме. Самыми первыми потребителями этой технологии были участники форумов, которые задействовали ее для опроса и отображения новых сообщений без перезагрузок страницы.
Давайте посмотрим, как реализовать Ajax, используя JavaScript.
Использование XMLHttpRequest
Из-за различий в реализации XMLHttpRequest, имеющихся в разных браузерах, возникает необходимость в создании специальной функции, обеспечивающей работу вашего кода во всех основных браузерах. Для этого необходимо разобраться с тремя способами создания объекта XMLHttpRequest:
О IE 5: request = new ActiveXObject("Microsoft.XMLHTTP");
О IE6+: request = newActiveX0bject("Msxml2.XMLHTTP");
О все остальные браузеры: request = new XMLHttpRequest().
Дело в том, что Microsoft с выпуском Internet Explorer 6 решила внести изменения, тогда как все остальные браузеры используют несколько иной метод. Поэтому код, показанный в примере 17.1, будет работать во всех основных браузерах, выпущенных за последние несколько лет.
Пример 17.1. Кросс-браузерная Ajax-функция
<script>
function ajaxRequestO
{
try // Браузер не относится к семейству IE?
{
var request = new XMLHttpRequest()
}
catch(el)
{
try // Это IE 6+?
{
request = new Act'veXQb :ect("Msxml2.XMLHTTP")
}
catch(e2)
{
try // Это IE 5?
request = --e* -:tiveXObject("Microsoft.XMLHTTP”)el- Данный браузер не поддерживает Ajax
}
returr
}
</script>
Вспомним элементарные способы обработки ошибок из предыдущей главы, где применялась конструкция try. . .catch. Код примера 17.1 является прекрасной иллюстрацией той пользы, которую можно извлечь из применения этой конструкции, поскольку б этом коде ключевое слово try задействуется для выполнения Ajax-команлы не в формате IE и в случае успеха — перехода к завершающей инструкции '■е:---. возвращающей новый объект. Если все же команда даст сбой, с помощью инструкции ZzZtn осуществляется перехват ошибки и выполняется следующая команда. И опять в случае успеха возвращается новый объект, а в случае неудачи предпринимается попытка выполнения последней из трех команд. Если эта попытка окажется неудачной, значит, браузер не поддерживает Ajax и объект request получает значение fal se, а в случае удачи возвращается полноценный объект. Итак, теперь у вас есть кросс-браузерная функция запроса Ajax, которую можно будет добавить к своей библиотеке полезных функций JavaScript.
Теперь, когда вы располагаете средством для создания объекта XMLHttpRequest, возникает вопрос о том, что можно делать с подобными объектами. Каждый такой объект поступает с набором свойств (переменных) и методов (функций), перечисленных в табл. 17.1 и 17.2 соответственно.
Таблица 17.1. Свойства объектов XMLHttpRequest
Свойство
Описание
onreadystatechange
Определяет функцию обработки события, вызываемую в случае изменения имеющегося в объекте свойства readyState
readyState
Целочисленное свойство, дающее представление о состоянии запроса. Оно может иметь любое из следующих значений: 0 = неинициализирован, 1 = загружается, 2 = загружен, 3 = в состоянии диалога и 4 = завершен
responseText
Данные, возвращенные сервером в текстовом формате
responseXML
Данные, возвращенные сёрвером в формате XML
status
Код статуса HTTP, возвращенный сервером
statusText
Текст статуса HTTP, возвращенный сервером
Таблица 17.2. Методы объектов XMLHttpRequest
Метод
Описание
abort()
Отмена текущего запроса
getAIIResponseHeaders()
Возвращение всех заголовков в виде строк
getResponseHeader( параметр)
Возвращение значения параметра в виде строки
open ('м етод','urf, 'асинхронно')
Определение используемого HTTP-метода (GET или POST), целевого URL-адреса и обязательности обработки запроса в асинхронном режиме (true или false)
send (данны е)
Отправка данных серверу назначения с использованием указанного НТТР-метода
setRequestHeader('параметр1, 'значение')
Установка в заголовок пары «параметр — значение»
Эти свойства и методы позволяют управлять данными, отправляемыми на сервер и получаемыми в ответ, а также выбирать методы отправки и получения этих данных. Например, можно выбрать формат запрашиваемых данных: текстовый (который может включать HTML и прочие теги) или XML. Можно также решить, какой из методов, POST или GET, следует использовать для отправки данных на сервер.
Рассмотрим сначала метод POST, создав очень простую пару документов: комбинацию из HTML и JavaScript — и PHP-программу для взаимодействия с первым документом через Ajax. Надеюсь, вам понравятся эти примеры, поскольку в них иллюстрируется цель использования Web 2.0 и Ajax. В этих примерах за счет использования всего лишь нескольких строк JavaScript у стороннего веб-сервера запрашивается веб-документ, который затем возвращается браузеру вашим сервером и размещается в определенном разделе текущего документа.
Реализация Ajax с помощью POST-запросов
Наберите и сохраните в файле url post. html код примера 17.2, но пока не загружайте его в свой браузер.
Пример 17.2. urlpost.html
<html><head><titlе>Пример использования Ajax</title>
</headxbody><center />
<hl>3aгрузка веб страницы в контейнер DIV</hl>
<div id= ’ i nfo' >Это предложение будет заменено^! v>
<script>
params = "url=oreilly.com"
request = new ajaxRequestO
request.open("POST", "urlpost.php", true)
request. setRequestHeaderCContent-type".
"application/x-www-form-urlencoded")
request.setRequestHeader("Content-1ength". pa rams.1ength) request.setRequestHeader("Connection". "close")
request, onreadystatechange = functionO
if (this.readyState == 4)
{
if (this.status == 200)
{
if (this.responseText != null)
{
document.getElementBylcK ’info') .innerHTML = this.responseText
}
else alerIt("Ошибка Ajax: Данные не получены”)
}
else alert( "Ошибка Ajax: " + this.statusText)
request.send(pa rams)
function ajaxRequestC)
{
try
{
var request = new XMLHttpRequestO
}
catch(el)
{
try
{
request = new ActiveX0bjectC'Msxml2.XMLHTTP")
}
catch(e2)
{ try { request = new ActiveXObjectC'Microsoft.XMLHTTP") } catch(e3)
{ request = false
}
}
}
return request
}
</scri pt></bodyx/html >
Изучим этот документ построчно и посмотрим, что он делает, начиная с первых трех строк, в которых устанавливается, что это HTML-документ, и отображается его заголовок. В следующей строке создается <div>-Ter с ID info, в котором изначально содержится текст: «Это предложение будет заменено». Позже сюда будет вставлен текст, возвращенный с помощью технологии Ajax.
Следующие шесть строк нужны для создания Ajax-3anpoca -^~Р POST. В первой строке переменной params, которая отправляется на сервер, присваивается значение, состоящее из пары параметр = значение. Затем создается Ajax-объект запроса. После этого вызывается метод open, настраивающий объект на создание POST-запроса по адресу url post, php в асинхронном режиме. Последние три строки в этой группе настраивают заголовки, необходимые для того, чтобы получающий сервер знал о поступлении POST-запроса.
Свойство readyState
Теперь мы наконец добрались до самых тонкостей Ajax-вызова, которые целиком базируются на использовании свойства readyState. Наряду с тем. что наша программа настраивает свойство onreadystatechange на то, чтобы при каждом изменении свойства readyState вызывалась выбранная нами функш!я. «асинхронность» Ajax позволяет браузерам реагировать на пользовательский ввод и изменять содержимое экрана. В данном случае будет использоваться не отдельная ф ун кция, имеющая собственное имя, а безымянная (или анонимная) встроенная функция. Она относится к так называемым функциям обратного вызова, пссхолъку вызывается при каждом изменении свойства readyState.
Синтаксис объявления функции обратного вызова втрвменяется
встроенная безымянная функция, имеет следующий ж.т
request, onreadystatechange = functionO
if (this.readyState == 4)
{ // какие-нибудь действия
}
}
Если нужно воспользоваться отдельной функш«ег- zм - жоэ:гмя.
применяется несколько иной синтаксис:
request.onreadystatechange = ajaxCal1 back
function ajaxCallbackO {
if (this.readyState — 4)
{ // какие-нибудь действия
}
}
При изучении табл. 17.1 можно увидеть, что у сэгггтзг. l i r *?:•—: :~£ть пять разных значений. Но только одно из них прелст-твгт ллз ези «е~?:«т-т зе^ч-г- ние 4, которое свидетельствует о завершении выэса£ А х йс:уж-- у
вызове новой функции она возвращает управлениезёкг-алл h i тгх
пор, пока свойство readyState не получит значение-. Kzcz* уу-чт-»^
это значение, следующим своим действием онагтгу-:Ц е-
диться в том, что он имеет значение 200, означающее. чп ьъз: 5
Если этот статус не равен 200, выводится окно предупреждения с сообщением об ошибке, которое содержится в свойстве statusText.
Обратите внимание на то, что на все эти свойства объекта идут ссылки this.readyState, this. « status и т. д., без использования текущего имени объекта request, как в ссылках request. *1 readyState или request.status. Это сделано для того, дать вам возможность просто скопировать и вставить код и чтобы он после этого смог работать с любым именем объекта, поскольку ключевое слово this всегда ссылается на текущий объект.
Итак, после того, как установлено, что readyState равен 4, a status равен 200, проверяется наличие значения у свойства responseText. Если значение отсутствует, в окне предупреждения выводится сообщение об ошибке. В противном случае содержимому контейнера <div> присваивается значение свойства responseText:
document.getElementById(’info') .innerHTML = this.responseText
В этой строке с помощью метода getEl ementBy ID осуществляется ссылка на элемент info, а затем его свойству innerHTML присваивается значение, возвращенное Ajax-вызовом.
После всех этих настроечных и подготовительных действий Ajax-запрос наконец-то посылается на сервер с помощью следующей команды, которой передаются параметры, заранее определенные в переменной params:
request.send(params)
Затем весь предыдущий код активизируется при каждом изменении свойства readyState. В конце документа находятся метод ajaxRequest из примера 17.1 и теги, закрывающие сценарий JavaScript и код HTML.
Серверная половина Ajax-процессэ
Теперь мы добрались до PHP-половины этого уравнения, которая показана в примере 17.3. Наберите этот код и сохраните его в файле u rl post. php.
Пример 17.3. urlpost.php
<?php // urlpost.php
if (isset($_PDSST’uuli])) {
echo file_get_contents("http://".SanitizeString($_P0ST['url']));
■'unction SanitizeString(Svar) { $var = strip_tags($var).;
Svar = htmlentities($var); return stripslashes($var);
Как видите, этот код невелик по объему и использует неизменно актуальную функцию обезвреживания содержимого строки — SanitizeString, которая должна применяться ко всем отправляемым в адрес сервера данным.
В этой программе для загрузки веб-страницы, которая находится по URL-адресу, представленному в POST-переменной $_P0ST['url'], используется PHP-функция file_ get contents. Эта функция обладает достаточной универсальностью, позволяющей ей загружать все содержимое файла или веб-страницы как с локального, так и с удаленного сервера, — она даже учитывает перемещенные страницы и другие перенаправления.
После набора программы можно будет вызвать в веб-браузере файл url post, html, и через несколько секунд должна появиться первая страница сайта oreilly.com, содержимое которой загружено в <di у>-контейнер, созданный нами для этих целей. Произойдет это не так быстро, как при непосредственной загрузке веб-страницы, поскольку данные переносятся дважды: сначала на сервер, а потом с сервера на браузер. Результат должен быть похож на тот, что показан на рис. 17.2.
® AJAX POST Example - Manila firefox£Ие gtHt Sew Hgbxy fiookmwte Joote И фС?Ж; д ( Li I http://toca<hort/uriposthtml® AJAX POST Example - Manila firefox£Ие gtHt Sew Hgbxy fiookmwte Joote И фС?Ж; д ( Li I http://toca<hort/uriposthtmlL I oading a web page into a DIVL I oading a web page into a DIV1.^Л[5шазва1.^Л[5шазва
® AJAX POST Example - Manila firefox
£Ие gtHt Sew Hgbxy fiookmwte Joote И ф
С?Ж; д ( Li I http://toca<hort/uriposthtml
® AJAX POST Example - Manila firefox
£Ие gtHt Sew Hgbxy fiookmwte Joote И ф
С?Ж; д ( Li I http://toca<hort/uriposthtml
L I oading a web page into a DIV
L I oading a web page into a DIV
1.^Л[5шазва
1.^Л[5шазва
- Co ; Search Tip*- Co ; Search Tip*POPULAR TOPICSPOPULAR TOPICSNEWS & COMMENTARYNEWS & COMMENTARYREGISTER NOW AND SAVE!REGISTER NOW AND SAVE!Actionscnpt Apach* CSSHead FirstJava Linux MySQL P*riActionscnpt Apach* CSSHead FirstJava Linux MySQL P*riPhotoshop Ruby XMLPhotoshop Ruby XMLAfaxC#Flex iPhon*JavaScript Missing Manuals Open Source PHPPythonW«b 2.0AfaxC#Flex iPhon*JavaScript Missing Manuals Open Source PHPPythonW«b 2.0[Archive IAudio IVideo I Popular Tags][Archive IAudio IVideo I Popular Tags]ETech Preview: Science Commons Wants Data to Be FreeETech Preview: Science Commons Wants Data to Be FreeBROWSE BOOKSBusiness & CultureDigital CulturePersonal FinanceProject & CareerManagement►DatabasesBROWSE BOOKSBusiness & CultureDigital CulturePersonal FinanceProject & CareerManagement►DatabasesJohn Wilbanks has a passion for lowering the barrier between scientists who want to share information. A graduate of Tulane University, Mr. WHbanks started his career working as a legislative aide, before moving on to pursue work inJohn Wilbanks has a passion for lowering the barrier between scientists who want to share information. A graduate of Tulane University, Mr. WHbanks started his career working as a legislative aide, before moving on to pursue work inbtoinformatics, which induded the founding of Incellico, a company which built semantic graph networks for use in pharmaceutical research and development Hr. Wilbanks now serves as the Vice President of Science at Creativebtoinformatics, which induded the founding of Incellico, a company which built semantic graph networks for use in pharmaceutical research and development Hr. Wilbanks now serves as the Vice President of Science at CreativeCommons, and runs the Science Commons project. He will be speaking at The O'Reilly Emerging Technology Conference in March, on the challenges and accomplishments of Science Commons, and he's joining us today to talk a bit about it. Read more.Commons, and runs the Science Commons project. He will be speaking at The O'Reilly Emerging Technology Conference in March, on the challenges and accomplishments of Science Commons, and he's joining us today to talk a bit about it. Read more.ETech ETech — Living, Reinvented: The Technology of Abundance and Constraints. Happening March 9-12 in San Jose, CA. Register Now andSave up to $300!Web 2.0Web 2.0 ExpoExpoSan Francisco— The Power of Less.Happening March 31-April 3 inSan Francisco. Register Nowand Save up to $3501Upcoming Conferences• O’Reilly EmergingTechnology ConferenceO'Reilly Web 2.0 Expo San FranoscoO'Reilly MySQL Conference• O'Reilly RailsConfI* More from ConferencesETech ETech — Living, Reinvented: The Technology of Abundance and Constraints. Happening March 9-12 in San Jose, CA. Register Now andSave up to $300!Web 2.0Web 2.0 ExpoExpoSan Francisco— The Power of Less.Happening March 31-April 3 inSan Francisco. Register Nowand Save up to $3501Upcoming Conferences• O’Reilly EmergingTechnology ConferenceO'Reilly Web 2.0 Expo San FranoscoO'Reilly MySQL Conference• O'Reilly RailsConfI* More from ConferencesStay ConnectedSubscribe to ourNewslettersO'Reilly on YouTubeO'Reilly on TwitterO'Reilly on FacebookNews FeedNew Titles FeedUpcoming Titles FeedI*New to RSS?Stay ConnectedSubscribe to ourNewslettersO'Reilly on YouTubeO'Reilly on TwitterO'Reilly on FacebookNews FeedNew Titles FeedUpcoming Titles FeedI*New to RSS?Satisfied CustomersAn Anonymous Readerread:Baseball Hacks"Some suggestions toreaders**, Feb. 21 2009Rating: *****Satisfied CustomersAn Anonymous Readerread:Baseball Hacks"Some suggestions toreaders**, Feb. 21 2009Rating: *****
- Co ; Search Tip*
- Co ; Search Tip*
POPULAR TOPICS
POPULAR TOPICS
NEWS & COMMENTARY
NEWS & COMMENTARY
REGISTER NOW AND SAVE!
REGISTER NOW AND SAVE!
Actionscnpt Apach* CSS
Head First
Java Linux MySQL P*ri
Actionscnpt Apach* CSS
Head First
Java Linux MySQL P*ri
Photoshop Ruby XML
Photoshop Ruby XML
Afax
C#
Flex iPhon*
JavaScript Missing Manuals Open Source PHP
Python
W«b 2.0
Afax
C#
Flex iPhon*
JavaScript Missing Manuals Open Source PHP
Python
W«b 2.0
[Archive IAudio IVideo I Popular Tags]
[Archive IAudio IVideo I Popular Tags]
ETech Preview: Science Commons Wants Data to Be Free
ETech Preview: Science Commons Wants Data to Be Free
BROWSE BOOKS
Business & Culture
Digital Culture
Personal Finance
Project & CareerManagement
►Databases
BROWSE BOOKS
Business & Culture
Digital Culture
Personal Finance
Project & CareerManagement
►Databases
John Wilbanks has a passion for lowering the barrier between scientists who want to share information. A graduate of Tulane University, Mr. WHbanks started his career working as a legislative aide, before moving on to pursue work in
John Wilbanks has a passion for lowering the barrier between scientists who want to share information. A graduate of Tulane University, Mr. WHbanks started his career working as a legislative aide, before moving on to pursue work in
btoinformatics, which induded the founding of Incellico, a company which built semantic graph networks for use in pharmaceutical research and development Hr. Wilbanks now serves as the Vice President of Science at Creative
btoinformatics, which induded the founding of Incellico, a company which built semantic graph networks for use in pharmaceutical research and development Hr. Wilbanks now serves as the Vice President of Science at Creative
Commons, and runs the Science Commons project. He will be speaking at The O'Reilly Emerging Technology Conference in March, on the challenges and accomplishments of Science Commons, and he's joining us today to talk a bit about it. Read more.
Commons, and runs the Science Commons project. He will be speaking at The O'Reilly Emerging Technology Conference in March, on the challenges and accomplishments of Science Commons, and he's joining us today to talk a bit about it. Read more.
ETech ETech — Living, Reinvented: The Technology of Abundance and Constraints. Happening March 9-12 in San Jose, CA. Register Now and
Save up to $300!
Web 2.0Web 2.0 Expo
ExpoSan Francisco
— The Power of Less.
Happening March 31-April 3 inSan Francisco. Register Nowand Save up to $3501
Upcoming Conferences
• O’Reilly EmergingTechnology Conference
O'Reilly Web 2.0 Expo San Franosco
O'Reilly MySQL Conference
• O'Reilly RailsConf
I* More from Conferences
ETech ETech — Living, Reinvented: The Technology of Abundance and Constraints. Happening March 9-12 in San Jose, CA. Register Now and
Save up to $300!
Web 2.0Web 2.0 Expo
ExpoSan Francisco
— The Power of Less.
Happening March 31-April 3 inSan Francisco. Register Nowand Save up to $3501
Upcoming Conferences
• O’Reilly EmergingTechnology Conference
O'Reilly Web 2.0 Expo San Franosco
O'Reilly MySQL Conference
• O'Reilly RailsConf
I* More from Conferences
Stay Connected
Subscribe to ourNewsletters
O'Reilly on YouTube
O'Reilly on Twitter
O'Reilly on Facebook
News Feed
New Titles Feed
Upcoming Titles Feed
I*New to RSS?
Stay Connected
Subscribe to ourNewsletters
O'Reilly on YouTube
O'Reilly on Twitter
O'Reilly on Facebook
News Feed
New Titles Feed
Upcoming Titles Feed
I*New to RSS?
Satisfied Customers
An Anonymous Readerread:
Baseball Hacks"Some suggestions toreaders**, Feb. 21 2009Rating: *****
Satisfied Customers
An Anonymous Readerread:
Baseball Hacks"Some suggestions toreaders**, Feb. 21 2009Rating: *****
O’REILLY
Search
• »• • •
Рис. 17.2. Первая страница веб-сайта oreilly.com, загруженная в <сПу>-контейнер
Мы не только добились осуществления Ajax-вызова и получения ответа, возвращенного JavaScript, мы также воспользовались способностью РНР объединять совершенно не связанные друг с другом веб-объекты. Кстати, если бы мы попытались найти способ извлечения веб-страницы oreilly.com непосредственно через Ajax (без обращения к PHP-модулю на стороне сервера), у нас ничего бы не вышло, поскольку существуют блоки безопасности, не допускающие кросс-доменного применения технологии Ajax. Поэтому данный небольшой пример показывает также удобное решение весьма актуальной практической задачи.
Использование GET вместо POST
При отправке данных любой формы можно выбрать GET-запросы, сэкономив на этом несколько строк кода. Но у таких запросов есть недостаток: некоторые браузеры могут кэшировать GET-запросы, притом что POST-запросы кэшированию никогда не подвергаются. Кэширование запроса нежелательно, потому что браузер просто-напросто заново отобразит то, что он получил в последний раз, и не станет обращаться к серверу за свежими входными данными. Решить эту проблему можно, применив обходной маневр, заключающийся в добавлении к каждому запросу произвольного параметра, обеспечивающего уникальность каждого запрашиваемого URL-адреса.
В коде примера 17.4 показано, как можно добиться такого же результата, который был получен при использовании кода примера 17.2, но на этот раз применяя в Ajax не POST-, а GET-запрос.
Пример 17.4. urlget.html
<html><head><titlе>Пример Ajax с GET-3anpocoM</title>
</head><body><center />
<И1>Загрузка веб-страницы в 01\/-контейнер</111>
<div id=’infoг>Это предложение будет 3aMeHeH0</div>
<script>
nocache = "&nocache=H+ Math.randomO * 1000000
request = new ajaxRequestO
request.open("GET". "urlget.php?url=oreilly.com" + nocache, true)
request .onreadystatechange = functionO
{
if (this.readyState == 4)
{
if (this.status == 200)
{
if (this.responseText != null)
{
document.getElementById(’info’).innerHTML =this.responseText
}
else alert("Ошибка Ajax: Данные не получены '')
}
else alert( "Ошибка Ajax: " + this.statusText)
}
•'equest. send (null)
■jnction ajaxRequestO
try
var request = new XMLHttpRequestOvar request = new XMLHttpRequestO{
var request = new XMLHttpRequestO
var request = new XMLHttpRequestO
}
catch(el)
{
try
{
request = new ActiveX0bject("Msxml2.XMLHTTP")
1
catch(e2)
{
try
{
request = new ActiveXObject.("Microsoft.XMLHTTP")
}
catch(e3)
{ request = false
}
}
1
return request
}
</script></body></html>
Различия между этими двумя документами, на которые следует обратить внимание, выделены полужирным шрифтом и состоят в следующем.
О Для GET-запроса не требуется отправка заголовков.
О Метод open вызывается с использованием GET-запроса с предоставлением URL- адреса, строка которого содержит символ ?, за которым следует пара «параметр — значение» —url =orei 1 ly. com.
Q Вторая пара «параметр —значение» начинается с использования символа &, за которым для параметра nocache устанавливается случайное значение из диапазона от 0 до 1 ООО ООО. Такой прием обеспечивает разное содержимое каждого запрашиваемого URL-адреса, что препятствует обслуживанию запросов из кэша.
О Вызов метода send теперь содержит только параметр nul 1, поскольку здесь отсутствуют параметры, которые передаются при POST-запросе. Учтите, что опускать этот параметр нельзя, поскольку это вызовет ошибку.
Для сопровождения нового документа необходимо изменить РНР-программу так, чтобы она отвечала на GET-запрос. Файл ur 1 get. php, в котором содержится код программы, показан в примере 17.5.
Пример 17.5. urlget.php
<?php
if (isset($_GET['url' ])) {
echo filejjetjrontentsChttp://" .sanitizeString($_GET[ 'urIl' ])):
function sanitizeString(Svar) { Svar = strip_tags($var):
$var = htm lentities($var);
return strips lashes(Svar):
? >
Разница между этим кодом и кодом примера 17.3 заключаются в том, что ссылка на массив S P0S1 заменена ссылкой на массив S_GET. Конечный результат вызова ur 1gett. html в вашем браузере будет идентичен резул ьтату вызова url post. html.
Отправка XML-запросов
Хотя создаваемые нами объекты называются объектами XMLHttpRequest, пока мы обходились без использования XML. В рассмотренных случаях применения данной технологии использование термина Ajax было в какой-то степени неоправданным, поскольку фактически технология позволяет запрашивать текстовые данные любого вида, а не только те, которые относятся к формату XML. Вы смогли убедиться в том, что мы запрашивали с помощью Ajax весь HTML-документ, но могли бы с таким же успехом запросить текстовую страницу, числовую строку или даже данные электронной таблицы.
Внесем изменения в приведенный ранее пример документа и РНР-программы и настроим их на извлечение данных в формате XML. Для этого рассмотрим сначала PHP-программу xml get. php, показанную в примере 17.6.
Пример 17.6. xml get.php
- ?php
■f (isset(S_GE1[ ’uгrl'])) {
header('Content-Type: text/xml’):
echo fi leget contentsC"http ://”.samtizeStrmg($_GET[ 'url' j))):
int. t. ion sam t. i i/eStri i ng($var) { Svar - stn1p)tags(Svar): Svar = htmlent I it i es($var): return strips I ashes(Svar);
Эта программа по сравнению с предыдущей подверглась небольшому изменению (которое выделено полужирным шрифтом), чтобы перед возвращением извлеченного документа выводился правильный XML-заголовок. Здесь не выполняются никакие проверки, поскольку предполагается, что Ajax-вызов запросит настоящий XML-документ.
Теперь рассмотрим HTML-документ xmlget.html, показанный в примере 17.7.
Тример 17.7. xmlget.html
><head><t.i11е>Пример извлечения XML с помощью Ajax</title>
^ead><body>
'I'Загрузка XMI содержимого в D1V-контейнераVh2■>
<div id='info ’>Это предложение будет 3aMeHeH0</div>
<script>
nocache = ,,&nocache=" + Math.randomO * 1000000
url = "rss.news.yahoo.com/rss/topstories"
request = new ajaxRequestO
request.open("GET”. "xmlget.php?url=" + url + nocache, true) out =
request .onreadystatechange = functionO {
if (this.readyState == 4)
{ if (this.status == 200)
{
if (this.responseXML != null)
{
tittles = this.responseXML.getElementsByTagName('title')
for (j = 0 : j < titles.length : ++j) {
out += titles[j].childNodes[0].nodeValue + '<br />'
}
document.getElementById('i nfo') . i nnerHTML = out
}
else alert("Ошибка Ajax: Данные не получены”) }
else alert( "Ошибка Ajax: " + this.statusText)
request.send(null)
function ajaxRequestO {
try
{
var request = new XMLHttpRequest()
}
catch(el)
{
try
{
request = new ActiveX0bject("Msxml2.XMLHTTP")
}
catch(e2)
{ try
{
request = new ActiveXObjectC'Microsoft.XMLHTTP")
}
catch(e3)
{ request = false
}
}
}
return request
}
</scri pt></bodyx/htrnl >
В этом коде все различия также выделены полужирным шрифтом, чтобы вы могли увидеть, что он очень похож на предыдущие версии, за исключением того, что теперь запрашивается URL-адрес rss.news.yahoo.com/rss/topstories, по которому находится XML-документ, содержащий поток последних новостей — Yahoo! News Top Stories feed.
Другое существенное изменение касается использования свойства responseXML, которым заменено свойство responseText. Когда сервер возвращает XML-данные, свойство responseText возвращает значение nul 1, а свойство responseXML будет содержать возвращенные XML-данные.
Но responseXML не просто содержит строку XML-текста — на самом деле в нем содержится полноценный объект XML-документа, который может быть проанализирован с использованием методов и свойств DOM-дерева. Это, к примеру, означает, что к нему можно применить JavaScript-метод getElementsByTagName.
Несколько слов о XML
Документ XML, как правило, имеет форму RSS-потока, показанного в примере 17.8. Но красота XML заключается в том, что этот тип структуры может быть сохранен внутри DOM-дерева (рис. *17.3), что дает возможность выполнять в нем быстрый поиск.
Рис. 17.3. DOM-дерево примера 17.8
Пример 17.8. Документ XML
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0M>
<channel>
<title>RSS-n0T0K</title>
<1i nk>http ://website.com</li nk>
<description>RSS-поток website.com </description>
<риЬ0а1е>Понедельник. 16 мая 2011 года. 00:00:00 GMT</pubDate> <item>
<ti 11e>3a r0 H0 B0 K</ti 11e>
<gui d>http ://websi te .com/headli ne</gui d>
<description>3io 3ai^0B0K</descripti0n>
</item>
<item>
<title>3aголовок 2</title>
<guid>http://website.com/headline2</guid>
<descriptiоп>Второй за^OBOK</descri ption>
</item>
</channel>
</rss>
Следовательно, используя метод getEl ementsByTagName, можно быстро извлечь значения, связанные с различными тегами, не занимаясь громоздким строчным поиском. Именно это и делается в коде примера 17.7, где выдается следующая команда:
titles = this.responseXML.getElementsByTagName('title')
За счет выполнения только этой одной команды все значения элементов title помещаются в массив titles. После этого остается лишь извлечь их с помощью следующего выражения (где j — порядковый номер заголовка, к которому осуществляется доступ):
titles[j].childNodes[0].nodeValue
Затем все заголовки добавляются к строковой переменной out, и поскольку все они уже прошли обработку, результат вставляется в пустой <div>-KOHTeftHep в начале документа. При вызове в браузере файла xml get. html будет получен результат, похожий на тот, что показан на рис. 17.4.
При запросе XML-данных, равно как и при работе со всеми другими формами данных, мож- « но воспользоваться либо методом POST, либо методом GET — ваш выбор на результат не повлияет.
А зачем вообще использовать XML?
Может возникнуть вопрос, а для чего еще можно применять XML, кроме как для извлечения XML-документов в виде RSS-потоков? Проще всего ответить, что пользоваться им вас никто не заставляет, но если вам нужно возвращать структурированные данные своим Ajax-приложениям, то отправка простых, неорганизо-
ванных фрагментов текста может превратиться в настоящую проблему, для решения которой потребуется довольно сложная обработка в JavaScript.
' Призер извлечения XML с помощью Ajax - Mozilld FirefoxQ&ifxJ
Файл Праека §ид Журнал Закладки Инструменты ^правка т С Ж Hf Гф (Q http://kxcdhost/web/xmlget.html ft ^
• ЛИГ- ._.__ _ _______ _.__——_ -—.—.
Загрузка XML-содержимого в DIV-контейнер
Yahoo! News Top StonesI'-i
Yahoo* News
CIA chief Panetta: US has dnven back al-Qaida (AP),}
Police arrest more than 500 at Toronto summit (AP)
Pope deplores Belgian sex raids, stresses autonomy (AP)lyj.
Longtime Sen Byrd in hospital, senously ill (AP)
GOP senators: Can Kagan be impartial judge? (AP)‘ ^
Man charged in Pa. stabbing rampage that killed 4 (AP); ^
Oil spill's psychological toll quietly mounts (AP) Scientists say test could predict menopause (AP)
Jay-Z leads nominees at music-filled BET Awards (AP)
Argentina beats Mexico 3-1 at World Cup (AP)
G20 walks tightrope between growth, deficits (Reuters)
Toronto police fired tear gas on G20 protesters (Reuters)S
Готовоj
Рис. 17.4. Извлечение с помощью Ajax новостного XML-потока Yahoo!
Вместо этого можно создать XML-документ и вернуть его Ajax-функции, кото-' рая автоматически поместит его в DOM-дерево в виде уже знакомого вам легкодоступного HTML DOM-объекта.
Использование для Ajax специальной среды
Теперь, когда вы узнали о том, как создавать собственные Ajax-процедуры, можно будет исследовать некоторые из свободно распространяемых программных продуктов, которые представляют собой среду, способную упростить работу с применением этой технологии и предлагающую множество более совершенных функциональных возможностей. В частности, я советую обратить внимание на библиотеку j Query, которая, наверное, является наиболее востребованной средой для работы с применением Ajax.
Ее можно загрузить (и получить полную документацию) с веб-сайта http://jquery. zdm. но знайте, что вас с самого начала ждет крутой поворот в ее изучении, посколь- >гу вам придется познакомиться с предоставляемой этой средой функцией $, кото- гая широко используется для обращения к функциональным возможностям j Query. Тем не менее, разобравшись с работой j Query, вы поймете, что эта среда может существенно облегчить и ускорить веб-разработку благодаря большому количеству ~рех1агаемых ею готовых функций.
Теперь, изучив азы работы технологии Ajax, в следующей главе мы рассмотрим, у^к можно в наш набор технологий веб-разработки ввести каскадные таблицы :ти_гей — CSS (Cascading Style Sheets).
Проверьте ваши знания
Зачем нужна функция для создания новых XMLHttpRequest-объектов?
Для чего предназначена конструкция try... catch?
Сколько свойств и методов имеется у объекта XMLHttpRequest?
Как можно определить завершение Ajax-вызова?
Как узнать об успешном завершении Ajax-вызова?
В каком свойстве объекта XMLHttpRequest содержится текстовый ответ, возвращенный Ajax-вызовом?
В каком свойстве объекта XMLHttpRequest содержится XML-ответ, возвращенный Ajax-вызовом?
Как указать функцию обратного вызова, предназначенную для обработки ответов Ajax-вызова?
Какой метод объекта XMLHttpRequest используется для инициирования Ajax-3a- проса?
В чем состоит основное различие между Ajax GET- и POST-запросом?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 17».
18 Введение в CSS
Используя каскадные таблицы стилей — Cascading Style Sheets (CSS), — вы можете применить стиль к своим веб-страницам, чтобы придать им желаемый внешний вид. Работа CSS основана на их подключении к объектной модели документа — Document Object Model (DOM), которая была рассмотрена в главе 13.
Используя CSS, можно быстро и просто изменить стиль любого элемента. Например, если не нравится исходный вид заголовков, определяемых тегами <hl>, <h2> и т. д., можно назначить новый стиль, отменяющий исходные настройки, касающиеся используемого семейства шрифтов и размера, применения полужирного шрифта или курсива, а также многих других свойств.
Один из способов добавления стилей к веб-странице заключается во вставке требуемых для этого инструкций в заголовок страницы между тегами <head> и </head>. Поэтому для изменения стиля, применяемого к содержимому тега <hl>, нужно воспользоваться следующим кодом (синтаксис которого будет рассмотрен чуть позже):
<style>
hi { col or:red: font-size:3em; font-family:Arial; } </style>
Внутри HTML-страницы этот код может иметь вид, показанный в примере 18.1, в котором, подобно всем остальным примерам, используемым в данной главе, применяется стандартное HTML5-oбъявлeниe DOCTYPE. Результат выполнения этого кода показан на рис. 18.1.
Пример 18.1. Простая HTML-страница
<!DOCTYPE html>
<html>
<head>
<Ш1е>Здравствуй. мир! </title>
<style>
hi { co I loured; font-size:3em; font-family:Arial: }
</style>
</head>
<body>
<hl>BceM привет</1т1>
</body>
</html>
Рис. 18.1. Стилизация тега, оригинальный стиль которого показан во вставке
Импортирование таблицы стилей
Когда стиль нужно применить не к одной странице, а ко всему сайту, лучше управлять стилями путем перемещения их из веб-страниц в отдельные файлы, называемые таблицами стилей, и затем импортировать стиль, который вам нужен. Тем самым достигается минимизация того объема кода, который дублируется на ваших страницах, улучшаются показатели обслуживаемости и предоставляется возможность применения разных таблиц стилей к разным форматам подачи информации (например, в варианте просматриваемой веб-страницы и в варианте вывода на печать) без изменения HTML. Такое отделение содержимого от разметки является фундаментальным принципом дизайна.
Достичь этого можно двумя различными способами, первый из которых заключается в использовании CSS-директивы @import:
<style>
@import url('styles.css');
</style>
Эта инструкция заставляет браузер извлечь таблицу стилей с именем styles css. Гибкость, присущая команде @import, позволяет создавать таблицы стилей, которые сами импортируют другие таблицы стилей, а те, в свою очередь, могут импортировать другие таблицы и т. д. А вот теги <style> и </style> ири вызове внешних таблиц стилей из других таблиц не нужны, и их присутствие сделает инструкцию неработоспособной.
Импортирование таблицы стилей из HTML-кода. Включить таблицу стилей можно также с помощью HTML-тега <link>:
4ink rel ='stylesheet' type='text A ss' href='styles.css' />
Результат будет точно таким же, как и при использовании директивы @import., но< И появляется тегом, применяемым только в HTML, и не относится к стилевым
директивам, поэтому он не может задействоваться в одной таблице стилей для импорта другой такой таблицы. Он также не может помещаться внутри пары тегов <style>.. ,</style>
Точно так же, как в CSS, можно использовать несколько директив (Pimport для включения в состав таблицы стилей нескольких внешних таблиц, в коде HTML можно применять любое нужное количество элементов, задействующих теги <1 ink>.
Встроенные настройки стиля
Можно также выполнять индивидуальные настройки или заменять конкретные стили, вставляя объявления стилей непосредственно в код HTML следующим образом (в данном случае внутри тегов задается курсивный текст синего цвета):
<div sty1е=’font-stylе :italiс; color:blue;'>Всем npMBeT</div>
Но подобные настройки стоит отложить до тех пор, пока не сложатся самые крайние обстоятельства, поскольку они нарушают принцип отделения содержимого от разметки.
Использование идентификаторов (ID)
Более удачным решением для настроек стиля отдельно взятого элемента является назначение формирующему его коду HTML идентификатора
<Jiv id=’iblue'>Всем npnBeT</div>
Тем самым устанавливается, что содержимое <di у>-контейнера с идентификатором, имеющим значение i bl ие, должно иметь применяемый к нему стиль, который определен в стилевых настройках iblue. Соответствующая CSS-инструкция для этого может иметь следующий вид:
=iblue { font -stylе:i tali с: color:blue; }
Обратите внимание на использование символа решетки (#), который указывает на то, чт эта инструкция задает стилевые настройки только для идентификатора по имени iblue.
Использование классов
Если нужно применить один и тот же стиль ко многим элементам, не следует давать каждому из них особый ID, поскольку можно указать класс для управления всеми этими элементами:
:iv class='iblue'>npnBeT</div>
Тем самым утверждается, что стиль, определенный в классе iblue, должен применяться к содержимому данного элемента (и любых других элементов, относящихся к этому классу). При использовании класса можно либо в заголовке страницы, либо во внешней таблице стилей задействовать для настройки стилей класса следующее правило:
.iblue { font- style:italic; col or:blue: }
Вместо использования символа решетки (#), который закреплен за идентификаторами (ID), инструкции, относящиеся к классу предваряются символом точки (.)I.
Правила CSS
Правило CSS является инструкцией или последовательностью инструкций, предписывающих браузеру способ отображения конкретного элемента или элементов на странице. Каждая инструкция в CSS-правиле начинается с селектора, являющегося элементом, к которому будет применяться правило. Например, в следующем назначении hi является селектором, для которого задается размер шрифта на 240 % больше чем у того, который используется по умолчанию:
hi { font-size:240%; }
Все имеющиеся в правилах изменяемые свойства должны быть расположены внутри символов { и } и следовать за селектором. Часть, которая находится перед двоеточием (в данном случае это font-size) является изменяемым свойством, а часть, которая располагается после двоеточия, является применяемым к этому свойству значением (в данном случае это 240%). Задавая для принадлежащего селектору hi свойства font -size значение 240%, мы гарантируем, что содержимое тегов <h 1>... </h 1> будет отображено с размером шрифта, превосходящим на 240 % исходный размер.
И наконец, следует точка с запятой (;), завершающая инструкцию. В данном примере, поскольку font-size является последним свойством правила, точка с запятой не требуется (но она должна присутствовать, если за этим свойством будет задано значение еще одного свойства).
Использование точек с запятой
В CSS точки с запятой применяются в качестве разделителей нескольких инструкций CSS, расположенных в одной и той же строке. Но при наличии в правиле только одной инструкции (или при встраивании настройки стиля в HTML-теге) точку с запятой можно опустить, и то же самое можно сделать в отношении последней инструкции в группе.
Но чтобы при использовании CSS избавиться от ошибок, которые трудно будет распознать, можно взять за правило использование точки с запятой после каждой настройки CSS. Затем их можно копировать и вставлять или же изменять свойства, не заботясь об удалении точек с запятой там, где они, в принципе, не нужны, или о добавлении их туда, где они необходимы.
Множественные задания стиля
Задать несколько стилевых настроек можно двумя разными способами. Можно объединить их в одной строке:
hi { font-size:240%; col or:blue: }
Здесь добавлено торос* задание стиля, изменяющее цвет всех заголовков, зада ваемых тегом <hl>, на синий. Можно также расположить задания построчно:
''1 { font s I/с»; ;М0Ж:
о lor:blue: }
Или же можно разнести задания еще дальше, расположив столбцами по двое- гоч иям:
lor it. м/е Х40Й:
<(I Iorblue;
Тогда будет проще заметить, где начинается каждый новый набор правил, поскольку селектор всегда находится в первом столбце, а следующие за ним задания аккуратно выстраиваются благодаря одинаковому горизонтальному смещению всех значений свойств.
В предыдущих примерах замыкающие точки с запятой не нужны, но если придется объединять какие-нибудь подобные группы инструкций в одну строку, то при наличии всех точек с запятой это можно будет сделать довольно быстро.
Один и гот же селектор можно указывать произвольное количество раз, и CSS будет объединять все свойства. То есть предыдущему примеру можно также придать следующий вид:
• . { font-ы/е: 240Я: }
"I { color : blue: }
4« Каких-либо правильных или неправильных способов раскладки кода CSS не существует, но ^ А * я рекомендую вам по крайней мере стараться соблюдать единообразие в построении каж- —— дого блока CSS, чтобы в нем можно было разобраться с первого взгляда.
А что произойдет, если задать одно и то же свойство для одного и того же селектора дважды?
"I { colorred; }
"I { colorblue: }
Будет применено последнее заданное свойство, в данном случае то, которое имеет значение blue. Повторять в одном файле одно и то же свойство для одного и того же селектора было бы бессмысленно, но такие повторения часто бывают при реальном использовании веб-страниц, когда для них применяются сразу несколько стилей. Это и есть одно из ценных свойств CSS, которое называется каскад I ированием.
Использование комментариев
CSS-правила желательно прокомментировать, пусть даже не все или не основную их часть, а только главную группу инструкций. Это можно сделать двумя способами. Можно, например, разместить комментарии внутри нары следующих тегов:
./* Зто комментарий CSS */
Или же можно развернуть комментарий на несколько строк:
/*
Много строчный комментарий
При1 использовании1 многострочногоD комментарияЯ нужноJ иметь в1 виду, чтоDв1 них< нельзяЯ ыватьtоднострочные (или любые другие) комментарии. ЭтоVможетAпривести к непред-шред- сказуемым ошибкам.
Типы стилей
Существует несколько разных типов стилей, начиная с исходных стилей, установленных в вашем браузере (и любых пользовательских стилей, которые вы можете применить в своем браузере, чтобы переопределить исходные значения), продолжая вложенными или встроенными стилями и заканчивая внешними таблицами стилей. Для стилей, которые были определены, действует иерархическая последовательность выполнения, направленная снизу вверх.
Исходные стили
В веб-браузере применяется задание исходных стилей, имеющих самый низкий уровень приоритета. Этот набор стилей создается на тот случай, когда у веб-страницы нет определений каких-нибудь других стилей. Он предназначен для использования в качестве общего набора стилей, достаточно корректно отображаемого в большинстве случаев.
До создания CSS это были только стили, применяемые к документу, и только небольшая часть этих стилей могла быть изменена веб-страницей (например, внешний вид шрифта, его цвет и размер плюс несколько аргументов, относящихся к размеру элементов).
Пользовательские стили
Далее по уровню возрастания приоритета следуют стили, определенные пользователем. Они поддерживаются большинством современных браузеров, но в каждом из них реализованы по-своему. Если вы хотите узнать, как создавать собственные исходные настройки стилей для просмотра веб-страниц, воспользуйтесь какой-нибудь поисковой системой и введите в нее название вашего браузера и далее слова «пользовательские стили» (user styles) Например, «Firefox пользовательские стили» или «Internet Explorer пользовательские стили». На рис. 18.2 показано окно выбора таблицы пользовательских стилей Microsoft Internet Explorer.
.►паяй*- домашней страницы -
чтобы добавить дополнительные домашние страницы, съедите адок/;**дой и* ни> t отдельной строке
; Оформлен,w-; Оформлен,w-Стиль ьеб-с границыО Не учитывать цьет а. угаданные на вебстраница,CD Не учитывать шрифта, w азаиные на wn страница*□ не учитывать размеры шрифтоь, казанные на веб-страница-.Пользовательский стиль страницы[~У)ОФоРНЛЯТЬ. ИСПОЛЬЗУЯ ГТОПЬЗОБоТеЛЬОИп с runtСписок стилей:•■.'irMyjtyteiCivОбзор...Стиль ьеб-с границыО Не учитывать цьет а. угаданные на вебстраница,CD Не учитывать шрифта, w азаиные на wn страница*□ не учитывать размеры шрифтоь, казанные на веб-страница-.Пользовательский стиль страницы[~У)ОФоРНЛЯТЬ. ИСПОЛЬЗУЯ ГТОПЬЗОБоТеЛЬОИп с runtСписок стилей:•■.'irMyjtyteiCivОбзор...Но-. Трон» о ■■■н'-лчании ДЛЯ ПОИОаНо-. Трон» о ■■■н'-лчании ДЛЯ ПОИОаВкладкиВкладки[ Текущая | ( Исходная |[ Текущая | ( Исходная |Удаление временны» Фзйлс-е, истории просмотра.I -'гн-фамлое эаиомненмм' паролей и данные ns ьеб-Ф'Ч1 f ]Удалите *,-рньл сбо^рев-зтега при еы>одеУдаление временны» Фзйлс-е, истории просмотра.I -'гн-фамлое эаиомненмм' паролей и данные ns ьеб-Ф'Ч1 f ]Удалите *,-рньл сбо^рев-зтега при еы>оде[~ Удалить... | [ Параметры ][~ Удалить... | [ Параметры ][ Параметры ][ Параметры ]| ' ОК " ) [ Отмена ]| ' ОК " ) [ Отмена ]Настромгабклады дп* отображении веб-страницНастромгабклады дп* отображении веб-страниц[ Параметр"ы □[ Параметр"ы □Представление—- •| Цвета | [~ Языки [ Шрифты | [~Офорнпение ~|Представление—- •| Цвета | [~ Языки [ Шрифты | [~Офорнпение ~|
; Оформлен,w-
; Оформлен,w-
Стиль ьеб-с границы
О Не учитывать цьет а. угаданные на вебстраница,
CD Не учитывать шрифта, w азаиные на wn страница*
□ не учитывать размеры шрифтоь, казанные на веб-страница-.
Пользовательский стиль страницы
[~У)ОФоРНЛЯТЬ. ИСПОЛЬЗУЯ ГТОПЬЗОБоТеЛЬОИп с runt
Список стилей:
•■.'irMyjtyteiCivОбзор...
Стиль ьеб-с границы
О Не учитывать цьет а. угаданные на вебстраница,
CD Не учитывать шрифта, w азаиные на wn страница*
□ не учитывать размеры шрифтоь, казанные на веб-страница-.
Пользовательский стиль страницы
[~У)ОФоРНЛЯТЬ. ИСПОЛЬЗУЯ ГТОПЬЗОБоТеЛЬОИп с runt
Список стилей:
•■.'irMyjtyteiCivОбзор...
Но-. Трон» о ■■■н'-лчании ДЛЯ ПОИОа
Но-. Трон» о ■■■н'-лчании ДЛЯ ПОИОа
Вкладки
Вкладки
[ Текущая | ( Исходная |
[ Текущая | ( Исходная |
Удаление временны» Фзйлс-е, истории просмотра.
I -'гн-фамлое эаиомненмм' паролей и данные ns ьеб-Ф'Ч1 f ]Удалите *,-рньл сбо^рев-зтега при еы>оде
Удаление временны» Фзйлс-е, истории просмотра.
I -'гн-фамлое эаиомненмм' паролей и данные ns ьеб-Ф'Ч1 f ]Удалите *,-рньл сбо^рев-зтега при еы>оде
[~ Удалить... | [ Параметры ]
[~ Удалить... | [ Параметры ]
[ Параметры ]
[ Параметры ]
| ' ОК " ) [ Отмена ]
| ' ОК " ) [ Отмена ]
Настромгабклады дп* отображении веб-страниц
Настромгабклады дп* отображении веб-страниц
[ Параметр"ы □
[ Параметр"ы □
Представление—- •
| Цвета | [~ Языки [ Шрифты | [~Офорнпение ~|
Представление—- •
| Цвета | [~ Языки [ Шрифты | [~Офорнпение ~|
( ОК | | Отшхо1 [ Применить !
Рис. 18.2. Применение пользовательской таблицы стилей для Internet Explorer
Если задан пользовательский стиль, который уже был определен в качестве исходного стиля браузера, то пользовательский стиль заменит исходный стиль браузера. Любые стили, не определенные в пользовательской таблице стилей сохранят свои исходные значения, установленные в браузере.
Внешние таблицы стилей
Стили, заданные во внешней таблице стилей, заменят любые стили, заданные как пользователем, так и браузером. Внешние таблицы стилей являются рекомендуемым способом создания ваших стилей, поскольку вы можете создавать разные таблицы стилей для разных целей, например для общего использования в Интернете, для просмотра страниц на браузерах мобильных устройств с небольшими экранами, для получения распечатки и т. д. Нужно будет просто применить при создании вебстраницы один нужный набор стилей для каждого типа носителя информации.
Внутренние стили
Внутренние стили, создаваемые внутри тегов <stу lo> .</style>, имеют более высокий уровень приоритета над всеми предыдущими типами стилей и поэтому могут использоваться для замены стилевого оформления в любых внешних таблицах стилей, загруженных в то же самое время. Но с этого места начинается отделение стилевого оформления от содержимого.
Внедренные стили
И наконец, рассмотрим внедренные стили, представляющие1собой назначение свойства непосредственно элементу. Они также имеют наивысшин уровень приоритета над любым типом стилей, и их использование имеет следующий вид:
■ d href=”http://google.com” sty le="color:green: "^Посетите Google^'Ai -
В этом примере определяемая ссылка будет отображена зеленым цветом, независимо от любых исходных или других цветовых настроек, применяемых браузером или любой другой таблицей стилей либо непосредственно к этой ссылке, либо общим порядком ко всем ссылкам.
При использовании этого типа образования стилей вы нарушаете отделение разметки от содержимого, и поэтому применять подобные решения рекомендуется только в случае крайней необходимости.
Селекторы CSS
Средства доступа к одному или нескольким элементам страницы называются селекцией , и, как уже ранее показывалось, та часть правила CSS, которая этим занимается, известна как селектор. Как вы уже, наверное, догадались, существует множество разнообразных селекторов.
Селектор типа
Селектор типа определяет HTML-элемент, к которому применяется стиль, например <р> или <i>. Следующее правило, к примеру, обеспечивает полное выравнивание всего текста, находящегося между тегами <р>.. </р>:
р { text-align:justify; }
Селектор потомков
Селекторы потомков позволяют применять стили к элементам, содержащимся внутри других элементов. Например, следующее правило настраивает вывод всего текста внутри тегов <Ь>. . </Ь> красным цветом, но только если эти теги окажутся внутри тегов <р> . </р> (как в этом случае: <p><b>Hel lo</b> there«7p>):
Р h { со lor:red: }
Вложенность селекторов потомков может продолжаться до бесконечности, поэтому следующее правило является вполне приемлемым для того, чтобы полужирный текст внутри элемента маркированного списка выводился синим цветом:
м1 II b { пНопЫие. 1
В качестве практического примера представим себе, что нужно использовать другую систему нумерации, отличающуюся от исходной для пронумерованного списка вложенного в маркированный список. Этого можно достичь следующим способом, заменяющим исходную нумерацию (начинающуюся с 1) буквами в нижнем регистре (начинающимися с а):
<!DOCTYPE html>
<html>
<head>
<style>
ul ol { list-style-type:lower-alpha: }
</style>
</head>
<body>
<ol>
<И>0дин</И>
<Н>Два</1 i>
<11>Три</11>
</ о I>
<ul>
<ol>
<И>0дин</И>
<И>Дв-а</И>
<li>Tpn</li>
</ol>
</ul>
</body>
</html>
Загрузка этого кода HTML в веб-браузер даст следующий результат — как видите, <И>-элементы внутри второго <о1>-блока отображаются по-иному, нежели в первом пронумерованном списке:
Один
Два
Три
a. Один
b . Два
c. Три
Селектор дочерних элементов
Селектор дочерних элементов похож на селектор потомков, но он еще больше конкретизирует область применения стиля, выбирая только те элементы, которые являются непосредственными дочерними элементами другого элемента. Например, следующий код использует селектор потомков, который изменит цвет любого текста, выделенного полужирным шрифтом внутри абзаца на красный, даже если сам текст, выделенный полужирным шрифтом находится внутри выделения курсивом (подобно следующему коду: <р><i><Ь>Привет</Ь> всем</i ></р>):
р b { color:red; }
В данном случае слово Привет отображается красным цветом. Но когда требуется более конкретное поведение, чтобы сделать область применения селектора еще уже, может использоваться селектор дочерних элементов. Например, следующий селектор дочерних элементов установит красный цвет для текста, выделенного полужирным шрифтом только в том случае, если элемент будет непосредственным дочерним элементом абзаца и внутри не содержится другого элемента:
р > b { col or:red: }
Теперь слово Привет в предыдущем примере HTML-кода не изменит свой цвет, потому что элемент <Ь> не является непосредственным дочерним элементом того элемента, который задается тегом <р>.
В качестве практического примера представим себе, что нужно применить стиль только к тем <1 ^-элементам, которые являются непосредственными дочерними элементами <о!>-элементов. Добиться этого можно с помощью следующего кода, где на <11>-элементы, являющиеся непосредственными дочерними элементами <и1>-элементов, стиль применяться не будет:
<!DOCTYPE html>
<html>
<head>
<style>
ol > li { font-weight:bold: }
</style>
</head>
<body>
<ol>
<1i >0дин</1i>
<И>два</И>
<1i >Три</1i >
<ul>
<1i >0дин</1i>
<И>два</И>
<li>Tpn</li>
</ul>
</ol>
</body>
</html>
Результат загрузки этого HTML-кода в браузер будет иметь следующий вид:
Один
два
Три
о Один
о два
о Три
Селектор смежных элементов
Селектор смежных элементов похож на селектор дочерних элементов, за исключением того, что вместо применения к родительским и дочерним элементам он применя-
ется к элементам, находящимся на одном и том же уровне и следующим непосредственно друг за другом без каких-либо других элементов, которые располагаются между ними (хотя текст между ними допускается).
Селектор смежных элементов состоит из двух и более селекторов со знаком «плюс* ( + ) между каждым из них:
1 * : - color: red: }
Этот селектор сделает красным любой текст, выделенный полужирным шрифтом. но только там, где он следует сразу же за элементом, выделенным курсивом. Например, текст между тегами <Ь> и </Ь> в следующем фрагменте кода будет выведен красным цветом:
<!DCCTYPE html >
<htnr.>
<head>
<style>
i + b (color: red: }
</style>
</head>
<body>
<div>3T0 текст в div-контейнере.
<i>3T0 текст, выделенный курсивом.</i>
А это возвращение к основному тексту.
<Ь>А теперь это текст, выделенный полужирным шрифтом, который будет отображен красным цветом.</Ь>
</div>
</body>
</html>
Селектор элементов, имеющих идентификатор
Если у элемента есть идентификатор (наподобие следующего: <div id='mydiv’>), к нему можно обратиться из CSS напрямую следующим способом, выделяющим весь текст в названном элементе курсивом:
#mydiv { font - sty!е :i ta li с : }
Повторное использование идентификаторов
Идентификаторы могут использоваться в документе только один раз, поэтому только первое найденное появление идентификатора приведет к применению нового значения того или иного свойства, заданного правилом CSS. Но в CSS можно непосредственно ссылаться на любые идентификаторы, имеющие одинаковые имена, если они появляются в элементах разного типа:
<div id='myid'>Привет</сИv> <span id=,myid'>npnBeT</span>
Поскольку идентификаторы обычно применяются только к уникальным элементам, следующее правило будет применять подчеркивание только к первому появлению myid:
#myid { text-decoration underline: }
Но можно добиться того, чтобы правило в CSS применялось к обоим появлениям данного идентификатора:
span#myid { text-decoration:underline: } div#myid { text-decoration:underline: }
Или в сокращенной записи (см. далее раздел, посвященный группировке):
span#myid.#myid { text-decoration:underline: }
Я не рекомендую использовать такую форму селекции, поскольку любой код JavaScript, который также должен обращаться к этим элементам, не сможет с этим справиться, потому что широко применяемая функция getElementByld вернет только первое появление элемента с таким идентификатором. Для ссылки на любые другие экземпляры программе придется перебрать весь список элементов в документе, что является куда более сложной задачей. Лучше всегда выбирать для идентификаторов только уникальные имена.
Селектор класса
Когда на странице имеются элементы, для которых нужно применить один и тот же стиль, всем этим элементам можно задать одно и то же имя класса (например: <span class='myc 1 ass' >), а затем создать единое правило для одновременного изменения всех этих элементов, как в следующем правиле, создающем смещение левого края на 10 пикселов для всех элементов, которые используют данный класс:
.myclass { margin-1 e ft:Юрх: }
В современных браузерах HTML-элементы могут использовать более одного класса, если имена классов разделить пробелами, например: <span cl ass=' thi scl ass thatcl ass othercl ass' >. Но следует запомнить, что некоторые очень старые браузеры допускают применение в аргументе cl ass только одного имени.
Ограничение области действия класса
Вы можете ограничить область действия класса, указав тип элементов, к которым должно применяться правило. Например, следующее правило применяет настройки только к абзацам, использующим класс main:
p.main { text-indent:30рх: }
В данном примере только те абзацы, которые используют класс main (как этот: <р cl ass="mai п ">), получат новое значение свойства. На любые другие типы элементов, которые могут применять этот класс (такие как <di v cl ass="mai n ">), это правило распространяться не будет.
Селектор атрибутов
Многие HTML-теги поддерживают атрибуты, и использование селектора данного типа может избавить вас от применения идентификаторов и классов для ссылок на элементы, задаваемые этими тегами. Например, можно непосредственно со-
слаться на атрибуты следующим образом, установив для всех элементов, задействующих атрибут type="submit", ширину, равную 100 пикселам:
'type=" submit"'] { width:lOOpx: }
Нели нужно ограничить область действия селектора до, к примеру, элементов ввода, принадлежащих форме и имеющих это значение атрибута типа, можно вместо предыдущего воспользоваться следующим правилом:
*orm iriput[type="submit" ] { width:ЮОрх: }
Селекторы атрибутов также работают применительно к идентификаторам и классам, на* пример, селектор [class="classname"] работает точно так же, как и селектор .classname (за *• исключением того, что у последнего из них более высокий уровень приоритета). Точно таким же образом селектор [id="idname"] может использоваться вместо селектора идентификатора #idname. Селекторы классов и идентификаторов, предваряемые символами решетки (#) и точки (.) могут рассматриваться в качестве краткой формы селекторов атрибутов, имеющей при этом более высокий уровень приоритета.
Универсальный селектор
Групповой символ *, или универсальный селектор соответствует любому элементу, поэтому следующее правило приведет к полному беспорядку в документе, установив зеленое обрамление для всех его элементов:
* { border:lpx solid green: }
Скорее всего, универсальный-селектор будет использоваться не сам но себе, а как часть какого-нибудь составного правила, где он будет весьма эффективен. Например, следующее правило будет применять тот же самый стиль, что предыдущее, но только ко всем абзацам, которые являются подчиненными элементами того элемента, у которого имеется идентификатор со значением boxout, и только в том случае, если они не являются непосредственными дочерними элементами:
#boxout * р {border:lpx solid green; }
Разберемся в том, что здесь происходит. Первым селектором, следующим 3a#boxout является символ звездочки (*), стало быть, он ссылается на любой элемент1внутри объекта boxout. Затем следующий селектор р ограничивает фокус селекци и, направляя его только на абзацы (что и определяется символом р), являющиеся подчиненными элементами, возвращаемыми селектором * Поэтому данное CSS-правило приводит к выполнению следующих действий (в которых для ссылки на одни и те же вещи я использую взаимозаменяемые понятия «объект» и «элемент»).
Поиск объекта с идентификатором, имеющим значение boxout.
Поиск всех подчиненных элементов объекта, возвращенного при выполнении действия 1.
.3. Поиск всех подчиненных p-элементов тех объектов, которые были возвращены при выполнении действия 2, и, поскольку это последний селектор в группе, поиск также всех подчиненных p-элементов, подчиняющихся этим подчиненным
элементам (и т. д.) того объекта, который был возвращен нрн выполнении действия 2.
\. Применение стилей, заданных внутри символов { и } к объектам, возвращенным при выполнении действия 3.
В результате зеленое обрамление применяется только к абзацам, являющимся внучатыми (или правнучатыми и т. д.) элементами основного элемента.
Групповая селекция
При использовании CSS имеется возможчюсть одновременного применения правила более чем к одному элементу, классу или любому другому типу селектора путем разделения селекторов запятыми. Например, следующее правило поместит пунктирную оранжевую линию под всеми абзацами, элементом с идентификатором idname и всеми элементами, использующими класс со значением ' lassname:
[). # i driame. .с lassname { border- boll от: Ipx dotted orange1. }
На рис. 18.3 показан результат применения разных селекторов, а рядом показаны применяемые к ним правила.
/ СSS Selectors• \С О selectors.htm<p>TTris is a paragraph.</p><p>Here is a paragraph with some<t»bold text</b> in it.</p><ul><ii>Thisis aHst element.</ii><li>Tbis is a Kst dement with <b>bo!d text</b> in it.</ii></ul><div id='mydiv’>This is a div with the Ю of 'mx'drv'./ СSS Selectors• \С О selectors.htm<p>TTris is a paragraph.</p><p>Here is a paragraph with some<t»bold text</b> in it.</p><ul><ii>Thisis aHst element.</ii><li>Tbis is a Kst dement with <b>bo!d text</b> in it.</ii></ul><div id='mydiv’>This is a div with the Ю of 'mx'drv'.ф ^ф ^P Itext- align: justify; }P b {color:red; jP Itext- align: justify; }P b {color:red; jul Li b { color:blue: }ul Li b { color:blue: }#mydiv {font-style:italic; )#mydiv {font-style:italic; )
/ СSS Selectors• \
С О selectors.htm
<p>TTris is a paragraph.</p>
<p>Here is a paragraph with some
<t»bold text</b> in it.</p>
<ul>
<ii>Thisis aHst element.</ii>
<li>Tbis is a Kst dement with <b>bo!d text</b> in it.</ii>
</ul>
<div id='mydiv’>
This is a div with the Ю of 'mx'drv'.
/ СSS Selectors• \
С О selectors.htm
<p>TTris is a paragraph.</p>
<p>Here is a paragraph with some
<t»bold text</b> in it.</p>
<ul>
<ii>Thisis aHst element.</ii>
<li>Tbis is a Kst dement with <b>bo!d text</b> in it.</ii>
</ul>
<div id='mydiv’>
This is a div with the Ю of 'mx'drv'.
ф ^
ф ^
P I
text- align: justify; }
P b {
color:red; j
P I
text- align: justify; }
P b {
color:red; j
ul Li b { color:blue: }
ul Li b { color:blue: }
#mydiv {
font-style:italic; )
#mydiv {
font-style:italic; )
Рис. 18.3. Фрагменты кода HTML и применяемые в отношении этих фрагментов правила CSS
Каскадность CSS
Одной из основополагающих особенностей свойств CSS является их каскад- ность, благодаря которой технология и называется каскадными таблицами стилей (Cascading Style Sheets). По что это означает?
Каскадирование является методом, используемым для решения потенциальных конфликтов между различными типами стилей, поддерживаемых браузером, и применения их в порядке приоритетности в зависимости от создателя стилей, от метода, который использован для создания стилей и от типов выбранных свойств.
Создатель таблиц стилей
Все современные браузеры поддерживают три основных тииатаблицстилсй. 13 порядке приоритетности сверху вниз они располагаются следующим образом.
Созданные автором документа.
Созданные пользователем.
Созданные браузером.
Эти три набора таблиц стилей обрабатываются в обратном порядке. Сначала к документу применяются исходные настройки веб-браузера. Без них веб-станицы, не использующие таблицы стилей, выглядели бы ужасно. Он I и включают внешний вид, размер и цвет шрифта, интервалы между элементами, обрамления и отступы в таблицах и все остальные разумные стандарты, ожидаемые пользователем.
Затем, если пользователь создал какие-нибудь стили, которые предпочитает использовать в качестве стандартных, эти стили заменяют исходные стили браузера, с которыми они могут конфликтовать.
И наконец, применяются любые стили, созданные автором текущего документа, заменяя любые стили, либо созданные в качестве исходных стилей браузера, либо созданные пользователем.
Методы создания таблиц стилей
Таблицы стилей могут создаваться с помощью трех различных методов. Если расположить их в порядке приоритетности сверху вниз, получится следующий список.
Внедренные стили.
Встроенная таблица стилей.
Внешняя таблица стилей.
Эти методы создания таблиц стилей также применяются в порядке, обратном порядку их приоритетности. Поэтому сначала обрабатываются все внешние таблицы стилей, и к документу применяются их стили.
Затем обрабатываются любые встроенные стили (которые находятся внутри тегов <style>. . </style>). Все, что конфликтуете внешними правилами, получает приоритет и заменяет эти правила.
И наконец, наивысший приоритет получают любые стили, применяемые непосредственно к элементу в качестве внедренного стиля (такие как <di v styl е=”..."*/
di v>), которые заменяют все предыдущие заданные свойства.
Селекторы таблиц стилей
Существует три разных способа выбора стилизуемых элементов. В порядке убывания приоритетности их список имеет следующий вид.
Обращение по индивидуальному идентификатору или селектор атрибутов.
Обращение в группах по классу.
Обращение по тегам элементов.
Селекторы обрабатываются согласно количеству и типам элементов, подпадающих под правило, что несколько отличается от предыдущих двух правил разрешения конфликтов. Причина состоит в том, что правила не должны сразу применяться только к одному типу селектора и могут иметь отношение к разным селекторам.
Таким образом, метод, необходимый для определения уровня приоритета правил, может содержать любую комбинацию селекторов. Это делается с помощью вычисления специфики каждого правила путем выстраивания их в порядке убывания обл аст и де йс гв и я.
Вычисление специфики
Специфика правила вычисляется путем создания трехкомпонентных чисел на основе типов селекторов в показанном выше пронумерованном списке. Эти составные числа сначале выглядят как [0.0.0]. При обработке правила каждый селектор, который ссылается на идентификатор, увеличивает первое число на единицу, и составное число приобретает вид [1.0.0].
Посмотрим на следующее правило. У него имеется три ID-ссылки (#headiriq, #main и #menu), поэтому составное число приобретает вид 13.0 .01:
#heading. ftnain. #menu.
. text.. .quote. . boxout. . news. . comments . p. blockquote {
font-farmI I у: ’ 1 lines New RomaIn': font-s i/e ■14pt. }
.Затем во вторую часть составного числа помещается количество селекторов, ссылающихся на класс. В этом примере таких селекторов пять text, .quote, boxout, .news и .comments, поэтому число приобретает вид | З.Ь.О].
И наконец, вычисляется количество селекторов, ссылающихся па теги элементов, и результат помещается в последнюю часть составного числа. В нашем примере таких селекторов два (р и blockquote), поэтому составное число приобретает следующий окончательный вид: [3.5.2], чего вполне достаточно для сравнения специфики этого правила с другими спецификами.
Когда в составном числе набирается девять или меньше селекторов каждого типа, его можно преобразовать непосредственно в десятичное число, в нашем случае это 352. Правила с меньшим числом, чем это, будут иметь меньший приоритет, а правила с более высоким числом будут иметь больший приоритет. Когда у двух правил будет одно и то же значение, выигрывает последнее из при менявшихся.
~'й-гИт1|ч ,Рг. t~'й-гИт1|ч ,Рг. t429
~'й-гИт1|ч ,Рг. t
~'й-гИт1|ч ,Рг. t
«апкэование другой системы счисления
;>у числе набирается больше девяти типов селекторов, нужно перехо-
« ■' •.'таршую систему счисления. Например, составное число [11.7.19] не
- *:* <6разованию в десятичное простым объединением трех частей. Вместо м .ffio преобразовать в число с более высоким основанием системы счисления, _• и " «нованием 20 (или выше, если будет больше 19 селекторов любого типа).
=-- - лужно умножить все три части и сложить результаты, как показано --.:- i' самого правого числа и гс самого правого числа и переходя влево:
I' Ч 19 = 38С380
>2: Ч / = 2800
-::^2o ч 111 = £88000
I-1: : " /--ОМ виде = с91180
1■. г. жноIIиспользоватьпболее(высокоеноснование,кзаменитеазначения 20 сл
~ —-:.г-:;1ями применяемого основания. Затем, когда все составные числа набора ~ ::рошли1 преобразование из этого основания в десятичное, будет просто - г^.ють специфику, а стало быть, и уровень приоритета каждого.
: ’ . частью, процессор CSS делает все это за вас, но понимание принципов данной
:юможет правильно создавать правилаiи разбиратьсяав тех уровнях< прио- ■~ - Ti. которые у них будут.
^ Если все предыдущие вычисления кажутся слишком сложными, вам будет приятно узнать, 4. -то6в большинстве случаев||вы)можете обойтись простым)практическим правилом: чем мен
— ^ Але изменяемых элементов и чем конкретнее они указаны, тем выше будет приоритет, присваиваемый правилу.
2фм правила бывают равнее других
- :za два или более правила задания стилей имеют абсолютно одинаковый уровень т дштета, то по умолчанию будет применяться последнее обработанное правило.
зы можете придать правилу более высокий уровень приоритета по сравнению ■ругими равными ему правилами, используя объявление ! important:
: :7or:#ff0000 !important: }
При этом все предыдущие равные настройки заменяются (даже те, в которых .'пользуется объявление !important), и любые равные правила, обрабатываемые ~ :зже, игнорируются. Например, второе из двух следующих правил обычно имело гь£ приоритет, но из-за применения объявления ! important в ранее заданном правиле второе правило игнорируется:
:col or:#ff0000 !important: }
:color:#ffff00 }
Пользовательские таблицы стилей могут создаваться для определения исходных стилей браузера, и в них может применяться объявление !important. В этом случае пользовательская настройка стиля будет иметь преимущество над аналогичными свойствами, указанными на текущей веб-странице. Но на очень старых браузерах, использующих CSS1, эта особенность не поддерживается.
Разница между <div> и <span>
Оба элемента <di v> и <span> относятся к контейнерам, но некоторые качества v них отличаются. По умолчанию <di v>-:uicmciit имеет бесконечную ширину (как минимум до края браузерного экрана), которую можно увидеть, если применить к контейнеру единичное обрамление:
<div style="border:lpx soli i d green;" 'Мривсг /div-
А <$рап>-элемент не шире того текста, который в нем содержится. Поэтому следующий код HTML создаст обрамление только вокруг слова Привет, и оно не будет расширяться до правого края браузерного экрана:
<span style="border:lpx solid green:">HpnBeT</span>
Кроме того, <span>-:-uieM0HT сопровождает текст или другие объекты и при их переносе на следующие строки, и поэтому может иметь довольно сложное обрамление. Например, в примере 18.2, код CSS использован для создания желтого фона для всех <di у>-элементов, для создания голубого фона для всех <5рап>-элементов и для добавления обрамления и к тем и к другим, перед тем как создать несколько примеров <span>- и <сИу>-6локов.
Пример 18.2. Пример контейнеров <div> и <span>
<!DOCTYPE html>
<html>
<head>
<title>npnMep div и span</title>
<style>
div. span { border:lpx solid black: }
div { background-color:yellow: } span { background-color:cyan: } </style>
</head>
<body>
<div>3T0T текст находится внутри тега div</div>
А этот нет. <div>A этот снова внутри тега div.</div><br />
<span> Этот текст находится внутри тега span.</span>
А этот нет. <span> А этот снова внутри тега span.</span><br /><Ьг />
<div>3to более объемный текст в теге div. который переносится на следующую строку браузера </div><br />
<span> Это более объемный текст в теге span, который переносится на следующую строку браузера </span>
</body>
</html>
Как выполнение кода этого примера выглядит в окне браузера, показано на рис. 18.4. Хотя в печатной версии книги все изображается в серых тонах, на рисунке четко показано, как <di у>-элементы расширяются до правого края браузерного
экрана, и заставляют следующее за ними содержимое появляться с начата первой доступной позиции ниже себя.
Рис. 18.4. Разнообразные элементы, имеющие разную ширину
На рисунке также отображено, как ведут себя <$рап>-элементы, занимая только то пространство, которое требуется для размещения их содержимого, не заставляя при этом последующее содержимое страницы появляться под ними.
Например, в двух самых нижних примерах, показанных на рисунке, можно увидеть, что когда <di у>-элементы при достижении края экрана переносятся на новую строку, они сохраняют прямоугольную форму, в то время как <$рап>-эле- ченты просто следуют за потоком содержащегося в них текста (или другого содержимого).
Поскольку <div>-Tern могут создавать только прямоугольные контейнеры, они лучше под- *У 4 « ходят для содержания таких объектов, как изображения, блоки, цитаты и т. д., а контейнеры, создаваемые <зрап>-тегами, больше подходят для содержания текста или других атрибутов, размещаемых один за другим на одной линии, и которые должны располагаться слева направо (или в некоторых языках справа налево).
Измерения
CSS поддерживает впечатляющий диапазон различных единиц измерения, позволяя очень точно выкраивать веб-страницы для конкретных значений или относительных размеров. Я обычно пользуюсь следующими единицами измерений (и считаю, что вам они также будут наиболее полезными) — это пикселы, пункты, эмы и проценты. Рассмотрим подробнее эти, а также другие единицы измерения.
Э Пиксел (pixel) — размер пиксела варьируется в соответствии с размерами и глубиной пиксела на пользовательском мониторе. Один пиксел равен ширине и высоте
отдельной точки на экране, поэтому данную единицу измерения .лучше всего использовать для мониторов. Например:
.classname { margin:5px: }
О П г ункт (point) —равен по размеру 1 /72 дюйма. Эта единица измерения пришла из полиграфии и лучше всего подходит для этой среды, но также широко используется и для мониторов. Например:
.classname { font-size:14pt: }
О Дюйм (inch) —равен 72 пунктам, и он также относится к типу единиц измерения, наиболее приспособленных для организации вывода на печать. Например:
classname { width:3in: }
О Санти м етр (centimeter) —являются еще одной единицей измерения, которая наиболее пригодна для организации вывода на печать. Один сантиметр немного превышает по размеру 28 пунктов. Например:
.classname { height:2cm; }
О МиIллиметр (millimeter) — это 1/10 сантиметра (или почти 3 пункта). Миллиметры являются еще одной единицей измерения, наиболее подходящей для организации вывода на печать. Например:
.classname { font-size:5mm: }
О Пика (pica) — является еще одной типографской единицей измерения, равной 12 пунктам. Например:
.classname { font - si ze:Ipc: }
О Эм (em) —равен текущему размеру шрифта (ширине латинской буквы т). Является одной из наиболее полезных единиц измерения для CSS, поскольку используется для описания относительных размеров. Например:
.classname { font-size:2em: }
О Экс (ех) — также относится к текущему размеру шрифта. Он равен высоте буквы х нижнего регистра. Это менее популярная единица измерения, которая наиболее часто используется в качестве хорошего приблизительного значения, помогающего установить ширину прямоугольного блока, который будет содержать некий текст. Например:
.classname { width:20ex: }
О П роцент (percent) — эта единица сродни эму (em), поскольку это относительная, а не абсолютная единица измерения. Когда используется шрифт, как в случае с lem, 100 % равны текущему размеру шрифта. Когда эта единица не относится к шрифту, она относится к размеру того контейнера, к которому применяется данное свойство. Например:
.classname { height:120^: }
На рис. 18.5 каждый из этих типов измерений показа по очереди применительно к отображаемому тексту почти одинаковых размеров.
; — v.~ ЩИ
У © Measurements*...
С О measurements.htm☆! л
font-size:37px;
font-size:28pt;
font-size:0.39in;
font-size:lcm;
font-size:10mm;
font-size:2.37pc;
font-size:2.37em;
font-size:5.28ex;
font-size:237%;
Рис. 18.5. Различные измерения, приводящие примерно к одинаковому результату
Шрифты и оформление
С помощью CSS можно настроить четыре основных свойства шрифта: семейство — family, стиль —style, размер — size и насыщенность — weight. Пользуясь этими свойствами, можно точно настроить способ отображения текста на ваших веб-страницах и (или) вывода его на печать и т. д.
font-family
Это свойство назначает используемый шрифт. Оно также поддерживает перечисление множества шрифтов в порядке предпочтения слева направо, чтобы стилевое оформление при отсутствии у пользователя установленного предпочитаемого шрифта постепенно снижалось в сторону менее предпочитаемых шрифтов. Например, для установки шрифта по умолчанию для абзацев можно воспользоваться следующим CSS-правилом:
р {. font-family:Verdana. Arial. Helvetica, sans-serif: }
Если название шрифта состоит из двух и более слов, его нужно заключить в кавычки:
р { font-family:"Times New Roman". Georgia, serif: }
Для использования на веб-страницах больше всего подходит такое семейство шрифтов, как ■. Ari* I, Arial, Helvetica, Times New3 Roman, Times,i Courier Newuи Courier,□поскольку всеLэти tшр доступны практически на всех веб-браузерах и операционных системах. Шрифты Verdana, Georgia, Comic Sans MS, Trebuchet MS, Arial Black и Impact можно смело применять на Mac и PC, но они могут быть не установлены в других операционных системах, таких как Linux. Другими распространенными, но менее надежными шрифтами являются-Palatino, Garamond, Bookman и Avant Garde. Если используется один из'менее надежных шрифтов, нужно убедиться что в ваших настройках CSS предложены один или несколько менее предпочтительных шрифтов, чтобы веб-страницы в отсутствие на браузерах предпочитаемых вами шрифтов шли в их использовании по нисходящей.
На рис. 18.6 показано применение этих двух наборов CSS-правил.
A © Font Family ♦• v C iQfontfamily.htm☆ I
font-family:Verdana, Arial, Helvetica, sans-serif;
font-family:'Times New Roman', Georgia, serif;
Рис. 18.6. Выбор семейства шрифтов
font-style
С помощью этого свойства можно выбрать вывод шрифта в обычном —normal, курсивном —italic или наклонном — oblique виде (последний похож на курсив и обычно используется для гарнитуры sans-serif). Следующие правила создают три класса (normal, italic и oblique), которые могут применяться к элементам для создания соответствующих эффектов:
.normal { font-style:normal: }
.italic { font- style:italic: }
.oblique { font-style:oblique: }
font-size
В предыдущем разделе, касающемся единиц измерения, было рассмотрено множество способов изменения размера шрифта, но все они сводятся к двум основным типам:
фиксированным и относительным. Фиксированная настройка похожа на следующее правило, которым для абзацев устанавливается размер шрифта, равный 14 пунктам:
Р { font, size: 14pt: }
В качестве альтернативы можно предпочесть работу с текущим размером шрифта но умолчанию, используя его для стилевого решения таких разновидностей текста, как заголовки. В следующих правилах определены относительные размеры некоторых заголовков, где тег <h4> начинает с прибавки в 20 % к размеру по умолчанию, и каждое возрастание размера задается больше предыдущего на 40 %:
hi { font-size :240%: }
h2 { font-size:200%: } h3 { font-size :160%: } h4 { font-size : 120%: }
На рис. 18.7 показана подборка размеров шрифтов в действии.
Рис. 18.7. Настройка четырех размеров заголовков и размер абзаца, используемый по умолчанию
font-weight
Используя это свойство, можно задать насыщенность или степень жирности шрифта.
Оно поддерживает несколько значений, но в основном востребованы normal и bold: .bold { font-weight:bold: }
Управление стилями текста
Независимо от используемого шрифта в способ вывода текста можно внести дополнительные изменения, меняя его оформление — decoration, разрядку — spacing и выравнивание — al ignment. Но свойства текста и шрифта перекликаются, в том
смысле что курсивный и полужирный текст можно получить, используя свойства font-style и font-weight, в то время как другой гскст, например подчеркнутый, требует использования свойства text-decoration.
Оформление
Используя свойство text-decoration, можно применить к тексту такие эффекты, как подчеркивание — underl ine, перечеркивание — 1i nc - through, верхнее подчеркивание - overline и мигание — blink. Следующее правило создает новый класс по имени over, который применяет верхнее подчеркивание к тексту (насыщенность линий, используемых для подчеркивания снизу и (‘верху и для перечеркивания, будет соответствовать насыщенности шрифта):
.over { text-decoration:overline: }
На рис. 18.8 можно увидеть подборку стилей, насыщенностей и оформлений шрифтов.
С Й А С< fontstyIe.htm
Обычный (Normal)
Курсивный (Italic)
Наклонный (Oblique)
Полужирный (Bold)
Подчеркнутый сверху (Overline.)
Подчеркнутый сн и зу (Underline)
Перечеркнутый (Strikeout)
Рис. 18.8. Примеры доступных правил стилевых решений и оформления
Разрядка
Существуют свойства, позволяющие изменить разрядку строк, слов и букв. Например, следующие правила настраивают разрядку строк для абзацев путем изменения свойства 1i ne-height, делая его на 25 % больше, устанавливают свойство word-spacing равным 30 пикселам и разрядку букв 3 пиксела:
Р { line-height :125%: word-spacing :ЗОрх: letter-spacing:3px; }
Выравнивание
В CSS доступны четыре типа выравнивания текста: полевому краю left, но правому краю — right, но центру — center и по ширине содержимого justi fy. В следующем правиле текст абзаца настроен на полное выравнивание по ширине:
j { text-align:justify: }
Преобразование
Для преобразования текста доступны четыре свойства: отсутствие преобразования попе, преобразование первых букв слов в заглавные capita 1ize, преобразование всех букв в заглавные uppercase и преобразование всех букв в строчные lowercase. Следующее правило создает класс по имени upper, гарантирующий при его применении вывод всего текста в верхнем регистре:
upper { text transform-.uppercase: }
Отступы
С помощью свойства text i rident можно создать отступ первой строки блока текста па указанную величину Следующее правило создает отступ первой строки каждого абзаца на 20 пикселов, по могут быть применены и другие единицы измерений или процентное увеличение:
р ( text indent:20рх; }
Па рис. 18.9 к блоку текста было применено следующее правило:
[j {I ine height : 150%:
word-spacing : Юрх: letter- spacing:lpx:}
Justify ^ text alignjjustif.y: }
uppercase { text transform:uppercase: ( . indent ^ text indent ;20px:}
,~~v„„,
j © F'ont Spacing, Alignment & v'
; <-CO fontspacing.htm& \
TO BE, OR NOT TO BE, that is the question: Whether 'tis Nobler in the mind to suffer The Slings and Arrowsof outrageous Fortune, Or to take Arms against a Sea of troubles, And by opposing end them
Рис. 18.9. Примененные правила отступа, преобразования в верхний регистр и разрядки
Цвета CSS
Цвета могут применяться к первому плану, а также к фону текста и объектов, путем использования свойства цвета —color и фонового цвета —background color (или путем предоставления единственного аргумента свойству фона —back ground). Указанный цвет может быть одним из именованных цветов (например, red или blue), цветом, составленным из трех шестнадцатеричных чисел RGB (например, #ff0000 или #0000ff), или цветом, составленным с использованием CSS-функции rgb.
Названия стандартных 16 цветов, определенных организацией по стандартам W3C (http://www.w3.org), следующие: аквамарин —aqua, черный - black, синий blue, яркий пурпурно-красный, или фуксия, - fuchsia, серый gray, зеленый green, яркий светло-зеленый — lime, красно-коричневый — maroon, темно-синий navy, оливковый —ol i ve, фиолетовый —purpl е, красный red, серебристый —si 1 ver, зеленовато-голубой teal, белый —white и желтый yel low. Следующее правило использует одно из этих названий для установки фонового цвета для объекта с II), имеющим значение object:
#object { background-col or:si 1ver: }
В показанном ниже правиле фоновый цвет текста во всех <di у>-элементах установлен желтым (поскольку на компьютерном дисплее шестнадцатеричные уровни ff красного, плюс ff зеленого, плюс 00 синего составляют желтый цвет):
div { color:#ffffOO: }
Или, если не хочется работать с шестнадцатеричными числами, можно указать свои три цветовые составляющие с помощью функции rgb, как в следующем правиле, которое изменяет фоновый цвет текущего документа на аквамарин:
body { background-color:rgb(0. 255. 255): }
Если вы не хотите работать в диапазоне 256 уровней для каждого основного цвета, можете вместо них в функции rgb использовать процентный показатель со значениями в диапазоне от самого низкого (0) количества основного цвета до самого высокого (100), например: rgb(58%, 95%, 74%). Можно также для более тонкого управления цветом применять числа с плавающей точкой, например: rgb(23.4%, 67.6%, 15.5%).
Сокращенные цветовые строки
Есть также короткая форма строки шестнадцатеричных чисел, в которой для каждого цвета используется только первое число из каждой двухбайтовой пары. Например, вместо назначения цвета #fe4692 можно применять #f49, где опущены вторые шестнадцатеричные цифры из каждой пары, что равно цветовому значению #ff4499.
Получается почти такой же цвет. Это можно применить там, где точный цвет не нужен. Разница между строками из шести и из трех цифр в том, что первые поддерживают шестнадцать миллионов различных цветов, а вторые всего четыре тысячи.
Гам, где используется такой цвет, как #883366, полным эквивалентом ему будет *836 (поскольку повторяющиеся цифры подразумеваются в сокращенной версии), и для создания одного и того же цвета можно применять любую строку
Градиенты
Вместо использования сплошного цветового фона можно применить градиент, который будет автоматически переходить от выбранного исходного цвета до выбранного конечного цвета. Градиент лучше использовать в связке с простым цветовым правилом, чтобы браузеры, не поддерживающие градиенты, отображали как минимум сплошной цвет.
В примере 18.3 задействуется правило отображения оранжевого градиента (или просто обычного оранжевого цвета на браузерах, не поддерживающих градиенты), как показано в средней части рис. 18.10.
Пример 18.3. Создание линейного градиента
- !DOCTYPE html > - html
<head ■’
Ц.1е>Создание ничейною i pa диен га< / 1 i 11
<style> orangegrad { background:orange: background:linear-gradient(top. #fbO. #f50): background:-moz-1inear-gradient(top. #fb0. #f50): background:-webkit-1inear-gradient(top. #fb0. #f50): background:-o-linear-gradient(top. #fb0. #f50): background:-ms-linear-gradient(top. #fb0. #f50): }
^ / st.y le>
' /head>
■body>
<div class='orcingegrad'>4epHbim гене, , <br Л- на оранжевом *Ьг />линейном rpaflneHTe</div> /bod.y>
/htm1>
Как показано в предыдущем примере, многие CSS-правила требуют префиксы, предназначен* ные для того или иного браузера, например -moz-, -webkit-, -о- и -ms- (соответственно дл браузеров на основе движка Mozilla, таких как Firefox; для браузеров на основе движка WebKit, таких как Apple Safari, Google Chrome, и браузеров iOS Android, а также для браузеров Opera и Microsoft). Основные CSS-правила и атрибуты, а также указания на то, где требуются версии, подстроенные под тот или иной браузер, перечислены на веб-сайте http://caniuse.com.
Для создания градиента нужно выбрать, где он будет начинаться, вверху (top), внизу (bottom), слева (left) справа (right) или по центру (center), или в любых составных местах, например в левом верхнем углу (top left) или от центра вправо (center right), затем ввести нужные начальный и конечный цвета и применить правило либо линейного (1 inear- gradient), либо радиального (radial - gradient) градиента, обеспечив правила для всех браузеров, па которые вы нацелились.
Рис. 18.10. Сплошной цвет фона, а также линейный и радиальный градиенты
Вы также можете' использовать не только начальный и конечный цвета, а предоставлять между ними в качестве дополнительных аргументов составляющие конечные цвета. Например, если предоставлены пять аргументов, каждый из них будет управлять изменением цвета одной пятой области (в соответствии с его местом в списке аргументов).
Позиционирование элементов
Элементы попадают на веб-страницу гуда, где они находятся в документе, но могут переметаться путем изменения свойства позиции элемента от исходной статической до абсолютной, относительной или фиксированной.
Абсолютное позиционирование
Элементе абсолютным позиционированием удаляется из документа, и любые другие элементы, которые в состоянии это сделать, займут освободившееся пространство. Затем вы можете позиционировать объект в любое нужное место в документе, используя свойства верх top, право right, низ bottom и лево loft. Он останется над (или иод) другими элементами.
Например, для перемещения объекта с II), имеющим значение object., в абсолютное место, находящееся на 100 пикселов ниже начала документа и па 200 пикселов от левого края, к нему нужно применить следующие правила (вы также можете использовать любые другие единицы измерения, поддерживаемые4 CSS):
#object {
posit Ion:abso 1 lute:
toplOOpx.
If'It Д)0рх. [ 1
Относительное позиционирование
Подобным образом можно переместить объект относительно того места, которое он бы занимал при обычном ходе формирования документа. Так, нанример, для перемещения объекта па 10 пикселов вниз и 10 пикселов вправо от его обычного положения нужно воспользоваться следующими правилами:
^object {
positiomrelative:
top : Юрх;
left :Юрх; }
Фиксированное позиционирование
Заключительные настройки свойства позиционирования позволяют переместить объект в абсолютное положение, но только внутри окна просмотра текущего браузера. Затем при прокрутке документа объект остается именно там, куда он был помещен, а основной документ будет прокручиваться под ним — это неплохой способ создания док-панелей и других подобных устройств. Для фиксирования объекта в левом верхнем углу браузера нужно воспользоваться следующими правилами: #object {
position:fixed;
top:Орх;
left:0px; }
Сравнение типов позиционирования
В примере 18.4 показано, как могут использоваться эти разные значения позиционирования.
Пример 18.4. Применение разных значений позиционирования
<!DOCTYPE html>
<html>
<head>
<Ш1е>Позиционирование</Ш1е>
<style>
lobjectl {
position absolute:
background:pink;
width height
:100px: :lOOpx;
top
:100px;
left
#object2 {
:Opx; }
position
relative;
background
1ightgreen:
width
lOOpx;
height
lOOpx;
top
-8px;ч
left :llOpx; }
#object3 {
position:fixed:
background-.yellow;
width:lOOpx:
height:lOOpx;
top:lOOpx:
left:236px: }
</style>
</head>
<body>
<br /><br /><br /><br /><br />
<d1v id='objectr>A6conK)THoe позиционирование<^1 v>
<div id= ’object2’Относительное позиционирование</й1v>
<div id=,object3'>0HKcnpOBaHHoe позиционирование^! v>
</body>
</html>
На рис. 18.11 код примера 18.4 был загружен в браузер, и окно браузера было уменьшено по ширине и высоте, чтобы появилась необходимость в прокрутке вниз, чтобы увидеть всю веб-страницу. На рисунке элемент с фиксированным позиционированием, который изначально находился на одной линии с другими двумя элементами, остался на месте, в то время как другие элементы были прокручены вверх по странице, и теперь этот элемент оказался смещенным ниже двух других элементов.
Если вы наберете код этого примера (или загрузите его с сопровождающего веб-сайта) и загрузите этот код в свой браузер, то увидите, что элемент с фиксированным позиционированием остается на месте даже при прокрутке страницы. Вы также можете заметить, что элемент с абсолютным позиционированием расположен точно на 100 пикселов ниже с нулевым горизонтальным смещением, а элемент
с относительным позиционированием фактически переместился вверх на 8 пикселов, а затем сместился от левого края на 110 пикселов, чтобы выстроиться в линию рядом с первым элементом.
Псевдоклассы
Существуют селекторы и классы, используемые только внутри таблиц стилей и не имеющие каких-либо соответствующих тегов или атрибутов в HTML. Их задача заключается в том, чтобы классифицировать элементы, используя характеристики, отличные от их имен, атрибутов или содержимого, то есть характеристики, которые не могут быть прослежены по дереву документа. К их числу относятся такие псевдоклассы, как fi rst-line, first-chi Id и first 1 - letter.
Псевдоклассы отделяются от элементов с помощью символа двоеточия (:). Например, для создания класса по имени bigfi rst для выделения первой буквы элемента можно воспользоваться следующим правилом:
.bigfi rs t t:fi rst-1etter {
font-size:4002;
float :le ft: }
Когда класс bigfirst применяется к текстовым элементам, первая буква будет отображаться сильно увеличенной, а остальной текст будет показан в обычнбм размере, аккуратно ее обтекая (благодаря свойству float), как будто первая буква является изображением или другим объектом. В число других псевдоклассов входят '■over, 1 i nk, acti ve и vi si ted. Все они наиболее полезны применительно к anchor-элементам, как показано в следующих правилах, которые устанавливают для ссылок в качестве исходного синий цвет, а для посещенных ссылок — светло-синий цвет:
.link { color:blue:}
vi si ted { color:lightblue; }
Следующая пара правил интересна тем, что в них используется псевдокласс "over, поэтому они применяются только при помещении указателя мыши над элементом. В этом примере они изменяют в ссылке цвет текста на белый на красном фоне, предоставляя динамический эффект, который можно было бы ожидать только от использования кода JavaScript:
hover {
color :white: background:red: }
Здесь вместо более длинного свойства цвета фона — background-color я использовал свойство фона — background с единственным аргументом.
Псевдокласс acti ve также имеет динамический характер, выражающийся в том, что он влияет на изменение ссылки в промежутке времени между щелчком кнопкой мыши и освобождением этой кнопки, как в следующем правиле, изменяющем цвет ссылки на темно-синий:
=:active { color:darkblue: }
Еще одним интересным динамическим псевдоклассом является focus, который применяется, только когда элемент получает фокус путем его выбора пользователем посредством клавиатуры или мыши. Следующее правило применяет универсальный селектор, чтобы всегда помещать светло-серую пунктирную границу толщиной 2 пиксела вокруг объекта, имеющего фокус:
*:focus { border:2рх dotted #888888: }
Как показано на рис. 18.12, код примера 18.5 выводит две ссылки и поле ввода. Первая ссылка показана серым цветом, поскольку она уже посещалась в этом браузере, а вторая ссылка еще не посещалась и показана синим цветом. Была нажата клавиша Tab, и фокусом ввода теперь служит поле ввода, поэтому цвет его фона поменялся на желтый. Когда будет щелчок кнопкой мыши на любой из ссылок, она отобразится фиолетовым цветом, а когда над ней будет проходить указатель мыши, она будет показана красным цветом.
Пример 18.5. Псевдоклассы link и focus
<!DOCTYPE html>
<html>
<head>
<ti tl е>Псевдокласськ/ti 11e>
<style>
a:1ink{{color:blue;}}
a:visitedI{{color:gray:}}
a:hover{{col or:red;}}
a:active1{{col or:purple;}}
*:focus{{background:yellow; }
</style>
</head>
<body>
<р>Для перемещения фокуса по элементам нажимайте клавишу Tab (и Shift+Tab)</p> <br /> <br />
<а href='http://google.com'>Ссылка на Google'</a><br /> <a href=’nowhere’>Ссылка вникуда’</а><Ьг /> <input type='text' />
</body>
</html>
Рис. 18.12. Псевдоклассы, примененные к подборке элементов
Доступны также и другие псевдоклассы, дополнительную информацию о них можно получить па следующем веб-сайте: http://tinyurl.com/pseudoclasses.
«* I Применять псевдокласс focus к универсальному селектору *, как показано в этом примере, ^ А , нужно с большой осторожностью — Internet Explorer воспринимает документ, не имеющий — фокуса, как уже имеющий фокус, применяемый ко всей веб-странице, и (в этом случае) будет желтой вся страница, пока не будет нажата клавиша Tab, или же фокус не перейдет на один из элементов страницы.
Псевдоэлементы
Псевдоэлементы являются средствами добавления к элементу не стиля, а содержимого. Они включаются путем установки двоеточия после спецификатора типа элемента, за которым следует псевдоэлемент. Например, для помещения какого- нибудь текста перед элементом, использующим класс offer, можно применить следующее правило:
offenbefore { contents'Special Offer! }
Теперь элемент, использующий класс offer, будет иметь строку, которая предоставлена свойству content и отображается перед этим элементом. Точно так же можно использовать псевдоэлемент :after для помещения чего-либо после всех ссылок (например), как в следующем коде, благодаря которому за всеми ссылками на странице будет следовать изображение 1i nk. gi f:
raftIer { url(link.gif): }
Сокращенная запись правил
Для экономии пространства группы родственных CSS-свойств могут объединяться в простое сокращенное назначение. Например, я уже несколько раз использовал сокращение для создания границы, как в правиле focus в предыдущем разделе:
*:focus { border:2рх dotted #888888: }
На самом деле это сокращенное объединение следующего набора правил:
т focus {
border width:2рх;
border-style:dotted.
border coIlorr:#ff8800. [
При использовании сокращенной записи правила нужно только применить свойства к тому пункту, у которого следует изменить значения. Для установки только ширины и стиля границы без изменения ее цвета можно также использовать c/i еду ю 1цее правило:
4 focus { bor ( d(er:2рх dot 1 <ч!. }
Порядок размещения свойств в сокращенной записи правила может играть важную роль, и их неправильная расстановка приводит обычно к неожиданным результатам. Поскольку изложить многочисленные подробности в данной главе не представляется возможным, при необходимости воспользоваться сокращенной записью CSS вы сможете найти описание свойств, используемых по умолчанию, и порядок их применения в руководстве по CSS или в какой-нибудь поисковой системе. Для начала могу порекомендовать зайти на следующий веб-сайт: http://tjnyurl.com/shcss.
Модель блока и макет страницы
Свойства CSS, влияющие на макет страницы, основаны на модели блока (более подробно этот вопрос рассмотрен в главе 19), вложенном наборе свойств, окружающем элемент Фактически такие свойства есть (или могут быть) у всех элементов, включая тело документа, чье поле вы можете (к примеру) удалить с помощью следующего правила:
body { margin:Орх; }
Модель блока объекта начинается снаружи, с поля объекта. Внутри него находится граница, затем следует отступ содержимого от границы. И наконец, следует содержимое объекта.
Если приобрести навыки работы с моделью блока, то можно существенно продвинуться на пути создания профессионально спланированных страниц, поскольку даже только эти свойства во многом определяют стилевое оформлен ие страницы.
Установка полей
Поле является самым крайним уровнем модели блока. Оно отделяет элементы друг от друга и требует разумного использования. Предположим, к примеру, что вы решили выбрать по умолчанию поле в 10 пикселов вокруг каждого из нескольких элементов. Если расположить их друг над другом, то в результате сложения полей между ними получится разрыв в 20 пикселов.
Чтобы устранить эту потенциальную проблему, когда два элемента с полями позиционируются непосредственно один над другим, используется только самое большое из двух полей для отделения их друг от друга. Если оба поля имеют одинаковую ширину, используется только одна ширина. С помощью этого вы, скорее всего, добьетесь желаемого результата. Но при этом следует иметь в виду, что поля элементов с заданным абсолютным позиционированием или встраиваемых элементов не подвергаются поглощению другими полями. .
Поля элемента могут быть изменены целиком с помощью свойства margin или отдельно друг от друга с помощью свойств margin- left, margin-top, margin-right и margin-bottom. При установке свойства margin можно предоставить один, два, три или четыре аргумента, в результате чего получится эффект, прокомментированный в следующих правилах:
~ Установка всех полей шириной Л пиксел */ rgi n:lpx:
* Установка верхнего и нижнего полей шириной 1 пиксел, а левого и правого - 2 пиксела */
~згд-тЛрх 2рх;
* Установка верхнего поля шириной 1 пиксел, левого и правого - 2 пиксела и нижнего - 3 пиксела */
"Згд-тЛрх 2рх Зрх;
* Установка верхнего поля шириной 1 пиксел, правого - 2. нижнего - 3 и левого - 4 пиксела */
~argin;lpx 2рх Зрх 4рх;
На рис. 18Л3 показан код примера 18.6, загруженный в браузер, где правило свойства таrgi п (выделенное в коде полужирным шрифтом) применяется к прямоугольным элементам, помещенным внутри элемента <tabl е> Размер таблицы не задан, поэтому она будет просто охватывать как можно плотнее внутренний <di у>-элемент. Вследствие этого сверху будет поле шириной 10 пикселов, справа —поле шириной 20 пикселов, снизу — поле шириной 30 пикселов и слева — поле шириной 40 пикселов.
Пример 18.6. Порядок применения полей
<!DOCTYPE html>
<html>
<head>
<Ш1е>Поля</Ш1е>
<style>
#objectl {
background lightgreen;
border-style solid;
border-width lpx;
font-family Courier New;
font-size9px;
widthlOOpx;
heightlOOpx;
padding5px;
marginЮрх 20px 30px 40px; }
</style>
</head>
<body>
<table bordere1r cel Ipadding='0’ cel lspacing=’0’ Ьдсо1ог='суап'>
<tr>
<td>
<div id=’objectl’>margin:<br />10рх 20px 30px 40px;</div>
</td>
</tr>
</table>
</body>
-/html>
Рис. 18.13. Охватывающая таблица расширяется в соответствии с шириной полей
Применение границ
Уровень границ модели блока похож на уровень полей, за исключением того, что здесь отсутствует поглощение. Это следующий уровень по мере продвижения к центру модели блока. Основными свойствами, используемыми для изменения границ, являются border, border-left, border-top, border-right и border-bottom. Каждое из них может иметь другие подсвойства, добавляемые в виде суффиксов, например -color, -style и -width.
Четыре способа установки отдельных свойств, которые использовались для свойства margi п, применимы также и для свойства ширины границы border-wi dth, поэтому все следующие правила составлены верно:
/* Все границы */ border-width:lpx;
/* Верхняя/нижняя и левая/правая */ border-wi dth;lpx 5рх ;
/* Верхняя, левая/правая и нижняя */ border-width:lpx 5рх Юрх;
/* Верхняя, правая, нижняя и левая */ border-width: lpx 5рх Юрх 15рх:
На рис. 18.14 показано каждое из этих правил, примененное но очереди к группе квадратных элементов. Если смотреть на первый из них, сразу становится понятно, что ширина всех границ равна 1 пикселу. А вот у второго элемента верхняя и нижняя границы имеют ширину 1 пиксел, а его боковые границы имеют ширину по 5 пикселов. У третьего элемента верхняя граница шириной 1 пиксел, его боковые границы —но 5 пикселов, а его нижняя — 10 пикселов. Четвертый элемент изображен с верхней границей шириной 1 пиксел, с правой границей 5 пикселов, нижней шириной 10 пикселов и левой шириной 15 пикселов.
Рис. 18.14. Применение правил задания границ в полной и сокращенной записи
Для последнего элемента, расположенного под предыдущими элементами, сокращенная запись правил не использовалась. Вместо этого каждая из его границ задавалась отдельно. Как видите, для получения аналогичного результата потребовалось набрать значительно больше символов.
Настройка отступов
Самыми глубокими уровнями модели блока (отличающимися от содержимого элемента) являются отступы, применяемые внутри любых границ и (или) полей. Основными свойствами, используемыми для изменения отступов, являются padding, Dadding-1 eft, padding-top, padding-right и padding-bottom.
Те четыре способа установки отдельных свойств, которые задействовались для свойств margi п и border, применимы также и для свойства отступа - paddi ng, поэтому все следующие правила составлены верно:
Все отступы */
Daddingilpx;
Верхний/нижний и левый/правый */ wadding:1рх 2рх;
Верхний, левый/правый и нижний */ .-adding: 1рх 2рх Зрх;
Верхний, правый, нижний и левый */
..adding :1рх 2рх Зрх 4рх;
11а рис. 18.15 показаны правила отступов, выделенные в примере 18.7 полужирным шрифтом и примененные к тексту в ячейке таблицы (как определено г помощью правила display:tablf1 cell которое задает охват iv-элемепта
наподобие ячейки таблицы). Размеры ячейки не заданы, поэтому она максимально плотно охватывает текст. Вследствие этого получается отступ, равный 10 пикселам, над внутренним элементом, 20 пикселам —справа, 30 пикселам —снизу и 40 пикселам — слева.
Пример 18.7. Применение отступов
<!DOCTYPE html>
<html>
<head>
<t i 11 е>Отступы</И tl e>
<style>
#objectl {
border-style:solid: border-width:lpx: background:orange:
color:darkred:
font-face:Arial:
font-size:12px;
text-align-.justify:
display.table- cell:
width:148px:
padding:10px 20px30px 40px; }
</style>
</head>
<body>
<div id='objectr>To be. or not to be that is the question: Whether *tis Nobler in the mind to suffer The Slings and Arrows of outrageous Fortune. Or to take Arms against a Sea of troubles. And by opposing end them.</div> </body>
</html>
Рис. 18.15. Применение к объекту разных значений отступов
Содержимое объекта
В глубине модели блока, в его центре, находится элемент, стиль которого может быть задан всеми способами, рассмотренными ранее в этой главе, и который может содержать (а зачастую и содержит) еще и подчиненные элементы, у которых, в свою очередь, могут быть свои подчиненные элементы и т. д., у каждого из которых могут быть свои настройки стиля и модели блока.
Теперь, после изучения основ, в следующей главе мы рассмотрим усовершенствованные CSS-таблицы, включая и способы применения таких переходных эффектов, как перемещение и вращение, а также других новых интересных свойств, появившихся в CSS3.
Проверьте ваши знания
Какая инструкция используется для импорта одной таблицы стилей в другую (или в блок <style> кода HTML)?
Каким HTML-тегом можно воспользоваться для импорта таблицы стилей в документ?
Какой атрибут HTML-тега применяется для непосредственной вставки стиля в элемент?
В чем разница между идентификатором CSS и классом CSS?
Какие символы используются в качестве префиксов в CSS-правилах: а) идентификаторы и б) классы?
Каково назначение точки с запятой в CSS-правилах?
Как в таблице стилей добавляется комментарий?
Какой символ используется CSS для представления «любого элемента»?
Как в CSS можно выбрать группу разных элементов и (или) типов элементов?
Как можно задать преимущество одного из двух CSS-правил, имеющих одинаковый уровень приоритета?
Ответы па эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 18».
Л?
Расширение CSS с помощью CSS3
Первая реализация CSS была разработана в 1996 году, выпущена в 1999 году и к 2001 году была поддержана всеми выпусками браузеров. Стандарт для этой версии, CSS1, был еще раз пересмотрен в 2008 году. Со второй спецификацией, CSS2, разработчики начали работать в 1998 году; ее стандарт был в конечном итоге завершен в 2007 году, а затем еще раз пересмотрен в 2009 году.
В 2001 году началась разработка спецификации CSS3, а ее характеристики были предложены сравнительно недавно, в 2009 году. Процесс разработки, вероятно, еще продолжится до поступления завершающих рекомендаций по CSS3. И даже несмотря на то, что разработка спецификации CSS3 еще не завершена, уже начали поступать предложения на будущее для спецификации CSS4.
В этой главе будут описаны характеристики CSS3, которые уже в целом приняты всеми основными браузерами. Некоторые из этих характеристик предоставляют такие функциональные возможности, которые до этого могли быть предоставлены только с помощью JavaScript.
Я рекомендую использовать CSS3 для реализации динамических свойств везде, где только можно, вместо JavaScript, поскольку весьма вероятно, что поддержка CSS приобретет очень высокий уровень оптимизации (и в силу этого также и очень высокое быстродействие) и, что для вас не менее важно, при этом упростится поддержка ваших продуктов в новых браузерах и их версиях.
Селекторы атрибутов
В предыдущей главе были подробно рассмотрены различные селекторы атрибутов, применяемые в CSS, которые мы сейчас вкратце повторим. Селекторы используются в CSS для сопоставления с HTML-элементами. Существует десять разных типов селекторов, показанных в табл. 19.1.
Таблица 19.1. CSS-селекторы, псевдоклассы и псевдоэлементы
Тип селектора
Пример
Универсальный селектор
* { color:#555; }
Селекторы типов
b { color:red; }
Селекторы классов
.classname { color: blue; }
Тип селектора
Пример
Селекторы идентификаторов
#idname { background :cyan; >
Селекторы потомков
span em { colorigreen; >
Селекторы дочерних элементов
div > em { background:lime; }
Селекторы смежных элементов
i + b { color:gray; }
Селекторы атрибутов
a[href='info.htm'] { color:red; }
Псевдоклассы
a:hover { font-weight:bold; }
Псевдоэлементы
p::first-letter { font-size:300%; }
Разработчики CSS3 решили, что большинство из этих селекторов работают достаточно хорошо и в представленном на данный момент виде, но три усовершенствования, направленные на упрощение поиска соответствия элементам на основе содержимого их атрибутов, они все же внесли.
Соответствующие части строк
В CSS2 для поиска соответствия строке ' i nfo. htm', находящейся в href-атрибуте, можно было использовать такой селектор, как a [href= ’ i nfo. htm' ], но поиска соответствия только части строки не существовало. Но в CSS3 пошли дальше и определили три новых оп ерат[ора:$ и *. Если один из них непосредственно предшествует символу равенства (=), то с помощью этих символов в том порядке, в котором они перечислены, можно искать соответствие в начале, в конце или в любой части строки.
Оператор Л
Например, следующему селектору будет соответствовать любой href-атрибут, чье значение начинается со строки ’ http: / /webs i te ':
s[hЕrfe f'http://websi te']
Таким образом, ему будет соответствовать следующий элемент:
<а href='http://website.com'>
А этот элемент соответствовать не будет:
<а href='http://mywebsite.com'>
Оператор $
Для поиска соответствия только в конце строки можно использовать следующий селектор, которому будет соответствовать любой img-тег, чей src-атрибут заканчивается на '.png *:
img[src$='.png']
Например, ему будет соответствовать следующий тег:
<img src='photo.png’ />
А этот тег соответствовать не будет:
<img src='snapshot.jpg' />
Оператор *
Для поиска соответствия подстроки, находящейся в любом месте атрибута, можно воспользоваться следующим селектором. Он найдет любые ссылки па странице, имеющие строку 'google' в любом месте ссылки:
a[href*='google']
Например, ему будет соответствовать эта часть кода HTML:
<а href='http://google.com'>
а эта часть соответствовать не будет:
<а href='http://gmai1.com’>
Свойство box-sizing
В модели блока W3C определено, что ширина и высота объекта должна относиться только к размерам содержимого элемента, игнорируя любые отступы или границы. Но некоторые веб-дизайнеры выразили желание указывать размеры, относящиеся ко всему элементу, включая любые отступы и границы.
Чтобы предоставить такое свойство, CSS3 позволяет вам выбрать желаемую модель блока со свойством задания размеров блока —box-sizing. Например, для использования общей ширины и высоты объекта, включая отступы и границы, нужно применять следующее объявление:
box-si zing:border-box;
Или, чтобы ширина и высота объекта относилась только к содержимому, нужно воспользоваться следующим объявлением (применяемым по умолчанию):
box-si zing:content-box;
Браузеры на движках WebKit и Mozilla (такие как Safari и Firefox соответственно) требуют « для этого объявления использования собственных префиксов (-webkit- и -moz-), о чем.по- дробно рассказывается на веб-сайте http://caniuse.com.
Создание фона в CSS3
Спецификация CSS3 предоставляет два новых свойства, background-cl i р и backgroundorigin, которые могут использоваться для указания, где должен фон начинаться внутри элемента и как усекать фон так, чтобы он не появлялся в тех частях модели блока, где он нежелателен.
Для выполнения этих задач оба свойства поддерживают следующие значения:
О border-box — относится к внешнему краю границы;
О padding-box — относится к внешнему краю области отступа;
О content-box —относится к внешнему краю области содержимого.
Свойство background-dip
Это свойство определяет, должен ли фон игнорироваться (усекаться) если он появляется либо внутри границы, либо в области отступов элемента. Например, следующее объявление определяет, что фон может отображаться во всех частях элемента, вплоть до внешнего края границы:
:ackground-clip:border-box:
Если не нужно, чтобы фон появлялся в области границы элемента, его можно ограничить только той частью элемента, которая находится внутри и заканчивается внешним краем его области отступов, например:
:ackground-cli р:paddi ng-box;
Или же можно ограничить фон, чтобы он отображался только внутри области содержимого элемента, воспользовавшись следующим объявлением:
:ackground-cli р:conttentt-box;
На рис. 19.1 показаны три ряда элементов, отображаемых в веб-браузере Safari: в первом ряду для свойства background-clip используется значение border-box, во втором применяется значение padding-box, а в третьем используется значение :ontent-box.
В первом ряду внутреннему блоку (файлу изображения, загруженному в левую верхнюю часть элемента с отключенным повторением) разрешается отображаться в элементе везде. Можно также совершенно отчетливо видеть, что он отображается в области границы первого блока, поскольку стиль границы указан пунктирным.
Во втором ряду в области границы не отображаются ни фоновое изображение, ни фоновое затенение, поскольку они были усечены по области отступов с помощью установки для свойства background-cl ip значения padding-box.
И наконец, в третьем ряду и фоновое затенение, и фоновое изображение были усечены для отображения только внутри области содержимого каждого элемента (показанного внутри светлого, ограниченного пунктирной линией блока), путем использования для свойства background-cl ip значения content-box.
Свойство background-origin
С помощью этого свойства можно также указать, где должно располагаться фоновое изображение, указав для этого, где должен начинаться левый верхний угол данного изображения. Например, следующее объявления указывает, что начало фонового изображения должно быть в левом верхнем углу внешнего края границы:
Dackground-origin:border-box;
Чтобы установить начало изображения в левый верхний внешний угол области отступов, нужно воспользоваться следующим объявлением:
Dackground-origin:padding-box;
^ CSS3 Background Clip^ CSS3 Background ClipРис. 19.1. Разные способы сочетания свойств фона CSS3Рис. 19.1. Разные способы сочетания свойств фона CSS3
^ CSS3 Background Clip
^ CSS3 Background Clip
Рис. 19.1. Разные способы сочетания свойств фона CSS3
Рис. 19.1. Разные способы сочетания свойств фона CSS3
И чтобы установить начало изображения в левый верхний угол области внутреннего содержимого элемента, нужно воспользоваться следующим объявлением: background-ori gin:content-box:
Посмотрите еще раз на рис. 19.1, в каждом ряду для первого блока используется свойство background-origin со значением border-box, для второго блока это же свойство применяется со значением padding-box, а для третьего — со значением content-box. Следовательно, в каждом ряду меньший по размеру внутренний блок отображается для первого блока в левом верхнем углу границы, для второго он отображается в левом верхнем углу области отступов, а для третьего — в левом верхнем углу содержимого.
Единственное отличие рядов, которое стоит отметить в отношении начала внутреннего блока на рис. 19.1 состоит в том, что во втором и третьем рядах внутренний блок усекается соответственно областями отступов и содержимого, и поэтому та часть блока, которая находится за пределами этих областей, не отображается.
Свойство background-size
Точно так же, как это делалось для указания ширины и высоты изображения при использовании тега <img />, в последних версиях всех браузеров можно сделать то же самое для изображений фона.
Это свойство можно применить следующим образом (здесь wzv -- ширина, a hh высота):
background-si ze:tvwpx /7/?рх:
При необходимости можно использовать только один аргумент, и для обоих размеров будет установлено указанное значение. Кроме того, если применить данное свойство к блочному элементу, например к <di v> (но не к такому встроенному элементу, как <span>), можно указать ширину и (или) высоту в процентном отношении, а не в виде фиксированного значения.
Использование значения auto
Если нужно масштабировать только один размер фонового изображения, чтобы при этом автоматически масштабировался и другой его размер для соблюдения прежних пропорций, для другого размера можно воспользоваться значением auto:
:ackground-size: ЮОрх auto:
Этим объявлением устанавливается ширина, равная 100 пикселам, и высота, равная значению, пропорциональному увеличению или уменьшению ширины.
Разные браузеры могут требовать различных версий имен свойства background, поэтому при их использовании обратитесь к веб-сайту http://caniuse.com, чтобы убедиться, что вы применяете все версии, которые требуются тем браузерам, на работу с которыми вы рассчитываете.
Использование нескольких фонов
Теперь, используя CSS3, вы можете прикрепить к элементу несколько фонов, каждый из который может применять ранее рассмотренные свойства фо 1 на CSS3. Соответствующий пример показан на рис. 19.2. На этом рисунке в качестве фона были назначены восемь изображений, которые используются для создания четырех углов и четырех кромок границы сертификата.
Для вывода нескольких фоновых изображений с помощью одного CSS-объявления нужно их разделить занятыми. В примере 19.1 показан код HTML и CSS, использованный для создания фона рис. 19.2.
Пример 19.1. Использование в фоне сразу нескольких изображений
-DOCTYPE html> '-html >
<head>
<tit.le>CSS3: пример нескольких фоновых изображений^/1i1 1e>
<style>
.border {
font-famiI у :‘ Iimes New Roman'
font-stylo ita lic :
font-size :1705:
text-a 1ign center:
padding60px:
width350px:
height500px:
background u rl( ’b l.g if)topleftno-repeat
ur1(’b2.gif)toprightno-repeat
u rl( ’b3 .gif)bottomleftno-repeat
u rl( ’b4.gif)bottomrightno-repeat
u rl( ’ba.gi f ) toprepeat-x.
url('bb.gif1) leftrepeat-y.
urH’bc.gif ) right repeat-y.
urK'bd.qif) bottomrepeat x 1
<Zstyle>
/head>
body>
<div class='border’>
<И1>Свидегельсгво пловца'- /hi
<h2>Hai раждаеи:я: </h2"
<h3>' /h3-
<И2>Дата:</h2>
<h3>__/__/___ </ h 3 >
</div>
</body>
' /html>
Первые четыре строки объявления фона в блоке CSS расставляют угловые изображения по четырем углам элемента, а последние четыре строки помещают изображения кромок, которые обрабатываются в последнюю очередь, потому что порядок приоритетности для фоновых изображений имеет направление сверху вниз. Иными словами, когда они накладываются друг на друга, дополнительные фоновые изображения будут появляться позади уже размещенных изображений. Если бы GIF-изображепия были перечислены в обратном порядке, то повторяющиеся изображения кромок отображались бы поверх углов, что было бы неправильно.
Используя этот код CSS, можно изменять размеры содержащего фон элемента по любым направлениям, и граница будет всегда правильно изменяться в размерах, чтобы поместиться в элементе, что намного проще, чем применение таблиц или нескольких элементов для получения такого же эффекта.
Границы CSS3
CSS3 так же придает намного больше гибкости способам возможного представления границ, разрешая независимо менять цвета всех четырех кромок, отображать изображения для кромок и углов, предоставлять значения радиусов /утя придания границам закругленных углов и помещать прямоугольные тени под элементами.
Свойство border-color
Применять цвета к границе можно двумя способами. Начнем с того, что свойству можно передать всего один цвет:
:order-colог:#888:
Это объявление устанавливает светло-серый цвет для всех границ элемента. Можно также установить цвета границ по отдельности (здесь цвета границы устанавливаются в различные градации серого):
:order-top-color :#000:
:order-left-color :#444:
:order-right-color :#888:
:order-bottom-color:#ccc:
Кроме того, можно назначить все цвета но отдельности в одном объявлении:
:order - color:#f00 #0f0 #880 #00f:
Это объявление устанавливает цвет верхней границы в #f00, правой границы B#0f0, нижней границы в#880 и левой границыв#00f (красный, зеленый.
оранжевый и синий соответственно). Можно также использовать в качестве аргументов названия цветов, что уже рассматривалось в предыдущей главе.
Свойство border-radius
До появления CSS3 способные веб-разработчики придумали множество различных настроек с целью получения закругленных границ, используя, как правило, теги <table> или <div>
Но теперь добавление закругленных границ к элементу дается по-настоящему просто, и, как показано на рис. 19.3, работает в последних версиях всех основных браузеров. На этом рисунке граница толщи ной 10 пикселов выведена различными способами. Код HTML для получения такого результата показан в примере 19.2.
Пример 19.2. Свойство border-radius
<D0CTYPF html
<html"
head •
'I it le>CSS3: примеры радиусов фаниц </title>
- style-»
.box {
margin bottom
font-family font-size text aligni padding width height border
bl { moz-border
Юрх:
’'Courier New ’ . monospace: 12pt:
center:
Юрх :
380px:
75px;
Юрх sol id #006: |
radius 40px
webkit border radius 40px
border-radius
b2 {
-moz-border-radius
40:px . }
:40px 40px 2Cf)x 20px:
-webkit-border-radius:40px 40px 2Cf>x 20px:
border-radius ,b3 {
x 40px4 2Cx 20[)x 20px:
-moz-border-radius-topi eft
-moz-border-radius-topright
-moz-border-radi us -bottomleft
-moz-border-radius-bottomright
-webkit-border-top-left-radius
-webkit-border-top-right-radius
20px
40px
60px
80px
20px
40px
-webk i t -border -bottom-1 eft - rad i us 60px
-webk i t - border -bottom-ri ght-radi us: 80px
border-top-1 eft -radi us border-top-ri ght-radi us border-bottom-left-radius border-bottom-right radius
h4 {
20px
40px
60px
80px )
:40рх 20рх::40рх 20рх;:40рх 20рх::40рх 20рх;-moz-border-radius-topl eft
:40рх 20рх:
:40рх 20рх;
:40рх 20рх:
:40рх 20рх;
-moz-border-radius-topright
-moz-border-radius-bottomleft:20рх40рх:
-moz-border-radi us-bottomri ght:20рх40рх:
-webkit-border-top-left-radius:40рх20рх:
-webk i t -border-top-ri ght-radi us:40рх20рх:
-webkit-border-bottom-1 e ft-radi us:20рх40рх:
-webk i t - border-bottom-r i ght-ra<di us:20рх40рх;
border-top-1 eft -radi us:40рх20рх:
border-top-ri ght-radi us:40рх20рх:
border-bottom-left-radius:20рх40рх:
border- bottom-ri ght-radi us:20рх40рх:}
</style>
</head>
<body>
<div class='box b]l’>
border-radius:40px:
</div>
<div class='box b2’>
border-radius:40px 40px 20px 20px;
</div>
<div class='box b3’>
border-top-left-radius  : : ::20px;<br/> border-top-right-radius :40px;<br />
border-bottom-left-radius :60px;<br />
border-bottom-r i gh I t-radi us:80px;
</div>
<div class=’box b4'>
border-top-left-radius  : : ::40px 20px:<br />
border-top-right-radius  ::40px 20px;<br />
border-bottom-left-radius :20px 40px:<br />
border-bottom-right-radi us:20px 40px:
</div>
</body>
</html>
Так, например, для создания закругленной границы радиусом 20 пикселов можно просто воспользоваться следующим объявлением:
border-radius:20px:
"N^
Хотя многие браузеры (включая Internet Explorer) со свойствами радиусЬв границы будут
«У 4 . работать хорошо, некоторые текущие (и многие более старые) версии основных браузеров —используют разные имена свойств, и если нужна поддержка всех этих браузеров, придется также включить для них соответствующие префиксы, характерные для тех или иных браузеров, такие как -moz- и -webkit-. Чтобы обеспечить работу предыдущего примера во всех браузерах, я добавил в код все требуемые префиксы.
Pl CSS3: примеры рэдиусог хPl CSS3: примеры рэдиусог хС л Ч borderradius.htmС л Ч borderradius.htmborder-radius:4 0рх;border-radius:4 0рх;border-top-left-radius border-top-right-radiusborder-top-left-radius border-top-right-radiusborder-radius:40рх 40рх 20рх 20рх;
Pl CSS3: примеры рэдиусог х
Pl CSS3: примеры рэдиусог х
С л Ч borderradius.htm
С л Ч borderradius.htm
border-radius:4 0рх;
border-radius:4 0рх;
border-top-left-radius border-top-right-radius
border-top-left-radius border-top-right-radius
:20рх;:4 0px;:20рх;:4 0px;20px;20px;40px;4 0px;20px;20px;40px;4 0px;border-bottom-left-radius :60px;
:20рх;
:4 0px;
:20рх;
:4 0px;
20px;
20px;
40px;
4 0px;
20px;
20px;
40px;
4 0px;
border-bottom-right-radius:8Clpx;
border-top-left-radius border-top-right-radius
:40px :40px:40px :40pxborder-bottom-left-radius : 2Qpx border-bottom-right-radius:20px
:40px :40px
:40px :40px
Рис. 19.3. Смешивания и сопоставления различных свойств радиусов границы
Можно указать отдельные радиусы для каждого из четырех углов (по часовой стрелке, начиная с левого верхнего угла):
border-radius:Юрх 20рх ЗОрх 40рх:
При необходимости можете также указать радиус отдельно для каждого угла элемента:
border-top-left-radius:20рх;
border-top-right-radi us:40px;
border-bottom-left-radius :60px;
border-bottom-ri ght-radi us:80px;
И при ссылке на отдельные углы можно предоставить два аргумента, выбирая тем самым разные вертикальные и горизонтальные радиусы (в результате чего получаются более интересные и тонко настраиваемые границы):
border-top-left-radius :40рх 20рх;
border-top-right^radi us :40рх 20рх;
border-bottom-left-radius :20рх 40рх;
border-bottom-ri ght-radi us:20px 40px;
Первым аргументом задается горизонтальный, а вторым вертикальный радиус.
Прямоугольные тени
Для применения прямоугольной тени нужно указать горизонтальное и вертикальное смещение от объекта и величину размытости, добавляемой к тени, а также используемый для тени цвет:
зох-shadow: 15рх 15рх Юрх #888;
Два значения по 15рх задают (по порядку) горизонтальное вертикальное смещение от элемента, и эти значения могут быть отрицательными, нулевыми или положительными. Значение Юрх указывает величину размытости (примерно четверть сантиметра на обычном дисплее), где меньшие значения приводят к меньшей размытости, а #888 — это цвет тени, который может быть любым допустимым цветом (см. раздел «Цвета CSS» главы 18). Результат этого объявления можно увидеть на рис. 19.4.
Рис. 19.4. Прямоугольная тень, отображенная под элементом elementРис. 19.4. Прямоугольная тень, отображенная под элементом element
Рис. 19.4. Прямоугольная тень, отображенная под элементом element
Рис. 19.4. Прямоугольная тень, отображенная под элементом element
При использовании этого свойства в браузерах, основанных на движках WebKit и Mozilla, для него нужно применять префиксы -webkit- и -moz-.
Выход элемента за пределы размеров
В CSS2 можно определить, что делать, когда один элемент слишком велик, чтобы полностью поместиться в другом, родительском по отношению к нему элементу, путем указания для свойства overflow значения hidden, visible, scroll или auto. Но теперь в CSS3 можно также отдельно применить эти значения к горизонтальному или вертикальному направлению, как в следующих примерах объявлений:
overflow-x:hidden:
Dverf1ow-x:visible:
overflow-y:auto:
Dverflow-y:scroll:
Разметка с использованием нескольких колонок
Использование нескольких колонок уже давно является у веб-разработчиков наиболее востребованным свойством, и в CSS3 оно наконец-то было реализовано, a Internet Explorer 10 стал последним из основных браузеров, принявшим это свойство.
Теперь перетекание текста по нескольким колонкам задать не сложнее, чем указать количество колонок, а затем (дополнительно) выбрать разрядку между ними и тип разделительной линии (если она нужна). На рис. 19.5 показан результат выполнения кода примера 19.3.
С Л | Я Пример 19.3.htmС Л | Я Пример 19.3.htmNow is the winter of our discontent Made glorious summer by this sun of York; And all the clouds that lour'd upon our house In the deep bosom of the ocean buried. Now are our brows boundNow is the winter of our discontent Made glorious summer by this sun of York; And all the clouds that lour'd upon our house In the deep bosom of the ocean buried. Now are our brows boundwith victorious wreaths; Our bruised arms hung up for monuments; Our stem alarums changed to merry meetings, Our dreadful marches to delightful measures. Grim-visaged warwith victorious wreaths; Our bruised arms hung up for monuments; Our stem alarums changed to merry meetings, Our dreadful marches to delightful measures. Grim-visaged warhath smooth'd his wrinkled front; And now, instead of mounting barded steeds To fright the souls of fearful adversaries, He capers nimbly in a lady's chamber To the lascivious pleasing of a lute.hath smooth'd his wrinkled front; And now, instead of mounting barded steeds To fright the souls of fearful adversaries, He capers nimbly in a lady's chamber To the lascivious pleasing of a lute.
С Л | Я Пример 19.3.htm
С Л | Я Пример 19.3.htm
Now is the winter of our discontent Made glorious summer by this sun of York; And all the clouds that lour'd upon our house In the deep bosom of the ocean buried. Now are our brows bound
Now is the winter of our discontent Made glorious summer by this sun of York; And all the clouds that lour'd upon our house In the deep bosom of the ocean buried. Now are our brows bound
with victorious wreaths; Our bruised arms hung up for monuments; Our stem alarums changed to merry meetings, Our dreadful marches to delightful measures. Grim-visaged war
with victorious wreaths; Our bruised arms hung up for monuments; Our stem alarums changed to merry meetings, Our dreadful marches to delightful measures. Grim-visaged war
hath smooth'd his wrinkled front; And now, instead of mounting barded steeds To fright the souls of fearful adversaries, He capers nimbly in a lady's chamber To the lascivious pleasing of a lute.
hath smooth'd his wrinkled front; And now, instead of mounting barded steeds To fright the souls of fearful adversaries, He capers nimbly in a lady's chamber To the lascivious pleasing of a lute.
Рис. 19.5. Перетекание текста по нескольким колонкам
Пример 19.3. Использование CSS для создания нескольких колонок
<D0CTYPE html>
<html>
<head>
<Ш1е>Использование колонок</Ш1е>
<style> .columns {
text-align
:justify:
font-size:
16pt;
-moz-column-count :
3:
-moz-column-gap:
lem;
-moz-column-rule :
lpx solid1 ы black:
-webkit-column-count
:3:
-webkit-column-gap :
lem;
-webkit-column-rule :
lpx solidj ы black;
column-count:
3:
column-gap:
lem;
column-rule:
lpx solid black: ]}
</style>
</head>
<body>
<div class=,columns'>
Now is the winter of our discontent
Made glorious summer by this sun of York;
And all the clouds that lour'd upon our house
In the deep bosom of the ocean buried.
Now are our brows bound with victorious wreaths:
Our bruised arms hung up for monuments;
Our stern alarums changed to merry meetings.
Our dreadful marches to delightful measures.
Grim-visaged war hath smooth'd his wrinkled front;
And now. instead of mounting barded steeds
To fright the souls of fearful adversaries.
He capers nimbly in a lady's chamber To the lascivious pleasing of a lute. </div>
</body>
</html>
Внутри класса .columns первые две строки просто предписывают браузеру выровнять текст по правому краю и установить для него размер шрифта 16pt. Эти объявления для нескольких колонок не нужны, но они улучшают отображение текста. В остальных строках элемент настраивается таким образом, чтобы внутри него текст перетекал по трем колонкам с разрывом между колонками, равным lem, и с границей в 1 пиксел, проходящей по середине каждого разрыва.
а ч
В этом примере браузеры на основе движков Mozilla и WebKit требуют для объявлении соответствующих этим браузерам префиксов.
Цвета и непрозрачность
Способы определения цветов в CSS3 существенно расширились: теперь вы можете также использовать CSS-функции для применения цветов в широко распространенных форматах RGB (красный, зеленый, синий), RGBA (красный, зеленый, синий, альфа), HSL (тон, насыщенность, яркость) и HSLA (тон, насыщенность, яркость, альфа). Значение альфа определяет прозрачность цвета, позволяющую увидеть элементы, расположенные ниже.
Цвета HSL
Для определения цвета с помощью функции hsl сначала нужно выбрать из цветового круга значение для тона в диапазоне от 0 до 359. Любой более высокий номер цвета просто возвращается по кругу к началу, таким образом значение 0 соответствует красному цвету, точно так же, как и значения 360 и 720.
В цветовом круге основные цвета — красный, зеленый и синий — поделены на 120 градусов, поэтому чистый красный цвет соответствует значению 0, зеленый — значению 120, а синий — значению 240. Числа между этими значениями представляют собой оттенки, содержащие различные пропорции основных цветов с обеих сторон.
Затем нужен уровень насыщенности, значение которого лежит в диапазоне от О до 100 %. Он определяет то, насколько сильно цвет будет размыт или ярок. Значения насыщенности начинаются в центре колеса со светло-серого цвета (насыщенность равна 0 %), а затем по направлению к краю (где насыщенность равна 100 %) становится все более отчетливой.
Вам остается только решить, насколько ярким нужен цвет, для этого нужно выбрать значение яркости в диапазоне от 0 до 100 %. Значение 50 % для яркости дает наполненный, яркий цвет, а уменьшение значения (вниз, вплоть до минимума в 0 %) делает его темнее, до тех пор пока цвет не станет черным. Увеличение значения (вверх, вплоть до максимума в 100 %) делает цвет светлее, до тех пор пока он не станет белым. Вы можете визуально представить это подмешиванием в цвет либо черного, либо белого цвета.
Так, например, для выбора полностью насыщенного желтого цвета со стандартной яркостью нужно воспользоваться следующим объявлением:
color:hsl(60. 100%. 50%);
Или для выбора темно-синего цвета можно воспользоваться следующим объявлением:
col or:hsl(240, 100%. 40%):
Этим также можно воспользоваться (как и всеми остальными CSS-функциями, связанными с заданием цвета) с любым свойством, ожидающим применения цветовых настроек, например с background-color и т. д.
Цвета HSLA
Для предоставления еще большего контроля над способом цветообразования, можно воспользоваться функцией hsl а, предоставив ей четвертый (или альфа) уровень настройки цвета, значение которого задается числом с плавающей точкой в диапазоне от 0 до 1. Значение 0 определяет, что цвет является полностью прозрачным, а число 1 задает полную непрозрачность цвета.
Выбрать желтый цвет с полной насыщенностью, стандартной яркостью и непрозрачностью 30 % можно с помощью следующего объявления:
col or:hslа(60, 100%. 50%. 0.3);
Или же для выбора полностью насыщенного, но чуть более светлого синего цвета с 82%-ной непрозрачностью можно воспользоваться следующим объявлением:
col or:hslа(240, 100%. 60%. 0.82);
Цвета RGB
Наверное, вам более знакома система выбора цвета RGB, поскольку она похожа на использование форматов цвета #пппппп и ^ппп. Например, для задания желтого
цвета можно воспользоваться любым из следующих объявлений (первое из них поддерживает шестнадцать миллионов цветов, а второе —четыре тысячи):
color:#ffff00:
color:#ffO;
Для получения такого же результата можно также воспользоваться CSS-функцией rgb, но при этом нужно применять не шестнадцатеричные, а десятичные числа (где десятичное число 255 соответствует шестнадцатеричному числу ff): color:rgb(255. 255. 0);
Но еще лучше вам будет даже не задумываться больше о том, чему соответствуют значения до 256, поскольку можно указать процентные значения: color:rgb(1002. 1002. 0):
Фактически теперь вы можете с большой точностью определить настройки для нужного цвета, просто думая о его основных цветовых составляющих. Например, сочетание зеленого и синего дает фиолетовый цвет, поэтому для задания цвета, близкого к фиолетовому, но с синей составляющей, преобладающей над зеленой, можно составить первое предположение, что для него нужно определить 0 % красного, 40 % зеленого и 60 % синего цвета и попробовать воспользоваться следующим объявлением:
col or:rgb(0%. 602. 402):
Цвета RGBA
Как и функция hsl а, функция rgba поддерживает четвертый (альфа) аргумент, позволяющий, к примеру, с помощью следующего объявления применить к прежнему фиолетовому цвету 40%-ную непрозрачность:
color:rgba(02. 602, 402. 0.4):
Свойство opacity
Свойство opacity предоставляет такое же альфа-управление, что и функции hsl а и rgba, но позволяет изменять непрозрачность объекта (или прозрачность, если это вам больше нравится) отдельно от его цвета.
Для использования этого цвета нужно применить к элементу следующее объявление (которое в данном примере устанавливает непрозрачность, равную 25 %, или прозрачность, равную 75 %):
opacity:0.25:
Jr ч -- ■- -
Для браузеров на основе движков WebKit и Mozilla для этого свойства требуются соответ. аду 4 , ствующие этим браузерам префиксы. Кроме того, для обратной совместимости с выпусками *— — 3?* Internet Explorer, предшествующими версии 9, нужно добавить следующее объявление (в котором значение непрозрачности умножено на 100):
fjlter:alpha(opacity='25');
Эффекты, применяемые к тексту
Теперь с помощью CSS3 к тексту могут применяться новые эффекты, включая тени текста, наложение, применяемое к тексту, и перенос слов.
Свойство text-shadow
Это свойство аналогично свойству box-shadow и получает такой же набор аргументов: горизонтальное и вертикальное смещение, величину размытости и используемый цвет. Например, следующее объявление задает смещение тени на 3 пиксела как в горизонтальном, так и в вертикальном направлении и отображает тень темносерым цветом с размытостью 4 пиксела:
text-shadow:Зрх Зрх 4рх #444:
Результат применения этого объявления выглядит, как показано на рис. 19.6. Это объявление работает в последних версиях всех основных браузеров (кроме Internet Explorer 9 или ниже).
This is shadowed text
Рис. 19.6. Применение тени к тексту
Свойство text-overflow
При использовании любого из CSS-свойств overflow со значением, равным hidden, можно также воспользоваться свойством text-overflow для помещения многоточия сразу же после некоторого текста, подвергшегося усечению:
text-overf1ow:el 1ipsi s :
Если это свойство не использовать, то когда текст «То be, or not to be. That is the question.» усекается, результат выглядит, как показано на рис. 19.7, но с применением объявления результат выглядит, как изображено на рис. 19.8.
То be, or not to be. That is
Рис. 19.7. Текст автоматически усекается
То be, or not to be. Tha...
Рис. 19.8. Вместо простого усечения, текст завершается многоточием
Чтобы это работало, требуется выполнить три условия.
О У элемента должно быть свойство overf1 ow, настроенное на невидимость, например overflow:hidden.
О Элемент должен иметь свойство white-space:nowrap, настраивающее на ограничение текста.
О Ширина элемента должна быть меньше, чем усекаемый текст.
Свойство word-wrap
Когда используется по-настоящему длинное слово, шире того элемента, в котором оно содержится, оно либо выйдет за пределы, либо будет усечено. Но в качестве альтернативного варианта свойству text-overflow и усечению текста можно воспользоваться свойством word-wrap со значением break-word для переноса длинных строк:
word -wrap:break-word;
Например, на рис. 19.9 показано, что слово Honorificabilitudinitatibus шире, чем содержащее его поле (чей правый край представлен в виде сплошной вертикальной черты между буквами t и а), и, поскольку свойство overflow применено не было, слово выходит за границу своего контейнера.
Honorificabilitudinitatibus
Рис. 19.9. Слово имеет слишком большую ширинудля своего контейнераи поэтому выходит за его границу
Но на рис. 19.10 свойству word-wrap элемента было присвоено значение break word и поэтому слово аккуратно перенесено на следующую строку.
Honorificabilitudinit atibus
Рис. 19.10. Теперь слово переносится по достижении правого края
Веб-шрифты
Применение веб-шрифтов CSS3 существенно повысило возможности оформления текста, доступные веб-дизайнерам, позволяя загружать шрифты из Интернета, а не только со своего пользовательского компьютера, и отображать их по всей Всемирной сети. Для достижения этого результата нужно объявить веб-шрифт, используя свойство @font - face:
@font-face
{
font - family:FontName;
src:url('FontName.otf'):
>
Функция url требует значение, содержащее путь или URL-адрес шрифта. На большинстве браузеров можно использовать либо шрифты TrueType (TTF), либо шрифты OpenType (OTF), но Internet Explorer ограничивает вас применением шрифтов TrueType, преобразованных в шрифты EOT (EOT).
Чтобы сообщить браузеру тип шрифта, можно воспользоваться функцией format, как в следующем примере (для шрифтов OpenType):
@font-face
{
font-family:FontName:
src:url('FontName.otf') format('opentype'):
1
или в этом примере (для шрифтов TrueType):
@font-face
{
font-family:FontName:
src:url('FontName.ttf') format('truetype'):
}
Но поскольку Internet Explorer принимает только EOT-шрифты, он игнорирует объявления @font-face, содержащие функцию format.
Веб-шрифты Google
Одним из лучших способов использования веб-шрифтов является их бесплатная загрузка с серверов Google. Дополнительную информацию по данному вопросу можно найти на сайте веб-шрифтов Google (http://google.com/webfonts; рис. 19.11), где можно получить доступ более чем к 500 семействам шрифтов.
Чтобы показать, насколько легко можно использовать один из этих шрифтов, в следующей строке для загрузки такого шрифта применяется HTML-тег <1 ink>:
<link href='http://fonts.googleapis.com/css?fami1y=Lobster’ />
Затем, чтобы воспользоваться таким шрифтом, его нужно просто применить в CSS-объявлении:
hi { font-family:'Lobster'. arial. serif: }
Трансформации
Используя трансформации, можно наклонять, вращать, растягивать и сжимать элементы в любом из трех измерений (да, 3D поддерживается, но пока только в браузерах, работающих на движке WebKit). Это упрощает создание впечатляющих эффек-
LDj^jeKWй ☆ ©LDj^jeKWй ☆ ©тов путем выхода за пределы однообразных макетов на основе <div>-контейнеров и других элементов, поскольку теперь они могут быть показаны под различными углами и в различных формах.
LDj^jeKWй ☆ ©
LDj^jeKWй ☆ ©
19Ы^ httpw'mvv/.google.com/webfcnts^CHoosePlace: Р С X |[«Д Google Web Fonts File |dit yiew Favorites look Help
GooglIe web fontsGooglIe web fontsKkkslart new fonts!Kkkslart new fonts!AboutAboutShowing 495 font familiesSearch:Showing 495 font familiesSearch:wordwordsentenceParagraph 'Ц Bookmark your Collection ♦ Download your CollectionsentenceParagraph 'Ц Bookmark your Collection ♦ Download your CollectionPreview I ext lie's I :le I s loni collection is va i si »S\ Size: I 36 1^pjQ^feA- f^Q/d coKediojb 16 vadPreview I ext lie's I :le I s loni collection is va i si »S\ Size: I 36 1^pjQ^feA- f^Q/d coKediojb 16 vadx SortingTren ’ dingx SortingTren ’ dingGreat Vibes. 1 Style by г л.^Г.. RGreat Vibes. 1 Style by г л.^Г.. RQ9 Quick-useQ9 Quick-used ? Pop outd ? Pop outA3d Io CoiiechonA3d Io Coiiechon
GooglIe web fonts
GooglIe web fonts
Kkkslart new fonts!
Kkkslart new fonts!
About
About
Showing 495 font families
Search:
Showing 495 font families
Search:
word
word
sentenceParagraph 'Ц Bookmark your Collection ♦ Download your Collection
sentenceParagraph 'Ц Bookmark your Collection ♦ Download your Collection
Preview I ext lie's I :le I s loni collection is va i si »S\ Size: I 36 1
^pjQ^feA- f^Q/d coKediojb 16 vad
Preview I ext lie's I :le I s loni collection is va i si »S\ Size: I 36 1
^pjQ^feA- f^Q/d coKediojb 16 vad
x SortingTren ’ ding
x SortingTren ’ ding
Great Vibes. 1 Style by г л.^Г.. R
Great Vibes. 1 Style by г л.^Г.. R
Q9 Quick-use
Q9 Quick-use
d ? Pop out
d ? Pop out
A3d Io Coiiechon
A3d Io Coiiechon
Filters:All categoriesThicknessSlantFilters:All categoriesThicknessSlantGoogle’s font collection is vastGoogle’s font collection is vast
Filters:
All categories
Thickness
Slant
Filters:
All categories
Thickness
Slant
Google’s font collection is vast
Google’s font collection is vast
Wid I th[Й] Poset a!! fi)!.Wid I th[Й] Poset a!! fi)!.Oxygen 1 Style by Vernon AdamsOxygen 1 Style by Vernon AdamsГ71 Quick-useI (gl Pop outГ71 Quick-useI (gl Pop outScript:; LatinScript:; LatinStyles:Styles:Google’s font collection is vastGoogle’s font collection is vast0 font families in your Collection *0 font families in your Collection *ChoosoChoosoAdd to Cdl-cltonAdd to Cdl-cltonReviewReview
Wid I th
[Й] Poset a!! fi)!.
Wid I th
[Й] Poset a!! fi)!.
Oxygen 1 Style by Vernon Adams
Oxygen 1 Style by Vernon Adams
Г71 Quick-useI (gl Pop out
Г71 Quick-useI (gl Pop out
Script:
; Latin
Script:
; Latin
Styles:
Styles:
Google’s font collection is vast
Google’s font collection is vast
0 font families in your Collection *
0 font families in your Collection *
Chooso
Chooso
Add to Cdl-clton
Add to Cdl-clton
Review
Review
Рис. 19.11. Включить веб-шрифты Google не составляет труда
Для выполнения трансформаций нужно воспользоваться свойством transform (у которого, к сожалению, должны быть соответствующие префиксы для использования в браузерах Mozilla, WebKit, Opera и Microsoft; по этому вопросу снова следует обратиться на веб-сайт http://caniuse.com).
К свойству transform можно применять множество значений, начиная со значения попе, которое переключает объект в состояние, не допускающее трансформаций: transform:none:
Свойство transform можно дополнить одной или несколькими из следующих разнообразных функций:
О matrix - трансформирует объект, применяя к нему матрицу значений;
О trans 1 ate - перемещает исходную точку элемента;
О scale —масштабирует объект;
О rotate вращает объект;
О skew —наклоняет объект.
Существуют также отдельные версии многих из этих функций, например LranslateX, sealeY и т. д.
<3^— I:
Браузеры на основе движка WebKit (например, Apple Safari, iOS, Google Chrome и Android) «VV л , поддерживают также ЗО-трансформации. Но я не буду их рассматривать, потому что Internet — ^* Explorer, Opera и браузеры на основе движка Mozilla пока их не поддерживают, но есть надежда, что в ближайшее время подтянутся и эти браузеры.
Так, например, чтобы повернуть элемент по часовой стрелке на 45°, можно применить к нему следующее объявление:
Iransform:rotate(45deg):
В то же время вы можете увеличить объект, как это делается с помощью следующего объявления, приводящего к увеличению его ширины в полтора, а высоты в два раза с последующим поворотом:
l.rarisform:scale(1.5. 2) rotate(45deg):
На рис. 19.12 показан объект до и после применения трансформации.
Рис. 19.12. Объект до и после трансформацииРис. 19.12. Объект до и после трансформации
Рис. 19.12. Объект до и после трансформации
Рис. 19.12. Объект до и после трансформации
Переходы
В последних версиях основных браузеров (включая Internet Explorer 10, не ниже) появилось новое динамическое свойство, называемое переходами. Переходы определяют эффект анимации, который нужно применить при трансформации элемента, и браузер автоматически позаботится за вас обо всех промежуточных кадрах.
Для настройки перехода можно предоставить четыре свойства:
:свойсг3во: времвремя . вре.время.:свойсг3во: времвремя . вре.время.trans i tion property transition-duration transition delay
:свойсг3во
: времвремя . вре.время.
:свойсг3во
: времвремя . вре.время.
I;ransi t ion t imi nq - fund ion: тип:
Свойства нужно предварять соответствующими префиксами браузеров, работающих на ижках Mozilla, WebKit, Opera и Microsoft.
л"
Свойства, применяемые к переходам
У переходов есть такие свойства, как height, border-col or и т. д. При указании свойс тв преследуется цель изменения CSS-свойства по имени transi ti on-property (здесь слово property, «свойства», используемое разными инструментами, имеет разные значения). Можно включить в объявление сразу несколько свойств, разделяя их запятыми:
transi ti on-property:wi dth. height, opacity;
Или, если вам нужно абсолютно все, относящееся к элементу, подвергаемому переходу (включая цвета), используется значение all:
transition-property:all:
Продолжительность перехода
Свойство transit ion-duration требует значения от нуля и более секунд. Следующее объявление задает завершение перехода через 1,25 секунды:
transition-duration:1.25s;
Задержка перехода
Если свойству transi tion-del ay дается значение более нуля секунд (то есть больше значения по умолчанию), происходит задержка между исходным отображением элемента и началом его перехода. Следующее объявление задает начало перехода после задержки в 0,1 секунды:
transi ti on-delay :0.1s;
Если свойству transition-delay дается значение меньше нуля секунд (иными словами, отрицательное значение), переход будет выполнен в момент изменения свойства, но проявится таким образом, будто оно началось с указанным смещением по времени, то есть на каком-то своем промежуточном цикле.
Задание скорости перехода
Свойству transi tion-timing-function требуется присвоить одно из следующих значений.
О ease — медленное начало, ускорение и медленное завершение.
О 1 i near — переход с постоянной скоростью.
О ease - i n — медленное начало, а затем быстрый переход до самого завершения.
О ease-out — быстрое начало, сохранение высокой скорости почти до завершения и медленное завершение.
О ease-in-out —медленное начало, быстрый переход, затем медленное завершение.
Использование любого из этих значений со словом ease обеспечивает исключи
, тельную плавность и естественность перехода в отличие от линейного (linear) перехода, который выглядит как-то более механическим. И если этих изменений вам не вполне достаточно, вы можете также создать свой собственный переход, используя функцию cubic-bezier.
Например, следующие объявления применялись для создания пяти предыдущих типов переходов, и они показывают, как просто можно создавать свои собственные переходы:
transition-timing-function:cubic-bezier(0.25. 0.1. 0.25.. 11
transition-timing-function:cubic-bezier(0, 0.1..]1
transition-timing-function:cubic-bezier(0.42. 0.1..11
transition-timing-function:cubic-bezier(0. 0.0.58.. 11
transition-timing-function:cubic-bezier(0.42. 0.0.58.. 11
Сокращенный синтаксис
Возможно, проще будет воспользоваться сокращенной версией этого свойства и включить все значения в одно объявление, такое как показано далее, которым задается переход всех свойств в линейном режиме за период 0,3 секунды, после начальной (необязательной) задержки в 0,2 секунды:
transiti on:al1 .3s linear .2s:
Это избавит вас от хлопот, связанных с вводом многих очень похожих друг на друга объявлений, особенно если вы поддерживаете префиксы всех основных браузеров.
В примере 19.4 показано, как можно сразу воспользоваться и переходом, и трансформацией. С помощью CSS создается квадратный, оранжевый элемент, с неким текстом внутри, а псевдокласс hover указывает на то, что при проходе над этим объектом указателя мыши он должен повернуться на 180° и изменить свой цвет с оранжевого на желтый (рис. 19.13).
Пример 19.4. Эффект перемещения, связанный с применением псевдокласса hover
<D0CTYPE html>
<html>
<head>
<ti tl ^Перемещение при проходе мыши </ti tl е>
<style>
fsquare {
position
absolute
top
50px:
left
50px:
width
lOOpx:
height
lOOpx:
padding
2px:
text-align
center:
border-width
lpx:
border-style
solid:
backgroundorange;
transitionall .8sease-in-out
-moz-transitionall .8sease-in-out
-webkit-transition all .8sease-in-out
-o-transitionall .8sease-in-out
-ms-transitionall .8sease-in-out : }
#square:hover { backgroundyellow:
-moz-transformrotate(180deg)
-webkit-transform rotate(180deg)
-o-transformrotate(180deg)
-ms-transformrotate(180deg)
transformrotate(180deg) : }
</style>
</head>
<body>
<div id='square’>
Square shape<br /> created using<br /> a simple div<br /> element with<br /> a lpx border
</div>
</body>
</html>
Рис. 19.13. Объект поворачивается и меняет цвет при прохождении над ним указателя мыши
Пример кода удовлетворяет требованиям всех разнообразных браузеров путем предоставления версий объявлений, характерных для тех или иных браузеров. Во всех самых последних браузерах (включая Internet Explorer 10 и выше) объект будет поворачиваться по часовой стрелке при прохождении над ним указателя мыши и в то же время будет медленно менять свой цвет с оранжевого на желтый.
CSS-переходы выполняются вполне продуманно, что выражается в том, что после прекращения перехода все плавно возвращается к своему исходному значению. Поэтому, если убрать указатель мыши до завершения перехода, он тут же пойдет в обратную сторону и начнет переход назад к своему исходному состоянию.
Теперь вы неплохо разбираетесь в том, что вам предлагает CSS и как этим можно воспользоваться для получения желаемых эффектов. В следующей главе мы сделаем еще один шаг в изучении CSS, рассмотрев его динамическое взаимодействие со свойствами объектной модели документа (DOM) посредством использования JavaScript.
Проверьте ваши знания
Чем занимаются операторы CSS3 $ и *?
Какое свойство используется для указания размера фонового изображения?
С помощью какого свойства можно указать радиус границы?
Как можно задать перетекание текста по нескольким колонкам?
Назовите четыре функции, с помощью которых можно указать CSS-цвета.
Как можно создать серую тень под каким-нибудь текстом с диагональным отступом вправо и вниз на 5 пикселов и с размытостью 3 пиксела?
Как можно показать многоточием, что текст усечен?
Как включить в состав своей веб-страницы веб-шрифты Google?
Какое CSS-объявление нужно использовать для поворота объекта на 90°?
Как указатььпереходдобъектаатакимиобразом, чтобы прииизмененииилюбогосиз 3 его свойств переход осуществлялся сразу в линейном режиме в течение половины секунды?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 19».
о П Доступ к CSS
ЖтХЗ из JavaScript
Вы уже поняли, что такое объектная модель документа —Document Object Model (DOM) и каскадные таблицы стилей —CSS, из этой главы вы узнаете, как к ним можно получать доступ непосредственно из кода JavaScript, что позволит вам создавать высокодинамичные и быстро реагирующие на действия пользователей веб-сайты.
Мы также рассмотрим использование прерываний, что позволит создавать анимацию или предоставлять любой код, который должен продолжать работу на веб-странице в фоновом режиме, например часы. Кроме того, я объясню, как в DOM добавляются новые элементы или удаляются существующие элементы, чтобы вам не приходилось заранее создавать элементы в HTML на тот случай, если коду JavaScript может чуть позже понадобиться получить к ним доступ.
Еще одно обращение к функции getElementByld
В главе 13 уже упоминалось, что символ $ часто используется как имя функции, чтобы облегчить доступ к функции getElementByld. Фактически основные среды программирования, такие как jQuery, применяют эту новую $-функцию и существенно расширили ее функциональные возможности.
Я тоже намереваюсь предоставить вам улучшенную версию этой функции, чтобы вы могли работать с элементами DOM и стилями CSS быстро и эффективно. Но чтобы избежать конфликтов со средами программирования, использующими символ $, я буду просто применять в качестве имени функции заглавную букву О, поскольку это первая буква слова object («объект»), а именно объект и будет возвращаться при вызове этой функции (тот самый объект, представленный идентификатором ID, переданным функции).
Функция О
Осно]ва функции 0 имеет следующIий вид:
tunc ( iori 0(nh.i •
return document.getElementById(obj)
}
Только лишь этот код уже на 22 символа сокращает количество набираемого текста при вызове функции, но я намерен немного расширить функцию, позволяя передавать ей либо ID, либо объект, как показано в полной версии функции, представленной в примере 20.1.
Пример 20.1. Функция 0
function 0(obj)
{
if (typeof obj == 'object') return obj
else return document.getElementByld(obj)
}
Если функции передается объект, она просто возвращает этот объект обратно. В противном случае она предполагает, что ей был передан ID, и возвращает объект, на который этот ID ссылается.
Но с какой стати мне захотелось добавить эту первую инструкцию, которая просто возвращает переданный ей объект?
Функция S
Ответ на данный вопрос станет ясным, когда вы посмотрите на вспомогательную функцию, названную S и показанную в примере 20.2, которую я вам предоставляю для упрощения доступа к стилевым свойствам (или CSS) объекта.
Пример 20.2. Функция S function S(obj) {
return 0(obj).style
}
Для этой функции имя S выбрано потому, что это первая буква слова Style, а функция выполняет задачу возвращения свойства стиля (или подчиненного объекта) того элемента, на который она ссылается. Поскольку встроенная функция 0 принимает либо ID, либо объект, вы можете передавать функции S как ID, так и объект.
Рассмотрим, что получится, когда мы возьмем <di v>-:*neMeiiT с ID myobj и установим для цвета его текста значение green (зеленый):
<div id='myobj *>Some text</div>
<script>
OCmyobj').style.color = 'green'
</script>
Предыдущий код справится с этой задачей, но значительно проще будет вызвать новую функцию S:
S('myobj') .color = 'green'
Теперь рассмотрим случай, при котором объект, возвращенный в результате вызова функции 0, сохранен, к примеру, в объекте по имени fred:
fred = 0(*myobj')
Благодаря тому способу, который используется в работе функции S, мы можем для изменения цвета на зеленый вызвать и этот объект:
S(fred).color = 'green'
Это означает, что при желании получить доступ к объекту непосредственно или через его ID, вы можете сделать это, передавая его либо функции 0, либо функции S, в зависимости от того, что вам нужно. Нужно лишь помнить, что при передаче объекта (а не ID) ни в коем случае не следует брать его имя в кавычки.
Функция С
Вам уже предоставлены две простые функции, упрощающие доступ к любому элементу на веб-странице и любому свойству стиля элемента. Но иногда вам понадобится одновременный доступ более чем к одному элементу. Это можно сделать путем присваивания имени класса CSS каждому такому элементу, как показано в следующем примере, где для каждого элемента используется класс myclass:
<div class='myclass’>Coflep>KHMoe di v-контейнера </a>
<p class='myclass'>Coflep>KHMoe абзаца</р>
Если нужен доступ ко всем элементам страницы, использующим конкретный класс, можно воспользоваться функцией С (чье имя происходит от первой буквы в слове class), показанной в примере 20.3. Она вернет массив, состоящий из всех объектов, которые соответствуют предоставленному имени класса.
Пример 20.3. Функция С
function C(name) {
var elements = document.getElementsByTagName()
var objects = []
for (var i = 0 : i < elements.length : ++i)
if (elements[i].className == name) objects.push(elements[i])
return objects
}
Разберем код no частям. В аргументе name содержится имя класса, по которому мы пытаемся извлечь объекты. Затем внутри функции создается новый объект по имени el ements, содержащий все элементы документа, возвращенные путем вызова функции getElementsByTagName с аргументом который означает «нужно найти все элементы»:
var elements = document.getElementsByTagName()
Затем создается новый массив по имени objects, куда будут помещаться все найденные объекты, соответствующие условию поиска:
var objects = []
Затем цикл for осуществляет перебор всех элементов, имеющихся в объекте elements, используя в качестве индекса переменную i:
for (var i = 0 : i < elements.length ; ++i)
При каждом проходе цикла объект помещается в массив objects при условии, что значение свойства элемента className совпадает со строковым значением, переданным в аргументе name:
if (elements[i] .className == name) objects.push(elements[i])
И наконец, когда цикл завершится, массив objects будет содержать все элементы в документе, который используют имя класса, являющееся значением переменной name, поэтому он возвращается функцией:
return objects
Использование функции С
Для использования эту функцию следует просто вызвать, как показано далее, сохраняя возвращенный массив, чтобы иметь возможность получить доступ отдельно к каждому нужному элементу или (что чаще всего и бывает) ко всем элементам посредством использования цикла:
myarray = С('myclass')
Теперь можете делать с возвращенными объектами все, что нужно, например установить для их свойства textDecoration значение подчеркивания — ' under 1 ine ':
for (i = 0 : i < myarray.length ; ++i)
S(myarray[i]).textDecoration = ’underline'
Этот код осуществляет последовательный перебор объектов в myarray[] и использует функцию S для ссылки на свойство стиля каждого объекта, задавая для свойства textDecorati on значение ' underl i ne ’.
Включение функций
Функции 0 и S используются во всей оставшейся части главы, поскольку они делают код короче и понятнее. Поэтому я сохранил их в файле OSC . js (наряду с функцией С, поскольку я полагаю, что она принесет вам большую пользу) в папке Chapter 20 в сопутствующем архиве примеров, который вы можете загрузить с веб-сайта http://lpmj.net.
Они могут быть включены в веб-страницу с помощью следующей инструкции, которую предпочтительнее поместить в блок <head> где-нибудь перед .любым сценарием, работа которого зависит от вызова этих функций:
<script src='0SC.js'></script>
Содержимое файла OSC.js показано в примере 20.4.
Пример 20.4. Файл OSC.js
function 0(obj)
{
if (typeof obj == 'object') return obj
else return document.getElementById(obj)
}
function S(obj)
{
return 0(obj).style
function C(name)
{
var elements = document.getElementsByTagName('*')
var objects = []
for (var i = 0 ; i < elements.length ; ++i)
if (elements[i].className == name) objects.push(elements[i])
return objects
}
Обращение к свойствам CSS из JavaScript
Свойство textDecorati on, использовавшееся в ранее показанном примере, представляет свойство CSS, имя которого в обычном виде содержит дефис: text-decorat ion. Но поскольку в JavaScript дефис зарезервирован для применения в качестве математического оператора, при доступе к свойству CSS, в имени которого используется дефис, этот дефис нужно опустить и перевести символ, следовавший непосредственно за ним, в верхний регистр.
Еще одним примером может послужить свойство font - si ze, на которое вJavaScript при помещении после оператора точки ссылаются как на fontSize:
myobject.fontSize = ’16pt'
Вместо этого можно предоставить более развернутый код и воспользоваться функцией setAttribute, которая поддерживает (и фактически требует) стандартное имя свойства CSS:
myobject.setAttribute(’font-size'. ' 16pt’)
Некоторые версии Microsoft Internet Explorer в определенных ситуациях слишком разборчивы в применении JavaScript-стиля имен, принадлежащих свойствам CSS. Имеется в виду применение к ним специальных версий правил, в которых используются характерные для браузера префиксы -ms-.
Некоторые общие свойства
С помощью JavaScript вы можете изменить любое свойство любого элемента, имеющегося в веб-документе, примерно так же, как это делается с помощью CSS. Я уже показывал вам, как получить доступ к свойствам CSS, используя либо краткую форму JavaScript, либо функцию setAttribute (чтобы применить абсолютно такие же имена свойств, как и в CSS). Поэтому я не стану обременять вас детализацией всех этих сотен свойств. Вместо этого я покажу, как получить доступ к некоторым свойствам CSS, чтобы дать обзорное представление о возможностях их применения.
Сначала рассмотрим изменение нескольких свойств CSS из JavaScript, используя код примера 20.5, который в первую очередь загружает в себя три ранее упомянутые функции, затем создает <сИу>-элемент и, наконец, запускает инструкции JavaScript, находящиеся внутри блока <script> кода HTML, с целью изменения различных атрибутов элемента <div> (рис. 20.1).
Пример 20.5. Обращение к свойствам CSS из JavaScript
<html>
<head>
<title>00paineHne к свойствам CSS</title>
<script src='0SC.js,></script>
</head>
<body>
<div id=’object'>Div-o6beKT</div>
<script>
S('object').border=’solidj lpx red’
S(('object').width='lOOpx'
S('object') .height=’lOOpx'
S('object') .background='#eee'
S('object').color='blue'
S('object') .fontSize=' 15pt'
S('object') .fontFamily = 'Helvetica' S('object') .fontStyle = 'italic' </script>
</body>
</html>
От такого изменения свойств нет никакой практической пользы, поскольку можно также легко включить код CSS непосредственно в атрибуты элемента, но скоро мы будем изменять свойства в ответ на действия пользователя, вот тогда и проявится настоящая эффективность сочетания JavaScript и CSS.
Другие свойства
JavaScript также открывает доступ к очень широкому диапазону других свойств, таких как ширина и высота окна браузера и любых появляющихся или присутствующих в браузере окон или фреймов, и к полезной информации, такой как родительское окно (если таковое имеется) и история URL-адресов, по которым осуществлялись визиты в текущем сеансе.
Все эти свойства доступны из объекта window через оператор точка (например window.name). В табл. 20.1 перечислены все эти свойства с описаниями.
Таблица 20.1. Общие свойства объекта window
Свойство
Описание
closed
Возвращает булево значение, показывающее, было ли закрыто окно
defaultStatus
Устанавливает или возвращает исходный текст панели состояния окна
document
Возвращает объект документа для окна
frames
Возвращает массив, состоящий из всех фреймов и i-фреймов окна
history
Возвращает для окна объект истории
innerHeight
Устанавливает или возвращает внутреннюю высоту области содержимого окна
innerWidth
Устанавливает или возвращает внутреннюю ширину области содержимого окна
length
Возвращает количество фреймов и i-фреймов окна
location
Возвращает местоположение объекта в окне
name
Устанавливает или возвращает имя окна
navigator
Возвращает для окна объект-навигатор
opener
Возвращает ссылку на то окно, из которого было создано данное окно
outerHeight
Устанавливает или возвращает внешнюю высоту окна, включая панель инструментов и полосу прокрутки
outerWidth
Устанавливает или возвращает внешнюю ширину окна, включая панель инструментов и полосу прокрутки
pageXOffset
Возвращает количество пикселов, на которое был горизонтально прокручен документ от левого края окна
pageYOffset
Возвращает количество пикселов, на которое был вертикально прокручен документ от верхнего края окна
parent
Возвращает для окна объект родительского окна
screen
Возвращает для окна объект экрана
Продолжение &
Таблица 20.1 (продолжение)
Свойство
Описание
screenLeft
Возвращает координату x окна относительно экрана во всех последних браузерах, кроме Mozilla Firefox (для которого нужно использовать screenX)
screenTop
Возвращает координату у окна относительно экрана во всех последних браузерах, кроме Mozilla Firefox (для которого нужно применять screenY)
screenX
Возвращает координату х окна относительно экрана во всех последних браузерах, кроме Opera, который возвращает неправильное значение;
поддерживается в версиях Internet Explorer, предшествующих 9-й версии
screenY
Возвращает координату у окна относительно экрана во всех последних браузерах, кроме Opera, который возвращает неправильное значение;
поддерживается в версиях Internet Explorer, предшествующих 9-й версии
self
Возвращает the current window
status
Устанавливает или возвращает текст на панели состояния окна
top
Возвращает верхнее окно браузера
В отношении некоторых из этих свойств следует отмстить такие моменты.
о Свойства defaul tStatus и status могут быть установлены, только если пользователи изменили настройки своих браузеров и разрешили их применение (что маловероятно).
о Содержимое объекта hi story не может быть прочитано (поэтому нельзя посмотреть, какие адреса посещались вашими визитерами), но этот объект поддерживает свойство length, чтобы определить длину истории, а также методы back, forward и go для переходов на указанные страницы в истории.
о Когда нужно узнать, какое пространство доступно в текущем окне веб-браузера, следует просто прочитать значения свойств wi ndow. i nnerHei ght и wi ndow. i nnerWi dth. Я часто использую эти значения для размещения появляющихся в окне браузера диалоговых окон оповещения и подтверждения по центру.
о Объект screen поддерживает свойства, доступные только для чтения avai 1 Height, avai 1 Wi dth, col orDepth, hei ght, pi xel Depth и wi dth, и поэтому отлично подходит для извлечения информации о дисплее пользователя.
Многие из этих свойств могут быть просто бесценными при позиционировании на мобиль- « ных телефонах и планшетных устройствах, поскольку они дадут точную информацию об экранном пространстве, с которым придется работать, о типе используемого браузера ит. д.
Этого объема информации вполне достаточно для начала работы и для получения представления о многих новых и интересных приемах работы с JavaScript. Разумеется, существует намного больше доступных свойств и методов, которые могли бы быть рассмотрены в данной главе, но теперь, зная о том, как обращаться к свойствам и использовать их, вам нужен лишь информационный ресурс, на котором все они перечислены. Я рекомендую для начала обратиться к веб-сайту http://tinyurl.com/domproperties.
Встроенный JavaScript
Использование тегов <scri pt> не является единственным способом выполнения инструкций JavaScript. Получить доступ к JavaScript можно также и из тегов HTML, что и делается для повышения динамической интерактивности.
Например, для быстрого добавления эффекта при прохождении указателя мыши над объектом можно воспользоваться таким же кодом, который показан в теге <img /> в примере 20.6. Там изначально отображается картинка с яблоком, которая при прохождении над ней указателя мыши заменяется картинкой с апельсином (а при выходе указателя за пределы картинки возвращается картинка с яблоком).
Пример 20.6. Использование встроенного JavaScript
<html>
<head>
<ttitlе>Встроенный JavaScri pt</title>
</head>
<body>
<img src='apple.png'
onmouseover="thi s .src='orange.png' M onmouseout="this.src='apple.png’M/>
</body>
</html>
Ключевое слово this
В предыдущем примере продемонстрировано применение ключевого слова this. Оно заставляет JavaScript работать с названным объектом, а именно с тегом <i mg />. Результат можно увидеть на рис. 20.2, где указатель мыши только что прошел над картинкой с яблоком.
Рис. 20.2. Пример встроенного кода JavaScript, обрабатывающего прохождение указателя мыши над объектом
Когда ключевое слово this находится во встроенном вызове JavaScript, оно представляет вызываемый объект. А при использовании в методах класса оно представляет объект, к которому применяется метод.
Привязка событий к объектам в сценарии
Предыдущий код является эквивалентом предоставления тегу <1 mg /> идентификатора с последующей привязкой действий к событиям мыши этого тега, как в примере 20.7.
Пример 20.7. Невстроенный JavaScript
<html>
<head>
<titlе>Невстроенный JavaScript</t1t1e>
<script src='0SC.js'x/scr1pt>
</head>
<body>
<1mg id='objecf src='apple.png’ />
<script>
0('object').onmouseover = functionO { this.src = 'orange.png' }
0( 'object') .onmouseout = functionO { this.src = 'apple.png’ } </script>
</body>
</html>
Этот код применяет ID объекта к тегу <i mg /> в блоке HTML, а затем продолжает работать с ним отдельно в блоке JavaScript, прикрепив к каждому событию безымянную функцию.
Прикрепление к другим событиям
Какой бы JavaScript ни использовался, встроенный или отдельный, существуют события, к которым вы можете прикрепить действия. И активировать тем самым множество дополнительных функций, которые можете предоставить своим пользователям. В табл. 20.2 перечислены эти события и указаны условия их наступления.
Таблица 20.2. События и условия их наступления
Событие
Условие его наступления
опа bort
Когда загрузка изображения останавливается до ее завершения
onblur
Когда элемент теряет фокус
onchange
Когда изменяется любая часть формы
onclick
Когда происходит щелчок кнопкой мыши на объекте
ondblclick
Когда происходит двойной щелчок кнопкой мыши на объекте
onerror
Когда обнаруживается ошибка JavaScript
onfocus
Когда элемент получает фокус
onkeydown
Когда нажата клавиша (включая Shift, Alt, Ctrl и Esc)
Событие
Условие его наступления
onkeypress
Когда нажата клавиша (исключая Shift, Alt, Ctrl и Esc)
onkeyup
Когда клавиша отпущена
onload
Когда объект загрузился
on mousedown
Когда над элементом нажата кнопка мыши
onmousemove
Когда указатель мыши проходит над элементом
onmouseout
Когда указатель мыши покидает элемент
onmouseover
Когда указатель мыши заходит на элемент со стороны
onmouseup
Когда отпускается кнопка мыши
onsubmit
Когда отправляется форма
onreset
Когда сбрасываются данные формы
onresize
Когда изменяются размеры окна браузера
onscroll
Когда документ прокручивается
onselect
Когда выделяется какой-нибудь текст
onunload
Когда удаляется документ
События нужно прикреплять только к тем объектам, для которых в них имеется смысл. Например, объект, не являющийся формой, не будет реагировать на событие onsubmit.
Добавление новых элементов
Работая с JavaScript, вы можете манипулировать не только элементами и объектами, которые были предоставлены документу его кодом HTML. Вы можете создавать объекты по своему желанию, вставляя их в DOM.
Предположим, к примеру, что вам нужен новый элемент <div>. Способ добавления его к веб-странице показан в примере 20.8.
Пример 20.8. Вставка элемента в D0M
<html>
<head>
<Ш1е>Добавление элементов<Аи1е>
<script src='0SC.js'></script>
</head>
<body>
В этом документе содержится только этот текст.<Ьг /><Ьг />
<script>
alert( 'Для добавления элемента щелкните на кнопке 0К')
newdiv = document.createElement(('di v'))•
newdiv.id = ’NewDiv’
document.body.appendChi1d(newdi v)
S(newdiv).border = ’solid lpx red’
S(newdiv).width = ’lOOpx’
S(newdiv).height = ’lOOpx’
newdiv.innerHTML = "Это новый объект, вставленный в D0M"
tmp= newdiv.offsetTop
а 1егК’Для удаления элемента щелкните на кнопке ОК ’ )
newdi v.parentNode.removeChi1d(newdi v)
</script>
</body>
</html>
Сначала новый элемент создается с помощью функции createElement, затем вызывается функция appendChi Id, и элемент вставляется в DOM. После этого элементу присваиваются различные свойства, включая текст для его свойства innerHTML (внутреннего HTML). А затем, чтобы обеспечить немедленное отображение нового элемента на экране, значение его свойства offsetTop считывается во временную переменную tmp. Это заставляет DOM обновиться и вывести элемент на экран в любом браузере, который в противном случае выдержал бы паузу, прежде чем это сделать. В частности, это касается Internet Explorer. Результат показан на рис. 20.3.
Этот новый элемент точно такой же, как если бы он был включен в исходный HTML, и он открывает доступ к аналогичным свойствам и методам.
Иногда я использую технологию создания новых элементов, когда хочу создать окна, появляющиеся в окне браузера, потому что она не зависит от наличия запасных ^м>-элемен- тов в DOM.
Удаление элементов
Вы можете также удалить элементы из DOM, включая и те, которые не были вставлены с помощью кода JavaScript. Это даже проще, чем добавление элемента. Если предположить, что удаляется объект el ement, то это делается следующим образом:
рIement.pa гепtNode.removeChiId(e 1ement)
Этот код обращается к объекту parentNode элемента, поэтому он может удалить элемент из этого узла. Затем он вызывает метод этого объекта removeChi Id, передавая ему удаляемый объект. Но чтобы обеспечить немедленное обновление DOM на всех браузерах, возможно, будет предпочтительнее заменить предыдущую инструкцию следующим кодом:
pnode = element.parentNode
pnode.removeChi1d(element) tmp = pnode.offsetTop
Здесь первая инструкция помещает копию element.parentNode (родительского элемента объекта) в переменную pnode, которая (после того как дочерний элемент удаляется во второй строке) позволяет прочитать значение ее свойства offsetTop во временную переменную tmp, гарантируя тем самым полное обновление DOM.
Альтернативы добавлению и удалению элементов
Вставка элемента предназначена для добавления к веб-странице абсолютно нового объекта. Но если вы намерены только скрывать и показывать объекты в соответствии с наступлением события onmouseover или какого-нибудь другого события, не забудьте, что есть пара свойств CSS, которые могут использоваться для этой цели без принятия таких радикальных мер, как создание и удаление элементов DO М.
Например, когда нужно сделать элемент невидимым, но оставить его на месте (оставляя на своих местах все окружающие его элементы), можно просто установить для свойства vi sibi 1 ity объекта значение ' hidden':
myobje I ct.visibility = 'hidden'
А для повторного отображения объекта можно воспользоваться следующим кодом:
myobject.visibi1ity = 'visible1
Можно также свернуть элемент, чтобы он занимал нулевую ширину и высоту (и чтобы все окружающие его объекты заняли освободившееся пространство):
myobject.display = 'none'
Для последующего восстановления элемента в его исходных размерах можно воспользоваться следующим кодом:
myobject.display = 'block'
И конечно же, в вашем распоряжении всегда есть свойство innerHTML, с помощью которого можно изменить код HTML, примененный к элементу. Например:
mylement.. innerHTML = ‘'Ь>3амена HTML</b>'
Можно также воспользоваться упомянутой ранее функцией 0:
0 ('someid’ ) . innerHTMl - 'Новое содержимое'
Можно также заставить элемент показаться исчезнувшим:
Of'someid').innerHTMl -
Не забывайте обо всех других полезных свойствах CSS, к которым можно обратиться из JavaScript. Например, для переключения объекта из видимого в невидимое состояние и обратно можно воспользоваться свойством непрозрачности, а для изменения размеров объекта можно изменить значения свойств width и height. И конечно же, применяя для свойства position значения 'absolute', 'static' или 'relative', вы можете даже поместить объект в любое место окна браузера (или снаружи).
Использование прерываний
JavaScript предоставляет доступ к прерываниям, методу, с помощью которого можно попросить браузер вызвать ваш код после определенного периода времени или даже продолжать вызовы через указанные интервалы времени. Это дает вам средства обработки фоновых задач, таких как обмен данными с помощью Ajax или даже такие средства, как анимация веб-элементов.
Существует два типа прерываний, setTimeout и set Interval, сопровождающихся функциями clearTimeout и clearlnterval для их выключения.
Использование функции setTimeout
При вызове функции setTimeout передается код JavaScript или имя функции и значение в миллисекундах, отображающее продолжительность задержки запуска кода на выполнение:
setTimeout(dothis. ЬООО)
Ваша функция dothi s может иметь следующий вид:
function dothis()
{
а1егК'Это ваш будильник!'):
}
«г
■ ■
Как ни удивительно, вы не можете просто указать alert() (с круглыми скобками) в качестве « функции, вызываемой setTimeout, потому что функция будет тут же выполнена. Передавать мя функции, чтобы код был выполнен только по истечении указанного времени, можно только без круглых скобок, служащий для указания аргументов (например, alert).
Передача строки
Если исполняемой функции нужно передать аргумент, то функции set Timeout можно также передать строковое значение, которое не будет выполняться, пока не наступит нужное время. Например:
setTimeout("alert('Hello!')". 5000)
Фактически если после каждой инструкции ставить точку с запятой, то можно передать столько строк кода JavaScript, сколько нужно:
setTimeout("document.write('Start mtg'); aler t t('He 11о!')I". S000)
Повторение тайм-аутов
Для предоставления повторяющихся прерываний, создаваемых функцией setTimeout, некоторые программисты используют технологию вызова функции setTimeout из кода, вызываемого этой же функцией, как в следующем примере, который инициирует бесконечный цикл вывода окон предупреждений:
setTi meout(doth i s . 5000)
function dothisO
{
’ setTimeout(dothis. 5000) a I ert('Я вас раздражаю!')
}
Теперь окно предупреждения будет появляться каждые пять секунд.
Отмена тайм-аута
После установки тайм-аута вы можете отменить его, если предварительно сохранили значение, возвращенное при начальном вызове функции setTimeout:
handle = setTimeout(dothis. 5000)
Теперь, когда у вас есть это значение в переменной handle, вы можете отменить прерывание в любой момент, вплоть до истечения назначенного срока:
clearTimeout(handle)
В результате этого прерывание полностью забывается и код, назначенный ему для выполнения, никогда не выполняется.
Использование функции setlnterval
Самый простой способ установки регулярных прерываний заключается в использовании функции setlnterval. Она работает точно так же, как и описанная выше, за исключением того, что, проявив себя после интервала, указанного вами в миллисекундах, она сделает это снова, после того как этот же интервал снова пройдет, и так далее до бесконечности, пока вы ее не остановите.
В примере 20.9 эта функция используется для вывода в браузере простых часов, показанных на рис. 20.4.
Пример 20.9. Часы, созданные с помощью прерываний
<html>
<head>
<Ш1е>Использование set Interval </ti 11e>
<script src='0SC.js’></script>
</head>
<body>
Текущее время: <span id= *time’>00:00:00</span><br />
' sc:ript>
set Interval("showtime(0('time'))” . 1000)
function showtime(object)
{
var date - new Date()
object. innerHTML = date.toTimeString(),substr(0.8)
1
</script*
<-/body>
</html>
Рис. 20.4. Поддержка показаний правильного времени с помощью прерываний
При каждом вызове функции ShowTime она присваивает объекту date текущее время и дату с помощью вызова функции Date:
var date = new DateO
Затем свойству innerHTML объекта, переданного функции showtime (то есть object), устанавливается значение текущего времени в часах, минутах и секундах, как определено вызовом функции toTimeString. В результате возвращается строка «09:57:17 UTC+0530», которая затем усекается до первых восьми символов с помощью вызова функции substr:
object.innerHTML = date.toT imeStri ng().substr(0.8)
Использование функции
Чтобы воспользоваться этой функцией, сначала нужно создать объект, чье свойство innerHTML будет применено для отображения времени, как в следующем коде HTML:
Текущее время: <span id=,time’>00:00:00</span>
Затем в блоке кода <script> вызов помещается в функцию setlnterval:
setlnterval("showtime(0('time')Г . 1000)
Этот вызов передает функции setlnterval строку, содержащую следующую инструкцию, настроенную на выполнение один раз в секунду (каждую 1000 миллисекунд):
showtime(0('time'))
В том редком случае, когда кто-нибудь отключил в своем браузере JavaScript (что некоторые делают из соображений безопасности), ваш JavaScript не запустится и пользователь увидит исходное значение 00:00:00.
Отмена интервала
Чтобы остановить повторяющийся интервал, при первой установке интервала путем вызова функции setlnterval вы должны пометить для себя в переменной handl е дескриптор этого интервала:
handle = setlnterval("showti me(0('time'))". 1000)
Теперь можно остановить часы в любое время, сделав следующий вызов:
cl earInterval(handle)
Можно также настроить таймер на остановку через определенный период времени:
setTimeout("clearlnterval(handle)". 10000)
Эта инструкция выдаст прерывание через 10 секунд (10 ООО миллисекунд), которое очистит повторяющиеся интервалы.
Использование прерываний для анимации
Путем сочетания нескольких свойств CSS с повторяющимся прерыванием можно создавать всевозможные анимации и эффекты.
Например, код в примере 20.10 перемещает прямоугольник по верхней части окна браузера, все время увеличивая его в размерах, как показано на рис. 20.5. Когда значение переменной LEFT сбрасывается в 0, анимация начинается снова.
Пример 20.10. Простая анимация
<html>
<head>
<ttitlе>Простая анимация</Ш1е>
<script src=’0SC.js'></script>
<style>
#box {
position :absolute:
background:orange:
border :lpx solid red: }
</style>
</head>
<body>
<div id='box’></div>
<script>
SIZE = LEFT = 0
setlnterval(animate. 30)
function animateO
{
SIZE += 10
LEFT += 3
if (SIZE == 200) SIZE = 0
if (LEFT == 600) LEFT = 0
'px' 'px' ’px’'px' 'px' ’px’S('box').width =sSIZE + S('box').height =sSIZE + S('box’).left =L LEFT +
'px' 'px' ’px’
'px' 'px' ’px’
</script>
</body>
</html>
Рис. 20.5. Объект плавно движется слева, одновременно меняя свой размерРис. 20.5. Объект плавно движется слева, одновременно меняя свой размер
Рис. 20.5. Объект плавно движется слева, одновременно меняя свой размер
Рис. 20.5. Объект плавно движется слева, одновременно меняя свой размер
В блоке <head> документа объекту box устанавливается цвет фона ' orange' (оранжевый) со значением его границы (border) 'lpx solid redа его свойству позиционирования position задается значение absolute, чтобы ему разрешалось перемещаться по окну браузера.
Затем в функции animate происходит постоянное обновление глобальных переменных SIZE и LEFT, а затем их значения применяются к атрибутам стиля width, height и left объекта box (с добавлением после каждого значения строки 'рх' для указания, что значение в пикселах), таким образом анимируя объект с частотой один раз каждые 30 миллисекунд. Тем самым задается скорость 33,33 кадра в секунду (1000 / 30 миллисекунд).
Вот и завершается введение во все темы, рассматриваемые в данной книге, и теперь вы находитесь на пути превращения в опытного веб-разработчика. Но перед тем, как завершить книгу, в последней главе я хочу собрать все, что здесь рассматривалось, вместе, в единый проект, чтобы вы на практике увидели, как все технологии совмещаются друг с другом.
Проверьте ваши знания
Для чего предназначены функции 0, S и С?
Назовите два способа изменения CSS-атрибута объекта.
Какие свойства предоставляют доступную в окне браузера ширину и высоту?
Как можно задать какие-нибудь действия при прохождении указателя мыши над объектом, а затем при выходе за границы объекта?
Какая функция JavaScript создает новые элементы и какая функция добавляет ихкБОМ?
Как сделать элемент а) невидимым и б) сжатым до нулевых размеров?
Какая функция задает одиночное событие в будущем времени?
Какая функция устанавливает повторяющиеся события через указанный интервал времени?
Как можно освободить элемент от его места на веб-странице, чтобы он мог перемещаться?
Какая должна быть установлена задержка между событиями^(в миллисекундах):) для получения скорости анимации 50 кадров в секунду?
Ответы на эти вопросы можно найти в приложении А, в разделе «Ответы на вопросы главы 20».
<^
4| Объединение ьХ технологий
В завершение книги я хочу привести вам реальный пример использования рассмотренных технологий, с которым вы можете досконально разобраться. В действительности это несколько примеров, объединенных в простом проекте социальной сети, имеющей все атрибуты, которые ожидают обнаружить на подобном сайте.
В разных файлах проекта представлены примеры создания таблиц MySQL и доступа к базе данных, таблиц стилей CSS, включения других файлов, управления сессией, доступа к DOM, Ajax-вызовов, обработки событий и ошибок, загрузки файлов на сервер, работы с изображениями и решения многих других задач.
Каждый файл, приводимый в качестве примера, является завершенной и самодостаточной программой, способной работать совместно с остальными файлами с целью построения полностью работоспособного сайта социальной сети. К тому же, включая таблицу стилей, вы можете полностью изменить внешний вид проекта. За счет своего небольшого объема и несложной конструкции конечный продукт особенно полезен на мобильных платформах, таких как смартфоны или планшетники, но он также хорошо будет работать и на полноразмерных настольных компьютерах.
Можно взять любой представляющийся полезным фрагмент кода и дополнить его в соответствии с поставленными задачами. Возможно, у вас даже появится желание создать на основе этих файлов собственную социальную сеть.
Проектирование сайта социальной сети
Перед написанием кода я определяю для себя важные составляющие подобного сайта, среди которых:
О процесс регистрации;
О форма для входа на сайт;
О средство для завершения работы с сайтом;
О управление сессией;
О пользовательские профили с загруженными миниатюрными изображениями;
О каталог участников сети;
О добавление участников в список друзей;
О открытый и закрытый обмен сообщениями между участниками;
О способ стилевого оформления проекта.
Я решил назвать проект «Сообщество Робина» — Robin’s Nest. Если выберете другое имя, то для внесения изменений потребуется модифицировать лишь одну строку кода (в functions. php).
Информация на веб-сайте
Все примеры, приводимые в данной главе, можно найти на прилагаемом к книге веб-сайте, который размещен по адресу http://lpmj.net. Можно также загрузить примеры с этого сайта на свой компьютер, щелкнув на ссылке Download 2nd Ed. Examples (Загрузка примеров 2-го издания). В результате будет загружен архивный файл 2nd_edi t i on exampl es. zi p, содержимое которого можно извлечь и поместить в удобное для вас место.
С учетом того, что данная глава представляет особый интерес, внутри ZIP -файла есть папка robinsnest, в которой все следующие примеры сохранены с использованием тех имен, которые требуются этому учебному приложению. Поэтому вы можете просто скопировать все эти примеры в свою рабочую папку web, чтобы увидеть их в действии.
Файл functions.php
Перейдем непосредственно к проекту и начнем с примера 21.1, funct i ons. php, представляющего собой включаемый файл, в который входят основные функции. Но в этом файле содержатся не только функции. Я добавил в него сведения, необходимые для входа в базу данных, чтобы не использовать для этой цели лишний файл.
В первых шести строках кода определяются хост, имя базы данных, имя пользователя и пароль, используемый для входа в базу данных. Неважно, как вы назовете базу данных, главное, чтобы она уже существовала (создание новой базы данных рассматривалось в главе 8). Нужно также обеспечить присвоение переменным Sdbuser и $dbpass правильных значений имени пользователя и пароля для входа в MySQL. Если они имеют такие значения, то выполнение следующих двух строк кода приведет к подключению к MySQL и выбору базы данных. Последняя из начальных инструкций устанавливает имя сайта социальной сети, присваивая значение Robi n ’ s Nest переменной Sappname. Именно здесь при желании можно заменить это имя другим.
Функции
В проекте используются пять основных функций:
О createTabl е — проверяет факт существования таблицы и создает отсутствующую таблицу;
О queryMysql —выдает запрос к MySQL, а в случае сбоя выводит сообщение об ошибке;
О destroySession - уничтожает PHP-сессию и очищает машину от ее данных для завершения сеанса работы пользователей;
О sanitizeString — удаляет потенциально вредный код или теги из информации, введенной пользователем;
О showProfi 1е — отображает миниатюрные изображения пользователей и их записи About me (Обо мне), если таковые имеются.
Работа всех этих функций должна быть вам понятна. За исключением, может быть, функции showProfi "Iе, которая осуществляет поиск изображения по имени user.jpg (где user — это пользовательское имя текущего пользователя) и после успешного поиска выводит это изображение на экран. Она также отображает любой текст About me (Обо мне), который пользователь мог сохранить.
Все нуждающиеся в этом функции снабжены кодом обработки ошибок, который позволяет перехватывать любую опечатку или другие допущенные ошибки ввода и сгенерировать сообщение об ошибке. Но если какая-нибудь из этих функций используется на рабочем сервере, то вам, скорее всего, захочется предоставить для этой цели собственные обработчики ошибок, чтобы сделать код более дружелюбным по отношению к пользователю.
Наберите код примера 21.1 и сохраните его в файле functi ons. php (или загрузите файл с прилагаемого к книге веб-сайта), после чего вы будете готовы перейти к изучению следующего раздела.
Пример 21.1. functions.php
<?php // functions.php
Sdbhost
='localhost';
//
Эта строка вряд ли нуждается в изменении
$dbname
='anexistingdb';
//
А значения этих переменных
//
поменяйте...
Sdbuser
='robinsnest';
//
... на те, что соответствуют
Sdbpass
='apassword';
//
... вашим настройкам
Sappname
="Robin's Nest";
/ /
... и пpeдпoчтениям
mysql_connect(Sdbhost, Sdbuser, Sdbpass) or die(mysql_error()); mysql_select_db(Sdbname) or die(mysql_error());
function createTable(Sname. Squery)
{
queryMysql("CREATE TABLE IF NOT EXISTS Sname(Squery)");
echo "Таблица ’Sname' создана или уже существовала<Ьг />":
function queryMysql(Squery)
{
Sresult = mysql_query($query) or die(mysql_error());
return Sresult;
function destroySessionO {
$_SESSION=a ггау():
if (session_id() != "" || isset($ _COOKIE[session_name()])) setcookie(session1 name(). ” . timeO-2592000. '/'):
session_destroy():
function sanitizeString(Svar)
{
$var = strip_tags($var);
$var = htmlentities($var):
$var = stripslashes(Svar):
return mysql_real_escape_string($var);
function showProfile($user)
{
if (file_exists("Suser.jpg"))
echo "<img src='$user.jpg' a 1ign='1 eft' />";
Sresult = queryMysql("SELECT * FROM profiles WHERE user='Suser'"):
if (mysql_num_rows($result))
{
$row = mysql_ fetch_row($result);
echo stripslashes($row[l]) "<br clear=left /><br />";
}
}
?>
Файл header.php
Чтобы сохранить единство стиля, каждая страница проекта должна иметь доступ к одному и тому же набору функций. Поэтому я поместил соответствующие настройки в файл header.php, показанный в примере 21.2. Этот файл включается практически во все остальные файлы, и он, в свою очередь, включает в себя файл functions.php. Поэтому в каждом файле необходимо включение только одного файла.
Код файла header. php начинается с вызова функции sessi on start. Из материалов главы 12 следует, что эта функция настраивает сессию, которая будет запоминать конкретные значения, необходимые для использования в различных РНР-файлах. Затем устанавливается тип документа и из файла osc. js берутся JavaScript-функции О, S и С, рассмотренные в главе 20.
После запуска сессии программа проверяет, присвоено ли значение элементу массива сессии с индексом ' user'. Если значение присвоено, значит, пользователь вошел на сайт и значение переменой Sloggedin установлено как TRUE.
Благодаря использованию значения переменной $1 oggedi п и блока i f отображается один из двух наборов меню. Набор для не вошедшего на сайт пользователя предлагает выбор только из главной страницы — Ноте, регистрации — Si gn up и входа на сайт — Log i п, а версия меню для вошедших на сайт предлагает полный доступ к функциям проекта. Кроме того, если пользователь вошел на сайт, его имя появляется в скобках в заголовке страницы и помещается после основного заголовка. Везде, где нужно поместить имя пользователя, можно свободно ссылаться на переменную $user, потому что, если пользователь не вошел, эта переменная будет пуста и никак не повлияет на внешний вид выводимой информации.
Стилевые настройки, применяемые в этом файле, находятся в файле sty 1es css, который будет подробно рассмотрен в конце главы. Кроме всего прочего, с его помощью создается широкий заголовок с цветным фоном и ссылки на страницах превращаются в кнопки с закругленными углами.
Пример 21.2. header.php
<?php // header.php
session_start():
echo "<!D0CTYPE html>\n<html><head><script src='0SC. js'x/script^": include 'functions.php';
Suserstr = ' (Guest)';
if (isset($_SESSI0N['user'])) {
$user = $_SESSION['user'];
$1oggedin =TRUE:
$userstr = " (Suser)";
}
else $1oggedin = FALSE:
echo "<title>$appname$userstr</title><link rel ='stylesheet"' "href='styles.css' type='text/css' />" .
"</head><body><div class='appname'>$appname$userstr</div>":
if (Sloggedin)
{
echo ”<br ><ul class='menu,>" .
"<1i><a href='members.php?vi ew=$user’>Home</a></1i >"
"<li><a href='members.php’>Members</a></li>" .
"<1i><a href='friends.php'>Friends</a></1i >".
"<li><a href=,messages.php,>Messages</a></li>, .
"<li><a href='profile.php’>Edit Profile</a></li>" .
"<li><a href='logout.php'>Log out</a></li></ul><br />";
} else
{
echo ("<br /xul class=,menu'>" .
"<li><a href=,index.php,>Home</a></li>n .
"<li><a href='signup.php’>Sign up</a></li>"
"<li><a href='login.php'>Log in</a></li></ul><br />" . ”<span class='info’>⇒ You must be logged in to " "view this page.</span><br /xbr />"):
11 Для просмотра этой страницы нужно войти на сайт
Использование тега <br/>, продемонстрированное в предыдущем примере, — быстрый, но довольно грубый способ создания разрядки в макете страницы. В данном примере он вполне приемлем, но вообще-то, наверное, для тонкой настройки разрядки элементов придется воспользоваться полями CSS.
Файл setup.php
После создания двух включаемых файлов настала очередь настройки используемых ими MySQL-таблиц. Это делается с помощью кода из примера 21.3, setup, php, который следует набрать и загрузить в свой браузер до вызова любых других файлов, в противном случае будут получены многочисленные сообщения об ошибках MySQL.
Создаваемые таблицы весьма лаконичны и имеют следующие имена и столбцы:
О members — имя пользователя user (проиндексированный столбец), пароль pass;
О messages — идентификатор id (проиндексированный столбец), автор auth (проиндексированный столбец), адресат recip, тип сообщения pm, сообщение message;
О friends — имя пользователя user (проиндексированный столбец), имя пользователей-друзей friend;
0 profi 1es — имя пользователя user (проиндексированный столбец), About me ( Обо мне) text.
Поскольку функция createTable сначала проверяет факт существования таблицы, эта программа может быть безопасно вызвана несколько раз без выдачи сообщений об ошибках.
Вполне возможно, что при принятии решения о расширении проекта вам понадобится добавить к этим таблицам множество дополнительных столбцов. После принятия такого решения для пересоздания таблицы может потребоваться MySQL- команда DROP TABLE.
Пример 21.3. setup.php
<html><head><ti11e>Setting up database</titlex/head><body>
<h3>Setting up...</h3> // Настройка...
<?php // setup.php
1 nclude _ once 'functions.ph p':
createTable('members
’user VARCHAR(16).
pass VARCHAR(16).
INDEX(user(6))'):
createTableCmessages'.
'id INT UNSIGNED AUTO INCREMENT PRIMARY KEY.
auth VARCHAR(16).
recip VARCHAR(16).
pmCHAR(l).
time INT UNSIGNED.
message VARCHAR(4096).
INDEX(auth(6)).
INDEX(recip(6))'):
createTable('friends'.
’user VARCHAR(16).
friend VARCHAR(16).
INDEX(user(6)).
INDEX(friend(6))’);
createTable('profi1es'.
'user VARCHAR(16).
text VARCHAR(4096).
INDEX(user(6))'):
<br />.. .done. // завершена.
</body></html>
Файл index.php
Это очень простой, но тем не менее необходимый файл, без которого у проекта не будет главной страницы. Он всего лишь отображает приветствие. В настоящем приложении это может быть страница, сообщающая о достоинствах вашего сайта, подталкивающая посетителя к регистрации.
Кстати, если все MySQL-таблицы созданы и включаемые файлы сохранены, вы можете загрузить файл примера21.4, index.php, в свой браузер, чтобы получить первое представление о новом приложении. На экране должно появиться изображение, показанное на рис. 21.1.
Пример 21.4. index.php
<?php // index.php
i nc1ude_once ’header.php':
echo "<br /><span class='main'>Welcome to Rob-in’s Nest.":
// Добро пожаловать в Сообщество Робина
if ($1oggedin) echo " Suser. you are logged in.":
11 вы вошли на сайт
elseecho ' please sign up and/or log in to join in . ’ :
// Пожалуйста, зарегистрируйтесь и (или) войдите на сайт
'/span><br /><Ьг /></body></htm>
)://localho$t')://localho$t'Robin’s Nest (Guest) X
)://localho$t'
)://localho$t'
File Edit View Favorites Tools Help
Robin's Nest (Guest) ..
[ Home ] [ Sign up [ Log in
=> You must be logged In to view this page.
Welcome to Robin's Nest, please sign up and/or log in to join in.
^100%
Рис. 21.1. Главная страница сайта
Файл signup.php
Теперь нам нужен модуль, позволяющий пользователям присоединиться к новой сети, который показан в примере 21.5, signup.php. Это более длинная программа, но все ее части вам уже встречались.
Начнем ее изучение с блока HTM L, расположенного в конце программы. Это простая форма, дающая возможность ввести имя пользователя и пароль. Но обратите внимание на использование пустого <5рап>-контейнера с атрибутом ' имеющим значение ’ i nfo1. В этот контейнер будут помещены результаты Ajax-вызова, имеющегося в этой программе, с помощью которого проверяется возможность использования того имени пользователя, которое вы хотите. Полное описание принципов работы этого вызова дано в главе 17.
Проверка возможности использования желаемого имени пользователя
Вернемся к началу программы, где имеется блок кода JavaScript, начинающийся с функции checkUser. Эта функция вызывается событием JavaScript onBlur, возникающим при перемещении фокуса за пределы принадлежащего форме поля username. Сначала эта функция вставляет в упоминавшийся ранее span-контейнер
(у которого id имеет значение ' info') пустую строку, которая очищает этот контейнер, если он уже имел какое-нибудь содержимое.
Затем делается запрос к программе checker. php, которая сообщает о возможности использования имени пользователя user. После чего возвращенный Ajax-вызовом результат — приветственное сообщение — помещается в <$рап>-контейнер с идентификатором ' i nfo'.
За блоком JavaScript следует P H P-код, известный по разделу главы 16, в котором рассматривалась проверка данных формы. Этот блок кода также использует функцию sanitizeString, чтобы удалить потенциально вредные символы перед поиском имени пользователя в базе данных, и если такое имя еще никем не используется, он вставляет новое имя пользователя Suser и пароль Spass в таблицу базы данных.
После успешной регистрации пользователю предлагается войти на сайт. Более гибкой реакцией на появление нового пользователя мог бы стать автоматический вход на сайт, но я не захотел излишне усложнять код и оставил модули регистрации и входа не связанными друг с другом. Я уверен, что при желании вы сможете и сами без особого груда реализовать подобную функцию.
В этом файле используется CSS-класс fieldname, предназначенный для приведения в порядок полей формы и их точного выравнивания друг под другом в столбцах. При загрузке в браузер (вместе с показанным далее файлом checkuser.php) эта программа отобразит информацию, показанную на рис. 21.2, из которой видно, что Ajax-вызов помог определить доступность имени Robin для использования. Если нужно, чтобы в поле пароля показывались одни только звездочки, измените тип этого поля с text на password.
Пример 21.5. signup.php
•?php // signup.php
i nc Iude_once ' header. php' :
echo <<< END
'-■script*
function checkUser(user)
{
if (user.value == '')
{
0(’info’ ) .innerHTML - '' return
params = "user=" + user.value
request - new ajaxRequestO
request.open("POST” . "checkuser.php". true)
request.setRequestHeader("Content-type".
"appli cation/x-www-form-ur1encoded")
request.setRequestHeader("Content-1ength". params.1ength) request.setRequestHeader("Connection". "close")
request .onreadystatechange = functionO)
if (this.readyState == 4)
if (this.status == 200)
if (this.responseText != null)'
0(' info') .innerHTML = this.responseText
}
request.send(params)
.function ajaxRequest() {
try {var request = new XMLHttpRequestO}
catch(e l){
try {request = new ActiveX0bject("Msxml2.XMLHTTP")} catch(e2){
try {request = new ActiveXObjectCMicrosoft.XMLHTTP")} catch(e3){
request = false
}}} return request
}
</script>
<div class=,main'><h3>Please enter your details to sign up</h3> // Введите, пожалуйста, свои регистрационные данные
_END;
Serror = Suser = Spass =
if (isset(S_SESSION['user'])) destroySessionO;
if (isset($_P0ST[‘user']))
{
Suser = sanitizeString($_POST[’user']);
Spass = sanitizeString(S_POST['pass']);
if (Suser = "" || Spass == "")
Serror = "Данные введены не во все поля<Ьг /><Ьг />"; else
{
if (mysql_num_rows(queryMysql("SELECT * FROM members
WHERE user-'Suser"')))
Serror = "Такое имя уже существует<Ьг /><br />"; else
{
queryMysql("INSERT INTO members VALUES('Suser'.
'Spass')");
die("<h4>Account created</h4>Please Log in.<br />
<br />");
// Учетная запись создана.
// пожалуйста, войдите на сайт
}
echo <«_END
<form method='post' action='signup.php'>$error
<span class=’fieldname'>Username</span>
<input type='text’ maxlength=,16' name=’user’ value='$user'
onBlur='checkllser(this) 7><span id=' info'></span><br />
<span class='fieldname'>Password</span>
<input type='text' maxlength='16' name=’pass'
value='Spass' /><br />
_END;
?>
<span class='fieldname'> </span>
<input type='submit' value='Sign up' />
</form></div><br /></body></html>
Рис. 21.2. Страница регистрацииРис. 21.2. Страница регистрации
Рис. 21.2. Страница регистрации
Рис. 21.2. Страница регистрации
При использовании рабочего сервера я не рекомендую сохранять пользовательские пароли так, как это сделано с целью экономии места и упрощения кода в данном проекте, то есть в открытом виде. К паролям нужно подмешивать произвольные строки и хранить их в виде хеш-строк, получаемых с помощью MD5 или других односторонних функций. Более подробно этот процесс рассмотрен в главе 12.
Файл checkuser.php
Файл checkuser. php, показанный в примере 21.6, предназначен для работы с файлом signup, php. В нем содержится программа, осуществляющая поиск имени пользова-
теля в базе данных и возвращающая строку, свидетельствующую о том, что такое имя уже было кем-то использовано.
Поскольку работа этой программы зависит от функций sani ti zeStri ng и queryMysql, в нее в самом начале включается файл functi ons. php. Если переменная, являющаяся элементом массива S P0ST, который имеет ключ ' user', имеет какое-нибудь значение, функция ищет это значение в базе данных. В зависимости от того, используется такое значение в качестве имени пользователя или нет, выводит либо строку «К сожалению, имя занято» (Sorry, already taken), либо строку «Это имя доступно» (Username available). Для решения данной задачи достаточно просто проверить значение, возвращенное функцией mysql num rows. Если будет возращен нуль, значит, такое имя не найдено, а если единица, то запись с таким именем уже существует.
Для установки перед строкой либо крестика, либо галочки используются H TM L-элементы &#х2718; и &#х2714;.
Пример 21.6. checkuser.php
<?php // checkuser.php i nclude_once 'functi ons.php';
if (isset($_P0ST['user']))
{
$user = sanitizeString(S$ POST[’user']):
if (mysql_num _ rows(queryMysql("SELECT * FROM members
WHERE user=,$user'")))
echo "<span class='taken’> ✘: " .
"Sorry, this username is taken</span>":
// К сожалению, имя занято
else echo "<span class='available,> ✔ ".
"This username is available</span>";
// Это имя доступно
} ?>
Файл login.php
После того как пользователи получили возможность регистрироваться на сайте в файле logi n. php, показанном в примере 21.7, предоставляется код, который необходим для входа на сайт. Страница, выводимая этим кодом, как и страница регистрации, похожа на обычную HTM L-форму, имеет простую проверку на отсутствие ошибок, а также использует функцию sani ti zeStri ng перед отправкой запроса к базе данных MySQL.
Стоит обратить особое внимание на присваивание в случае успешной проверки переменным сессии (элементам массива с ключами ' user' и ' pass') значений имени пользователя и пароля. На период активности текущей сессии эти переменные будут доступны всем программам проекта, позволяя им автоматически предоставлять доступ вошедшим на сайт пользователям.
Возможно, у вас возникнет вопрос, почему в случае успешного вход на сайт используется функция die. Это сделано из соображений экономии, поскольку данная команда объединяет в себе сразу две команды: echo и exit. Для стилевого оформления этого файла (как и большинства других), чтобы задать отступ содержимого от левого края, применяется класс mai п.
Если вызвать эту программу в браузере, появится изображение, показанное на рис. 21.3. Обратите внимание на то, как для маскировки пароля звездочками тегу <i nput /> был присвоен тип password, чтобы никто из заглядывающих пользователю через плечо не смог его увидеть.
Пример 21.7. login.php
<?php // login.php
i nclude_once 'header.php';
echo "<div class=,main'>
<h3>Please enter your details to log in</h3>":
// Введите, пожалуйста, свои данные для входа на сайт
Serror = Suser = Spass =
if (isset(S_P0ST[’user']))
{
Suser = sanitizeStri ng($_P0ST['user’]):
Spass = sanitizeString(S5 POST['pass']):
if (Suser ==|| Spass = "")
{
Serror = "Not all fields were entered<br />";
// Данные введены не во все поля
}
else
{
Squery = "SELECT user.pass FROM members
WHERE user='Suser' AND pass='Spass’":
if (mysql_num _rows(queryMysql(Squery)) == 0)
{
Serror = "<span class='error'>Username/Password invalid</span><br /xbr />";
// Ошибка при вводе пары "имя пользователя - пароль"
}
else
{
$_SESSI0N[’user'] = Suser;
S_SESSI0N['pass'] = Spass;
dieC'You are now logged in .” .
"Please <a href='members.php?view=Suser'>" .
"click here</a> to continue.<br /><br />”);
// Вы уже вошли на сайт. Пожалуйста, щелкните на этой ссылке
}
}
}
echo <«_END
<form method='post' action='login.php'>$error
<span class='fieldname,>Username</span><input type=’text' maxlength='16’ name=,user' value=’$user' /xbr />
-span class='fieldname'>Password</spanxinput type*'password* maxlength='16’ name='pass’ value='$pass' />
_FND:
? >
<br i '
<span с 1ass='fi eldname'> :</span>
<input type='submit' value='Login' />
</form><br /></div></body></html>
Рис. 21.3. Страница входа на сайт
Файл profile.php
После регистрации и входа на сайт у новых пользователей может появиться желание создать профиль. Это можно будет сделать с помощью файла profi 1е.php, код которого показан в примере 21.8. Я думаю, в этом коде можно найти несколько интересных для вас фрагментов, к которым относятся функции загрузки изображений на сайт, изменения их размера и четкости.
Для начала рассмотрим HTM L-код, который находится в конце этого файла. Он похож на формы, которые вы только что видели, но на этот раз в форме используется параметр enctype=' mul ti part/form-data ’. Он позволяет одновременно отправлять более одного типа данных, разрешая отправку на сайт изображений вместе с текстом. В форме также есть элемент <i nput />, имеющий тип fi 1е, с помощью которого создается кнопка просмотра. Щелкнув на ней, пользователь может выбрать файл для загрузки на сайт.
При отправке данных формы выполняется код, который находится в начале программы. Перед тем как разрешить выполнение программы, этот код проверяет, вошел ли пользователь на сайт. И только после этого отображается заголовок страницы.
Добавление текста в поле About Me (Обо мне)
Работа продолжается проверкой элемента с ключом ' text', принадлежащего массиву, который создан POST-запросом. Наличие в нем содержимого свидетельствует о том, что программе отправлен текст. Если текст отправлен, из него удаляются потенциально вредные фрагменты, а все длинные последовательности пробелов (а также символы возврата каретки и перевода строки) заменяются одним пробелом. В эту функцию включена двойная проверка безопасности, гарантирующая, что пользователь с таким именем действительно существует в базе данных и что перед вставкой этого текста в базу данных, где он превратится в сведения о пользователе в поле About Me (Обо мне), не сможет пройти никакая атака со стороны взломщика.
Если текст не отправлен, делается запрос к базе данных на обнаружение уже существующего текста, чтобы заранее заполнить текстовое окно, позволяя тем самым пользователю отредактировать текст.
Добавление изображения профиля
Теперь перейдем к разделу, проверяющему системную переменную $_FILES на наличие загруженного на сайт изображения. Если изображение было загружено, создается строковая переменная Ssaveto. Этой переменной присваивается значение, состоящее из имени пользователя и расширения JPG. Например, для пользователя с именем Ji 11 переменной $saveto будет присвоено значение J il l1. jpg. Это значение является именем файла, в котором будет сохранено загруженное изображение, предназначенное для использования в профиле пользователя.
Сразу за этим проверяется тип загруженного изображения, которое принимается только в том случае, если имеет JPEG-, PNG- или GIF-формат. Если тип загруженного изображения не относится к разрешенным, флажок Stypeok принимает значение FALSE, что препятствует выполнению последнего блока кода загрузки изображения. Но если изображение принимается, загруженное изображение присваивается переменной Ssrc. Для этого используется одна из функций imagecreatefrom, соответствующая типу загруженного изображения. Теперь изображение находится в том формате, который может быть обработан средствами РНР.
Обработка изображения
Сначала в переменных $wи $h сохраняются размеры изображения, для чего используется следующая инструкция, представляющая собой быстрый способ присваивания значений из массива отдельным переменным:
list($w. $h) = getimagesize(Ssaveto):
Затем с использованием значения переменной $max (которое установлено в 100) вычисляются новые размеры, которые приведут к созданию нового изображения с таким же соотношением сторон, но размерами, не превышающими 100 пикселов. В результате этого переменные $tw и $th получают новые значения. При желании получить миниатюры меньшего или большего размера нужно просто соответствующим образом изменить значение переменной $тах.
После этого вызывается функция imagecreatetruecolor, которая создает новую пустую картинку шириной Stw и высотой Sth и сохраняет ее в переменной $tmp. Затем вызывается функция imagecopyresampled, которая изменяет размер изображения, сохраненного в переменной Ssrc, на тот, который хранится в новой переменной $tmp. Иногда изменение размера изображения может привести к небольшой потере резкости получаемой копии, поэтому в следующем фрагменте кода используется функция imageconvolution, слегка повышающая резкость изображения.
И наконец, изображение сохраняется как J PEG -файл в том месте, которое определено значением переменной $saveto, после чего оба изображения — исходное и пустое, имеющее измененные размеры, — удаляются из памяти с помощью функции imagedestroy, возвращая системе занятую под них память.
Отображение текущего профиля
И последнюю, но не менее важную задачу выполняет функция showProfi le из файла functions. php, которая позволяет пользователю посмотреть, как выглядит текущий профиль, перед его редактированием. Эта функция вызывается до отображения формы HTM L. Если профиля еще нет, ничего отображаться не будет.
При выводе изображения профиля с помощью CSS создается граница, тень и поле справа, чтобы отделить текст профиля от изображения.
Результат загрузки в браузер файла, код которого содержится в примере 21.8, показан на рис. 21.4. На этом рисунке можно увидеть, что текстовое поле было заранее заполнено текстом About me (Обо мне).
Пример 21.8. profile.php
<?php // profile.php
i nclude_once 'header.php':
if (!$1oggedin) die():
echo ”<div class=,main,><h3>Your Profile<-/h3>":
// Ваш профиль
if (isset($_P0ST['text' I))
Stext = sani tizeStri ng($_P0ST['text1]);
Stext = preg_replace(7\s\s+/’ . 1 '. Stext);
if (mysql_num _ rows(queryMysql("SELECT * FROM profiles WHERE user='Suser'")))queryMysql("UPDATE profiles SET text='Stext' where user='Suser'");
else queryMysql("INSERT INTO profiles VALUES('Suser'. 'Stext')"):
}
else
{
Sresult = queryMysql("SELECT * FROM profiles WHERE user='Suser'"):
if (mysql_num_rows(Sresult))
{
Srow = mysql_fetch_row(Sresult):
Stext = stripslashes(Srow[l]):
}
else $text =
Stext = stripslashes(preg_replace('/\s\s+/'. ' '. Stext)):
if (isset(S_FILES['image'] [ 'name']))
{
Ssaveto = "Suser.jpg":
move _uploaded_ fi1e($_FILES[' image'] [ 'tmp_name'], $saveto):
Stypeok =TRUE;
switch($_FILES['image'] [ 'type'])
{
case "image/gif": Ssrc = imagecreatefromgif(Ssaveto); break:
case "image/jpeg":
//Как обычный, так и прогрессивный JPEG-формат
case "image/pjpeg": Ssrc = imagecreatefromjpeg(Ssaveto): break:
case "image/png": Ssrc = imagecreatefrompng(Ssaveto): break; default:Stypeok = FALSE; break;
}
i f ($typeok)
{
list($w, Sh) = getimagesize(Ssaveto):
Smax = 100;
Stw = Sw;
Sth = Sh;
if (Sw > Sh && Smax < Sw)
{
$th = $max / $w * Sh;
$tw = Smax:
}
elseif (Sh > Sw && Smax < Sh)
$tw = $max / $h * $w:
$th = Smax:
elseif (Smax < $w)
Stw = Sth = Smax:
$tmp = imagecreatetruecolor($tw. Sth):
imagecopyresampled($tmp. Ssrc. 0. 0. 0. 0. Stw. Sth. Sw. Sh);
imageconvolution(Stmp. array(array(-l. -1. -1).
аггау(-1. 16. -1). аггау(-1. -1. -1)). 8. 0):
imagejpeg(Stmp, Ssaveto);
imagedestroy(Stmp);
imagedestroy(Ssrc);
showProfile(Suser);
echo <«_END
<form method='post' action='profile.php' enctype='multi part/form-data'>
<h3>Enter or edit your details and/or upload an image</h3>
// Введите или отредактируйте сведения и (или) загрузите изображение
<textarea name='text' cols='50' rows=,3’>Stext</textarea><br />
_END;
?>
Файл members.ptu)
Используя файл members .php, код которого показан в примере 21.9, пользователи сайта смогут найти других участников сети и добавить их в список своих друзей (или удалить их оттуда, если они уже числились друзьями). У этой программы есть два режима: первый показывает пользовательские профили, а второй выдает список всех участников и их отношений к вам.
Просмотр профилей пользователей
В первом режиме проверяется элемент массива GET-вызова, имеющий ключ ’ vi ew'. Если такой элемент существует, значит, пользователь хочет просмотреть чей-то профиль, поэтому программа использует функцию showProfi 1е, а также предоставляет две ссылки для друзей и сообщений пользователей.
Добавление и удаление друзей
Затем проверяются два элемента массива GET-вызова с ключами ' add' и ' remove'. Если один или другой имеют значение, то оно будет представлять собой имя пользователя,
которого нужно либо добавить в список друзей, либо удалить из этого списка. Для этого выполняется поиск записи пользователя в MySQL-таблице fri ends с последующей вставкой имени пользователя в таблицу или удалением его из этой таблицы.
Разумеется, каждая отправленная переменная сначала проходит обезвреживающую обработку, осуществляемую функцией sanitizeString, которая призвана обеспечить ее безопасное использование в MySQL.
Вывод списка всех участников
В последнем блоке кода выдается SQL-запрос на вывод списка всех имен пользователей. Перед выводом заголовка страницы число возвращенных имен присваивается переменной $num.
Затем с помощью цикла for осуществляется последовательный перебор всех участников с извлечением сведений о них и последующим поиском их в таблице friends с целью определения, проявляют ли они интерес к дружбе с пользователем или проявляет ли пользователь свой интерес к дружбе с ними. Если обнаруживается взаимный интерес, такие участники классифицируются как взаимные друзья.
Переменная $tl имеет ненулевое значение в том случае, когда пользователь проявил интерес к дружбе с другим участником, а переменная $t2 имеет ненулевое значение, когда другой участник заинтересовался дружбой с пользователем. В зависимости от значений этих переменных текст, отображаемый после каждого имени пользователя, показывает степень взаимоотношений его владельца с текущим пользователем, если таковые имеются.
Кроме того, для отображения взаимоотношений используются соответствующие значки. Двунаправленная стрелка означает, что пользователи являются взаимными друзьями. Стрелка влево показывает, что пользователь проявил интерес к дружбе с другим участником. Стрелка вправо показывает, что дружбой с пользователем заинтересовался другой участник.
И наконец, в зависимости от заинтересованности пользователя в дружбе с другим участником ему предоставляется ссылка для добавления этого участника в список друзей или для удаления его оттуда.
При вызове в браузере программы из примера 21.9 будет выведена страница, показанная на рис. 21.5. Обратите внимание на то, как пользователю предлагается заинтересоваться дружбой с тем участником, к которому еще не проявлен интерес (follow), но если участник уже проявил интерес к дружбе с пользователем, то напротив его имени появляется ссылка recip (ответить), позволяющая ответить ему взаимностью. Если пользователь уже заинтересовался дружбой с другим участником, он может выбрать ссылку drop (отклонить) для удаления этой заинтересованности.
Пример 21.9. members.php
<?php // members.php i nclude_once ' header.php';
if ( !$loggedin) die();
echo "<div class=,main,>";
if (isset($_GET[’view’]))
{
$view = sanitizeString($_GET['view']);
i f (Sview == $user) Sname = "Your";
elseSname = "Sview's";
echo "<h3>Sname Profile</h3>";
showProfile(Sview);
echo "<a class='button' href='messages.php?view=$view,>"
"View Sname messages</a><br /><br />";
die("</div></body></html>");
}
if (isset($_GET[’add']))
{
$add = sanitizeString($_GET['add']):
if (!mysql_num _ rows(queryMysql ("SELECT * FROM friends
WHERE user='$add' AND friend='Suser"')))
queryMysql ("INSERT INTO friends VALUES CSadd'. 'Suser')"):
}
elseiif (isset(S_GET['remove']))
{
Sremove = sanitizeString($_GET[' remove']):
queryMysql("DELETE FROM friends WHERE user='Sremove' AND friend=‘Suser’"): }
Sresult = queryMysql("SELECT user FROM members ORDER BY user"):
Snum= mysql_numrrows($result):
echo "<h3>Other Members</h3><ul>": II Другие участники
for (Sj = 0 : Sj < Snum : ++$j) {
Srow = mysql_ fetch_row(Sresult):
if ($row[0] = Suser) continue:
echo "<li><a href='members.php?view=Srow[0]'>Srow[0]</a>";
Sfollow = "follow": II Проявить заитересованность в дружбе
Stl = mysql_num _ rows(queryMysql("SELECT * FROM friends
WHERE user='$row[0]' AND friend='Suser'")):
St2 = mysql_num _rows(queryMysql("SELECT * FROM friends
WHERE user='Suser' AND friend='$row[0]'"));
if ((Stl + St2) > 1) echo " &harr: is a mutual friend":
II Двунаправленная стрелка, взаимный друг
elseiif (Stl)echo " &larr: you are following":
II Ст Стрелкаn влево, ы вы заинтересованы в3 дружбе
elseiif (St2){ echo " &rarr: is following you":
Sfollow = "recip": }
// Стрелка вправо, проявляет интерес к дружбе с вами
if (!Stl) echo " [<а href=’members.php?add=".Srow[0]. "'>$follow</a>]M:
elseecho " [<a href='members. php?remove='\$row[0] . "’>drop</a>]";
// Снять заинтересованность в дружбе
<br /></div></body></html>
■^—
На рабочем сервере могут быть тысячи или даже сотни тысяч пользователей, поэтому,
<.**•' 4 * скорее всего, возникнет потребность в существенной модификации этой программы, чтобы включить поддержку поиска в тексте About me (Обо мне), поддержку разбиения выводи-
мой информации на страницы и т. д.
Home | [Members| |Friends]| Messages ][Edit ProfileI [Log out)
Other Members
Alex -M Is a mutual friend [drop]
Fred [follow]
Martha [follow]
Martin «- you are following [drop]
Mary [follow]
SandraD -» is following you [redp]
Trevor [follow]
<uoox -
Рис. 21.5. Использование модуля участников
Файл friends.php
Код модуля friends. php, показывающий друзей пользователя наряду со степенью проявления интереса к дружбе, приведен в примере 21.10. Этот код выполняет исследование таблицы friends, похожее на исследование, которое проводится в программе members php, но только применительно к одному пользователю. Затем этот модуль показывает всех друзей пользователя и проявляющих интерес к дружбе, а также тех участников, к дружбе с которыми пользователь сам проявляет интерес.
Все люди, проявляющие интерес к дружбе, сохраняются в массиве по имени $followers, а все люди, к дружбе с которыми проявляется интерес, сохраняются в массиве по имени Sfol 1owi ng. Затем для извлечения всех людей, которые проявляют интерес к дружбе и к которым проявлен взаимный интерес, применяется следующий весьма лаконичный фрагмент кода:
Smutual = array_intersect(Sfollowers. Sfollowing):
Функция array intersect извлекает всех участников, являющихся общими для обоих массивов, и возвращает новый массив, который содержит только этих людей. Затем этот массив сохраняется в переменной $mutual. Теперь можно воспользоваться функцией array di ff для каждого из массивов Sfol 1owers и Sfol 1owi ng, чтобы в них содержались только те люди, которые не являются взаимными друзьями:
Sfol lowers = arraydi ff (Sfol 1owers. Smutual);
Sfollowing = array_diff($following, Smutual):
В результате этого в массиве $mutual будут содержаться только взаимные друзья, в массиве $fol 1owers — только люди, проявляющие интерес к дружбе (без взаимных друзей), а в массиве $fol 1owi ng — только люди, к дружбе с которыми проявляется интерес (также без взаимных друзей).
При наличии этих массивов упрощается решение задачи отдельного отображения каждой категории участников. Результат этого решения показан на рис. 21.6. Функция РН Р si zeof возвращает количество элементов в массиве. Здесь я использую ее только для вызова кода, когда размер массива не является нулевым (то есть друзья данного типа существуют). Обратите внимание на то, как путем использования переменных $namel, $name2 и $name3 в соответствующих местах код может сообщить о том, что вы (пользователь) смотрите собственный список друзей, используя слова Your (Ваши) и You are (Вы), вместо того чтобы просто отобразить имя пользователя.
Если хотите вывести на экран информацию о профиле пользователя, можно убрать символы комментария из закомментированной строки кода.
Пример 21.10. friends.php
<?php // friends.php i nclude_once 'header.php':
if (!$1oggedin) die():
if (isset(S_GET['view'])) $view = sanitizeString($_GET['view']): else$view = Suser;
if (Sview == Suser)
{
Snamel = Sname2 = "Your":// Ваши
Sname3 ="You are": // Вы
} else
{
Snamel = "<a href=,members.php?view=$view,>$view</a>’s";
Sname2 = "Sview's":
Sname3 = "Sview is";
1
echo "<div class=’main,>";
// Если хотите вывести здесь профиль пользователя, уберите знаки
// комментария из следующей строки
// showProfile(Sview):
Sfol lowers = arrayO:
Sfol lowing = arrayO ;
Sresult = queryMysql("SELECT * FROM friends WHERE user='Sview'"):
Snum = mysql_num_rows(Sresult):
for (Sj - 0 ; Sj < Snum ; ++$j)
{
$row= mysql_fetch_row(Sresult);
$followers[$j] = $row[l]:
}
Sresult = queryMysql("SELECT * FROM friends WHERE friencKSview'"):
Snum = mysql_num_rows(Sresult):
for (Sj = 0 ; Sj < Snum : ++$j)
{
Srow= mysql_ fetch_row(Sresult):
Sfollowing[Sj] = $row[0]:
Smutual = array_intersect($followers. Sfollowing):
Sfollowers = array_diff($followers. Smutual):
Sfollowing = array_diff(Sfollowing. Smutual):
Sfriends = FALSE:
if (sizeof(Smutual))
{
echo "<span class='subhead'>Sname2 mutual friends</span><ul>": // Взаимные друзья
foreach(Smutual as Sfriend)
echo "<li><a href='members.php?view=$friend'>Sfriend</a>":
echo "</ul>":
Sfriends = TRUE:
if (sizeof(Sfollowers))
{
echo "< span class='subhead'>Sname2 followers</span><ul>":
// Интересующиеся в дружбе с...
foreach(Sfollowers as Sfriend)
echo "<li><a href=’members.php?view=Sfriend'>Sfriend</a>":
echo "</ul>":
Sfriends = TRUE:
}
if (sizeof(Sfollowing))
{
echo "< span class='subhead'>Sname3 following</span><ul>":
// Заинтересован в дружбе с...
foreach(Sfollowing as Sfriend)
echo "<li><a href='members.php?view=Sfriend'>Sfriend</a>":
echo "</ul>":
Sfriends = TRUE:
if (!Sfri ends) echo "<br />You don’t have any friends yet.<br /><br />" // Пока у вас нет друзей
echo "<а class='button' href=,messages.php?view=$view,>" "View $name2 messages</a>";
?>
</div><br /></bodyx/html>
file £dit View Favorites l I ook Helpfile £dit View Favorites l I ook Help^I—кгаиМКв^I—кгаиМКв.*&••;>;(•of?☆ @ ,,, ..*&••;>;(•of?☆ @ ,,, .Home)|Members| [Friends] Messages!| Edit Profile)[Log out|Home)|Members| [Friends] Messages!| Edit Profile)[Log out|
file £dit View Favorites l I ook Help
file £dit View Favorites l I ook Help
^I—кгаиМКв
^I—кгаиМКв
.*&••;>;(•of?☆ @ ,,, .
.*&••;>;(•of?☆ @ ,,, .
Home)|Members| [Friends] Messages!| Edit Profile)[Log out|
Home)|Members| [Friends] Messages!| Edit Profile)[Log out|
Your mutual friends
Alex
Your followers
SandraD
You are following
Martin
I View Your messages]
«Uoo%
Рис. 21.6, Отображение друзей пользователя и заинтересованности во взаимной дружбе
Файл messages.php
Код последнего из основных модулей, messages.php, показан в примере 21.11. Этот модуль начинает работу с проверки наличия отправленного сообщения в элементе POST-массива, имеющего ключ ' text'. Если сообщение имеется, оно вставляется в таблицу messages. Одновременно с этим сохраняется и значение элемента, имеющего ключ ' pm’. Значение этого элемента свидетельствует об открытом или закрытом статусе сообщения. Нуль представляет открытое, а единица — закрытое сообщение.
Затем отображаются пользовательский профиль и форма для ввода сообщения, а также переключатели для выбора между отправкой закрытого (private) или открытого (public) сообщения. После этого показываются все сообщения: если они имеют статус открытого сообщения, их могут просматривать все пользователи, а закрытые сообщения могут просматривать только отправитель и получатель. Все это управляется двумя запросами к базе данных MySQL. В дополнение к этому, когда сообщение имеет статус закрытого, оно представлено словом whispered (прошептал) и отображается курсивом.
И наконец, программа отображает две ссылки: для обновления сообщений (refresh the messages) в том случае, когда другой пользователь за время просмотра опубликовал новое сообщение, и для просмотра друзей пользователя (view the user's friends). Здесь опять используется прием с переменными Snamel и $name2, чтобы при просмотре вашего собственного профиля вместо имени пользователя отображалось слово К)мг(Ваши).
Результат запуска этой программы в браузере показан на рис. 21.7. Обратите внимание на то, как пользователям, просматривающим собственные сообщения, предоставляется ссылка для того, чтобы можно было стереть (erase) любое из нежелательных сообщений.
Пример 21.11. messages.php
<?php // messages.php
i nclude_once ’header.php':
if (!$1oggedin) die():
if (isset($_GET['view'])) Sview = sanitizeString($ t GET['view']);
elseSview = Suser:
if (isset($_P0ST[’text']))
{
$text = sanitizeString($_POST['text']):
if ($text != "")
{
$pm = substr(sanitizeString($_POST['pm']).0.1):
$time = timeO:
queryMysql("INSERT INTO messages VALUES(NULL. 'Suser'.
'$view'. '$pm'. Stime. '$text')"):
}
}
if (Sview != '”')
{
if (Sview == Suser) Snamel = Sname2 = "Your”; // Ваши else
{
Snamel = "<a href=,members.php?view=$view,>$view</a>,s";
Sname2 = "Sview's":
}
echo "<div class='main,><h3>Snamel Messages</h3>";
// Сообщения
showProfile(Sview);
echo <«_END
<form method=,post’ action='messages.php?view=Sview’>
Type here to leave a message:<br />
// Наберите здесь текст, чтобы оставить сообщение
<textarea name='text' cols=,40' rows=,3,></textareaxbr />
Public<input type='radio' name='pm' value='0’ checked='checked' />
Private<input type='radio' пате='ргтГ value='l' />
<input type='submit' value='Post Message' /></form><br /> // Опубликовать сообщение
_END;
if (isset($_GET['erase']))
{
$erase = sanitizeString($_GET['erase']);
queryMysql("DELETE FROM messages WHERE id=$erase AND recip='Suser'");
}
Squery = "SELECT * FROM messages WHERE recip='Sview' ORDER BY time DESC": Sresult = queryMysql(Squery);
Snum = mysql_num_rows(Sresult):
for (Sj = 0 ; Sj < Snum : ++$j)
{
Srow = mysql_ fetch_row(Sresult);
if (Srow[3] == 0 || Srow[l] — Suser || Srow[2] == Suser)
{
echo date('M jS \'y g:ia :'. $row[4]):
echo " <a href='messages.php?view=$row[l]’>$row[l]</a> ":
if ($row[3] == 0)
echo "wrote: "$row[5]" ";
// Сообщил
else echo "whispered: <span class='whisper'>" "":$row[5]":</span>
// Прошептал
if ($row[2] == Suser)
echo "[<a href=’messages.php?view=$view" .
"&erase=$row[0]’>erase</a>]":
// Стереть
echo "<br />":
}
}
if (!$r>um) echo "<br /><span class='info’>No messages yet</span><br /><br />";
// Пока сообщений нет
echo "<br /><а class='button' href=,messages.php?view=$view’>Refresh messages</a>". // Обновить сообщения
"<a class='button’ href='friends.php?view=$view'>\/iew Sname2 friends</a>":?>
</div><br /></body></html>
НИННИН
НИН
НИН
I^>)Ц https7ocslhost'mKaa3es.pbpVviev Р - § С Х^Я Robin’s Nest (Robin) X
file Edit View favorites look Help
[Home) Mem bers)(Friends)(Messages][Edit Profile) Log but)
Your Messages
Hi, I'm Robin. Welcome to my ’Nest'.
Type here to leave a message:
Public® Private о [Post Message j
Apr 17th '12 10:49am: Alex wrote: "This seems to be working well..." [erase]
Apr 6th '12 12:52pm: Mary whispered: "Hiya\n [erase]
[ Refresh messages ] [ View Your friends)
100%
Рис. 21.7. Модуль передачи сообщений
Файл logout.php
Модуль 1ogout. php, код которого показан в примере 21.12, является завершающим ингредиентом рецепта нашей социальной сети. Он отображает страницу выхода с сайта, которая закрывает сессию и удаляет любые связанные с ней данные и cookie- файлы. Результат вызова этой программы можно увидеть на рис. 21.8, где на этот раз к пользователю обращена просьба щелкнуть на ссылке. Эта ссылка приведет его на главную страницу, предназначенную для пользователей, еще не вошедших на сайт, с которой удалены все ссылки в верхней части экрана, предназначенные для вошедших на сайт пользователей. Разумеется, вы можете создать код на JavaScript или на РНР, который сразу перенаправил бы пользователя на эту страницу, чтобы очистить страницу выхода из сайта от ненужных элементов.
Пример 21.12. logout.php
<?php // logout.php i nclude_once 'header.php';
if (isset($_SESSI0N['user']))
destroySessionO:
echo "«div class=’main'>You have been logged out. Please " .
"<a href='index.php'>click here</a> to refresh the screen.";
// Вы уже покинули сайт. Пожалуйста...
// ... щелкните здесь, чтобы обновить экран
}
else echo "<div class=’main'><br />" .
"You cannot log out because you are not logged in":
// Вы не можете завершить сеанс работы.
// потому что не входили на сайт
<br /><br /></div></body></html>
Рис. 21.8. Страница выхода с сайта
Файл styles.css
Таблица стилей, используемая для этого проекта, показана в примере 21.13. В нем содержится несколько наборов объявлений.
О * — с помощью универсального селектора устанавливается используемое в проекте по умолчанию семейство шрифтов и размер шрифта.
О body — задается ширина окна проекта, горизонтальная центровка, указывается цвет фона и задается граница окна.
О html — устанавливается цвет фона блока HTML.
О i mg — задается граница, тень и правое поле для всех изображений.
О 1i а, и . button — удаляются подчеркивания у гиперссылок во всех тегах <а>, которые находятся внутри элемента <1 i >, и у всех элементов, использующих класс button.
О 1i : ahover и . button: hover — устанавливается цвет отображения текста в элементах <1 i> и в элементах, использующих класс button, при прохождении над ними указателя мыши.
О .appname — задаются свойства для заголовков, применяющих класс appname, включая центровку, цвета фона и текста, семейство шрифтов и размер шрифта, а также отступы.
О . fi el dname — устанавливается ширина элементов, использующих класс fi el dname, но сначала для них задается плавающая модель.
О . mai п — применяется отступ для тех элементов, которые используют этот класс.
О . i nfo — задействуется для отображения важной информации: для ее элементов, использующих этот класс, устанавливаются цвета фона и текста, применяются граница и отступы.
О .menu 1i , и .button — обеспечивается выстраивание всех элементов <1 i >и элементов, использующих класс button, в линию, задается наличие у них отступов, границы, цветов фона и первого плана, правого поля, скругленных границ и тени (в результате чего получается эффект кнопки).
О . subhead — выделяется блок текста.
О .taken, .available, .error и .whisper— устанавливаются цвета и стили шрифтов, используемых для отображения различных типов информации.
Пример 21.13. Таблица стиля проекта
/* styles.css */
font-family:verdana.sans-seri f ;
font-size :14pt; }
body {
width:700px;
positionrelative:
margin:7px< auto;
background;#f8f8f8;
border;lpxX solidd #888; }
html {
background;#fff }
img {
border:lpx< solidi black;
margin-right;15px;
-moz-box-shadow :2px 2px 2px #888;
-webkit-box-shadow;2px 2px 2px #888;
box-shadow :2px 2px 2px #888; }
l i a, .button {
text-decoration:none; }
li a;hover, .button;hover {
col or;green; }
.appname {
text-align ;center;
background ;#eb8:
color :#40d;
font-fami 1у:helveti ca;
font-size :20pt;
padding :4px; }
.fieldname { float:left;
width:120px; }
.main { margin-left:40px; }
.info {
background lightgreen;
margin-left 40px; }margin-left 40px; }blue;lpx solid green;5px Юрх;blue;lpx solid green;5px Юрх;.menu li, .button { display padding border background color margin-right border-radius -moz-box-shadow.menu li, .button { display padding border background color margin-right border-radius -moz-box-shadowinline;4px 6px;lpx solid #777;#ddd;#d04;8px;5px;2px 2px 2px #888inline;4px 6px;lpx solid #777;#ddd;#d04;8px;5px;2px 2px 2px #888-webkit-box-shadow 2px 2px 2px #888-webkit-box-shadow 2px 2px 2px #888box-shadowbox-shadow2px 2px 2px #888; }2px 2px 2px #888; }color border padding
margin-left 40px; }
margin-left 40px; }
blue;
lpx solid green;
5px Юрх;
blue;
lpx solid green;
5px Юрх;
.menu li, .button { display padding border background color margin-right border-radius -moz-box-shadow
.menu li, .button { display padding border background color margin-right border-radius -moz-box-shadow
inline;
4px 6px;
lpx solid #777;
#ddd;
#d04;
8px;
5px;
2px 2px 2px #888
inline;
4px 6px;
lpx solid #777;
#ddd;
#d04;
8px;
5px;
2px 2px 2px #888
-webkit-box-shadow 2px 2px 2px #888
-webkit-box-shadow 2px 2px 2px #888
box-shadow
box-shadow
2px 2px 2px #888; }
2px 2px 2px #888; }
.subhead {
font-weight:bold; }
.taken, .error {
col or:red; }
.available {
color:green; }
.whisper {
font-style:italic;
color :#006600; }
Ну вот, собственно, и все. Если вы создадите что-нибудь на основе этого кода или других примеров, приведенных в данной книге, или извлечете пользу из них каким-либо другим образом, я буду рад тому, что мне удалось вам в чем-то помочь, и спасибо за чтение данного издания.
Но перед тем, как закрыть книгу и приступить к практическому применению на просторах Всемирной паутины только что полученных навыков, пожалуйста, просмотрите следующие за этой главой приложения. В них содержится масса дополнительной информации, которая может оказаться полезной для вас.
Приложение А. Ответы на контрольные вопросы
Ответы на вопросы главы 1
Четырьмя компонентами, необходимыми для создания полностью динамических веб-страниц, являются веб-сервер (такой как Apache), язык сценариев на стороне сервера (РНР), база данных (MySQL) и язык сценариев на стороне клиента (JavaScript).
HTM L означает язык гипертекстовой разметки — HyperText Markup Language: сама веб-страница, включая текст и команды разметки.
Так же как практически все процессоры баз данных, MySQL воспринимает команды на структурированном языке запросов — Structured Query Language (SQL). SQL является способом общения с MySQL любого пользователя (а также РНР-программы).
Сценарии РН Р работают на сервере, а сценарии JavaScript — на машине клиента. У языка РНР есть средства общения с базой данных, позволяющие хранить и извлекать данные, но его средствами невозможно провести быстрое и динамическое изменение веб-страницы, просматриваемой пользователем. У языка JavaScript имеются совершенно противоположные достоинства и недостатки.
CSS означает каскадные таблицы стилей — Cascading Style Sheets: правила задания стилей и разметки, применяемые к элементам HTM L- документа.
Некоторые из этих технологий подконтрольны компаниям, которые собирают сведения об ошибках и занимаются их устранением, как и любые другие компании, производящие программные продукты. Но программы с открытым кодом зависят также от сообщества разработчиков, поэтому ваш отчет об ошибке может быть рассмотрен любым пользователем, хорошо разбирающимся в коде. Возможно, когда-нибудь и вам доведется исправлять ошибки, допущенные в инструментарии, имеющем открытый код.
Ответы на вопросы главы 2
WAM P означает Windows, Apache, MySQL и РНР, в то время как М в М АМ Р означает Мае вместо Windows, a L в LAM P — Linux. Все эти аббревиатуры ссылаются на полноценные решения для хостинга динамических веб-страниц.
Оба адреса, и 127.0.0.1, и http://localhost, являются способами ссылки на локальный компьютер. При правильной настройке WAM P или М АМ Р для вызова исходной страницы на локальном сервере в адресную строку браузера можно вводить любой из этих адресов.
FTP означает протокол передачи файлов — File Transfer Protocol. Программа FTP используется для передачи файлов между клиентом и сервером в обе стороны.
Чтобы обновить файлы, их нужно отправить на удаленный сервер с помощью FTP-программы, что может существенно увеличить время разработки, если это действие повторяется многократно на протяжении одного рабочего сеанса.
Специализированные редакторы программ обладают множеством автоматических функций и способны подсвечивать проблемные участки кода даже до его запуска на выполнение.
Ответы на вопросы главы 3
Тег, инициирующий интерпретацию PH P-кода, имеет вид <?php ... ?> и может быть сокращен до пары тегов <? ... ?>.
Для отдельной строки комментария можно воспользоваться сочетанием символов //, а комментарии, занимающие несколько строк, можно заключить в пары символов /*...*/.
Все P H P-инструкции должны заканчиваться точкой с запятой (;).
Все имена P H P-переменных, исключая имена констант, должны начинаться с символа $.
Переменные содержат значения, которые могут быть строкой, числом или другими данными.
Выражение Svariable =1 является инструкцией присваивания, а выражение $variable == 1 — инструкцией сравнения. Инструкцию Svariable = 1 следует использовать для присваивания значения переменной Svariable. Инструкцию Svariable == 1 необходимо применять в последующих строках программы для определения того, равно значение переменной Svariable единице или нет. Если, задумав выполнение сравнения, воспользоваться по ошибке инструкцией Svariable = 1, скорее всего, произойдут два нежелательных события: переменной Svariable будет присвоено значение 1 и будет неизменно возвращаться истинное значение, независимо от предыдущего значения этой переменной.
Дефисы зарезервированы для операторов вычитания. Если бы в именах переменных было разрешено использование дефисов, это затруднило бы интерпретацию такой конструкции, как $current-user, что в любом случае приводило бы к двусмысленности программного кода.
Имена переменных чувствительны к регистру букв. Поэтому $This_Variable и Sthis variable являются совершенно разными переменными.
Пробелы в именах переменных недопустимы, поскольку они собьют с толку парсер (синтаксический анализатор) РНР. Вместо них следует использовать знаки подчеркивания (_).
Чтобы перевести значение переменной из одного типа в другой, нужно сослаться на эту переменную, и РН Р автоматически преобразует ее тип.
Разница между ++$j и $j++ ни в чем не будет проявляться до тех пор, пока значение переменной $j не будет проходить проверку, присваиваться другой переменной или передаваться функции в качестве параметра. В таких случаях использование выражения ++$j приводит к увеличению значения $j на единицу до выполнения проверки или другой инструкции, а применение выражения $j++ приводит к тому, что сначала выполняется инструкция, а затем значение переменной $j увеличивается на единицу.
В большинстве случаев, когда не нужно соблюдать приоритетность, операторы &&и and являются взаимозаменяемыми. При необходимости соблюдения приоритетности у оператора &&более высокий уровень приоритета, чем у оператора and.
Чтобы создать многострочную команду echo или инструкцию присваивания, можно воспользоваться кавычками или конструкцией «< END ... END.
Значения констант нельзя переопределять, потому что по самой их сути, будучи единожды определенными, они сохраняют свое значение до прекращения работы программы.
Д л я изменения исходного предназначения кавычек можно воспользоваться* сочетанием \' для отключения одинарной кавычки или сочетанием \" для отключения двойной кавычки.
Команды echo и print похожи друг на друга, за исключением того, что print является функцией РН Р и воспринимает один аргумент, a echo является конструкцией, которая может воспринимать несколько аргументов.
Функции предназначены для выделения отдельных частей кода в самостоятельные изолированные блоки, на которые можно ссылаться по одному лишь имени функции.
Объявив переменную глобальной, ее можно сделать доступной для всех частей РНР-программы.
Если данные сгенерированы внутри функции, их можно передать всей остальной программе путем возвращения значения с помощью инструкции return или изменения значения глобальной переменной.
При объединении строки с числом получается еще одна строка.
Ответы на вопросы главы 4
В РН Р TRUE представляет значение 1, a FALSE представляет значение NULL, которое можно рассматривать как «ничто» и которое выводится на экран как пустая строка.
Самые простые формы выражений - это литералы, к которым относятся числа и строки, и переменные, которые просто вычисляются в самих себя.
Разница между унарными, бинарными и трехкомпонентными операторами состоит в количестве необходимых им операндов (им требуется соответственно один, два и три операнда).
Наилучший способ установки собственной приоритетности операторов состоит в заключении тех подвыражений, которым нужно придать более высокий уровень приоритета, в круглые скобки.
Взаимосвязанность операторов относится к направлению обработки выражения (слева направо или справа налево).
Оператор тождественности используется в том случае, когда нужно обойти присущее РН Р автоматическое изменение типа операнда (которое называется также приведением типов).
К условным инструкциям относятся i f, switch и оператор ?.
Чтобы пропустить выполнение текущей итерации цикла и перейти к следующей итерации, используется инструкция continue.
Циклы, в которых используется инструкция for, считаются более мощными, чем циклы whi 1е, потому что они поддерживают два дополнительных параметра, которые управляют работой цикла.
БольшинствосусловныхJвыражений f вEинструкциях > i f и whi 1е являются ? литералами (или булевыми выражениями), и поэтому они инициируют выполнение кода только в том случае, если вычисляются как TRUE. Числовые выражения инициируют выполнение, когда их значение является ненулевым. Строковые выражения инициируют выполнение, когда вычисляются как непустая строка. Значение NULL вычисляется как ложное, поэтому оно не инициирует выполнение кода.
Ответы на вопросы главы 5
Использование функций избавляет от необходимости многократного копирования или переписывания одних и тех же фрагментов кода, объединяя набор инструкций и позволяя использовать для его вызова простое имя.
По умолчанию функции могут возвращать одно значение. Но применение массивов, ссылок и глобальных переменных позволяет возвращать любое количество значений.
При обращении к переменной по имени, например при присваивании ее значения другой переменной или передаче ее функции, значение переменной копируется.
При этом изменение значения копии не приводит к изменению исходного значения. Но если вы ссылаетесь на переменную, используется только указатель (или ссылка) на ее значение, поэтому на одно и то же значение ссылается сразу несколько имен. Изменение значения но ссылке приводит к изменению исходного значения.
Под областью видимости понимаются части программы, из которых может быть получен доступ к переменной. Например, переменная с глобальной областью видимости может быть доступна из всех частей РНР-программы.
Чтобы включить один файл в состав другого файла, можно воспользоваться инструкциями i ncl ude или requi re или их более безопасными вариантами i ncl ude once и requi re_once.
Функция является набором инструкций, на который производится ссылка но имени и который может принимать и возвращать значения. Объект может состоять из нуля или нескольких функций (которые применительно к нему называются методами), а также из переменных (называемых свойствами объекта). Все они объединяются в одно образование.
Для создания в РН Р нового объекта используется ключевое слово new: Sobject =new Class.
Для создания подкласса используется ключевое слово extends, которое является частью следующей синтаксической конструкции: class Подкласс extends Родительский класс.
Чтобы при создании объекта вызвать инициализирующую часть кода, нужно внутри класса создать метод-конструктор_construct и поместить в него код.
Явного[)объявленияясвойствввнутрияклассаа неетребуется, посколькууониипослее первого же применения будут объявлены неявным образом. Но явное объявление считается правилом хорошего тона, потому что оно улучшает читаемость кода, упрощает его отладку и приносит особую пользу тем людям, которым приходится обслуживать ваш код.
Ответы на вопросы главы 6
Числовой массив может быть проиндексирован с помощью чисел или числовых переменных. Ассоциативный массив использует для индексации элементов буквенно-цифровые идентификаторы.
Основное преимущество от использования ключевого слова array состоит в том, что оно дает возможность присваивать массиву сразу несколько значений без повторения имени этого массива.
Как функция each, так и структура организации цикла foreach.. .as возвращают элементы из массива. Обе они приступают к работе с начала массива и увеличивают значение указателя на единицу, обеспечивая всякий раз возвращение следующего элемента, и обе они возвращают FALSE при достижении конца массива. Разница между ними в том, что функция each возвращает
только один элемент, поэтому она обычно помещается в цикл. Конструкция foreach.. .as уже является циклом, выполняемым снова и снова до тех нор, пока не закончатся элементы массива или пока цикл не будет прерван явным образом.
Для создания многомерного массива нужно элементам основного массива присвоить значения, представляющие собой дополнительные массивы.
Для подсчета количества элементов в массиве может использоваться функция count.
Функция explode предназначена для извлечения частей строки, которые разделены идентификатором, например, для извлечения слов, разделенных в предложении пробелами.
Чтобы вернуть внутренний указатель текущего элемента массива РНР па первый элемент этого массива, вызывается функция reset.
Ответы на вопросы главы 7
Для отображения числа с плавающей точкой следует использовать спецификатор преобразования И.
Для приема строки Happy Birthday и вывода строки **Нарру можно воспользоваться инструкцией printf: printf ("Г *7.5s". "Happy Birthday");.
Для выдачи информации из printf не в браузер, а в переменную следует воспользоваться альтернативной функцией sprintf.
Чтобы создать отметку времени Unix для времени и даты, представленных в виде «7:1 lam May 2nd, 2016», нужно воспользоваться командой Stimestamp = mktime(7, 11. 0. 5. 2. 2016):.
Чтобы открыть файл в режиме чтения и записи с усечением его размера и установкой указателя на начало файла, следует в функции fopen воспользоваться режимом доступа к файлу w+.
Для удаления файла file.txt следует применить PH P-команду uniink( ’file, txt');.
Для чтения целиком всего файла используется функция fi le_get_contents. Эта же функция, если ей предоставить U RL-адрес, позволяет прочитать файл из Интернета.
Сведения о файлах, загруженных на сервер, содержатся в ассоциативном РНР- массиве $_FILES.
Системные команды можно запускать с помощью PH P-функции ехес.
В XHTM L 1.0 тег <input type=file name=file size=10> должен быть заменен следующим тегом, в котором используется правильный синтаксис: <input type="file" name="file" size="10" />. Все параметры должны быть заключены в кавычки, а теги, не имеющие закрывающего тега, должны быть закрыты с использованием сочетания />.
Ответы на вопросы главы 8
В MySQL точка с запятой используется для разделения или для завершения команд. Если забыть ввести этот символ, MySQL выдаст приглашение и будет ожидать его ввода.
Для просмотра доступных баз данных следует набрать команду SHOWdatabases. Для просмотра таблиц, использующихся в базе данных, следует набрать команду SHOWtables. (Эти команды нечувствительны к регистру букв.)
Для создания нового пользователя применяется команда GRANT:
GRANT PRIVILEGES ON newdatabase.* TO 'newuser' IDENTIFIED BY ’newpassword';
Для просмотра структуры таблицы нужно набрать команду DESCRIBE имя_таб- лицы:.
Индекс в MySQL предназначен для существенного сокращения времени доступа к базе данных за счет поддержки индексов в одной или нескольких ключевых графах, в которых после индексации можно проводить быстрый поиск с целью обнаружения в таблице нужной строки.
Индекс FULLTEXT позволяется запросам, в которых используется естественный язык, находить ключевые слова, если они имеются в графе или графах, проиндексированных в режиме FULLTEXT. Способ поиска во многом похож на тот, для которого используется поисковый механизм.
К стоповым относятся слова, имеющие настолько широкое распространение, что нет никакого смысла включать их в индекс FULLTEXT или использовать при поиске. Но все же они должны участвовать в поиске, когда входят в состав большой строки, заключенной в кавычки.
По сути, спецификатор SELECT DISTINCT воздействует только на отображение, выбирая всего лишь одну строку и исключая все ее дубликаты. Спецификатор GROUP BY не исключает, а объединяет все строки, у которых есть одно и то же значение в графе. Поэтому спецификатор GROUP BY хорошо справляется с осуществлением таких операций, как COUNT, ведущей подсчеты в группе строк. А спецификатор SELECT DISTINCT для таких целей не годится.
Для возвращения только тех строк, в которых в каком-нибудь месте графы author таблицы classics содержится слово Langhorne, используется следующая команда:
SELECT * FROM classics WHERE author LIKE "J&Langhorne*";
Дляяобъединения двух таблицJууних8должна бытьIобщейЙхотя Ябы одна графа, например номер ID или, как в случае с таблицами cl assi cs и customers, графа i sbn.
Ответы на вопросы главы 9
Под словом отношение (relationship) понимается связь между двумя элементами данных, обладающими какими-нибудь взаимными ассоциациями, например:
книга и ее автор или книга и покупатель, который ее приобрел. Реляционная (то есть учитывающая отношения) база данных, например MySQL, специализируется на хранении и извлечении подобных отношений.
К процессу удаления повторяющихся данных и оптимизации таблиц применяется термин нормализация.
Существуют три правила первой нормальной формы: 1) в ней не должно быть никаких повторяющихся граф, содержащих одни и те же типы данных; 2) все графы должны содержать только одно значение; 3) для уникальной идентификации каждой строки должен использоваться первичный ключ.
Чтобы таблица отвечала требованиям второй нормальной формы, графы, в которых данные повторяются в нескольких строках, должны быть перемещены в собственные таблицы.
При отношении «один ко многим» первичный ключ из таблицы со стороны «один» должен быть добавлен в качестве отдельной графы (внешнего ключа) к таблице на стороне «многие».
Для создания базы данных, в которой имеется отношение «многие ко многим», нужно создать промежуточную таблицу, содержащую ключи из двух других таблиц. В результате эти две другие таблицы смогут ссылаться друг на друга посредством третьей таблицы.
Чтобы инициировать MySQL-транзакцию, используется либо команда BEGIN, либо команда START TRANSACTION. Для прекращения транзакции и отмены всех действий выдается команда ROLLBACK. Для завершения транзакции и совершения всех действий выдается команда COMMIT.
Для изучения подробностей работы запроса можно воспользоваться командой EXPLAIN.
Для создания в файле publ i cati ons. sql резервной копии базы данных publ i cati ons можно воспользоваться следующей командой:
mysqldump -u user -ppassword publications > publications.sql
Ответы на вопросы главы 10
Для подключения к базе данных MySQL используется стандартная РНР-функ- ция mysql connect.
Работу функции mysql resul t нельзя признать оптимальной в том случае, если запрашивается более одной ячейки, поскольку она извлекает из базы данных только одну ячейку и поэтому должна вызываться многократно, в то время как функция mysql fetch row извлекает сразу всю строку.
Как правило, метод POST больше подходит для отправки данных формы, чем метод GET, поскольку поля отправляются непосредственным образом, а не добавляются к U RL-адресу. У этого метода есть несколько преимуществ, особенно в том, что он не позволяет вставлять поддельные данные в адресную строку браузера. (И тем не менее он не обеспечивает полноценной защиты от вставки поддельных данных.)
Для определения последнего значения, введенного в столбец со свойством AUT0 INCREMENT, нужно воспользоваться функцией mysql_insert_id.
Для отключения опасных символов в строке и подготовки ее к использованию в MySQL применяется PH P-функция mysql_real_escape_string.
Атака внедрения при межсайтовом скриптинге может быть предотвращена с помощью функции html enti ties.
Ответы на вопросы главы 11
Для передачи данных формы PH P-программе GET-методом используется массив $_GET, а POST-методом — массив $_P0ST.
Свойство registerglobals включалось по умолчанию в версиях РНР, предшествующих версии 4.2.0. Недостаток такой настройки заключался в том, что отправленные данные полей формы автоматически присваивались в качестве значений PH P-переменным, открывая лазейку в системе безопасности для потенциальных взломщиков, которые могли попытаться внедрить в РНР-код свои вставки, инициализируя переменные по своему усмотрению.
Несмотря на то что оба этих поля при заполнении формы воспринимают текст, разница между ними заключается в том, что в текстовом поле может содержаться только одна строка, а в текстовой области может быть несколько строк и в ней осуществляется перенос слов на новую строку.
Для предоставления пользователю возможности выбора в веб-форме трех взаимоисключающих вариантов следует воспользоваться переключателями, поскольку флажки допускают множественный выбор.
Для отправки из веб-формы группы значений при использовании только одного имени поля следует воспользоваться не обычным именем поля, а именем массива с квадратными скобками, например choicest]. Тогда каждое значение помещается в массив, длина которого будет соответствовать количеству отправленных элементов.
Чтобы отправить данные поля формы, не отображая их на экране браузера, следует поместить эти данные в скрытое поле, в котором используется параметр type="hidden”.
Элемент формы, а также сопровождающий его текст или графику можно заключить в теги <1 abel >и </1 abel > и гарантировать тем самым возможность выбора щелчком кнопки мыши на всей области объединенного элемента.
Чтобы превратить HTM L в такой формат, который может быть отображен на экране, но не сможет интерпретироваться браузером как код HTM L, используется PH P-функция html enti ties.
Ответы на вопросы главы 12
Cookie должны быть переданы до кода HTM L веб-страницы, потому что они отсылаются в виде составной части заголовков.
Для сохранения cookie на машине веб-браузера применяется функция set_ cookie.
Для удаления cookie нужно его выдать заново, но при этом установить срок истечения действия на уже прошедшее время.
При использовании HTTP-аутентификации имя пользователя и пароль сохраняются в элементах массива $_SERVER[' PHP_AUTH_USER' ] и $_SERVER[' PHP_AUTH_PW' ].
Функция md5 считается мощным средством защиты, потому что это односторонняя функция, превращающая строку в 32-символьное шестнадцатеричное число, не поддающееся обратному преобразованию, из-за чего установить прежнее значение строки практически невозможно.
При подмешивании произвольных данных, прежде чем преобразовать строку с помощью функции md5, в нее добавляются лишние символы (известные только программисту). Благодаря этому лобовая словарная атака практически обречена на провал.
РНР-сессия — это группа уникальных для текущего пользователя переменных.
Чтобы инициировать PH P-сессию, используется функция session start.
Хищение сессии происходит в том случае, когда взломщик каким-то образом добывает ID существующей сессии и пытается ее захватить.
Фиксация сессииIзаключается$в попытке принудительного навязывания серверу ID сессии, не позволяя ему создавать собственный идентификатор.
Ответы на вопросы главы 13
В качестве контейнера для кодаJavaScript используются теги <scri pt> и </scri pt>
По умолчанию информация, выводимая кодомJavaScript, будет добавлена к той части документа, в которой находится этот код. Если он находится в заголовке, она будет добавлена в заголовок, а если в теле документа, то в тело.
КодJavaScript, принадлежащий другому источнику, может быть включен в ваш документ либо путем копирования и последующей вставки этого кода, либо более распространенным способом — включением его в качестве составляющей тега<script src='filename. js’>.
ВJavaScript эквивалентом РНР-команд echo и pri nt служит функция (или метод) document.write.
Чтобы создать комментарий в JavaScript, нужно перед текстом однострочного комментария поставить символы //, а многострочный комментарий поместить между символами /* и */.
Оператором объединения строк в JavaScript служит знак «плюс» (+).
Внутри функции JavaScript можно определить переменную, имеющую локальную область видимости, поставив в начале первой инструкции присваивания значения этой переменной ключевое слово var.
Чтооь: во всех основных браузерах отобразить U RL-адрес, присвоенный ссылке. имеющей ID со значением thislink, можно воспользоваться двумя следующими командами:
:::jnert.. a h te(document,getElementById('thislink' ) .href)
. write(thisl ink. href)
Команды для загрузки предыдущей страницы, ссылка на которую хранится в истории браузера, имеют следующий вид:
hi story.backf)
hi story.go(-1)
Для замены текущего документа главной страницей веб-сайта oreilly.com можно воспользоваться следующей командой:
document.location.href = 'http://oreilly.com'
Ответы на вопросы главы 14
Наиболее заметной разницей между булевыми значениями в РН Р и в JavaScript является то, что РНР распознает ключевые слова TRUE, true, FALSE и false, а вJavaScript поддерживаются только ключевые слова true и fal se. Кроме того, в РНР TRUE имеет значение 1, a FALSE имеет значение NULL, а вJavaScript они представлены значениями true и fal se, которые могут быть возвращены в виде строчных значений.
Разница между унарными, бинарными и трехкомпонентными операторами состоит в количестве необходимых для них операндов (им требуется соответственно один, два и три операнда).
Наилучший способ установки собственной приоритетности операторов состоит в заключении части выражения, которая должна быть вычислена в первую очередь, в круглые скобки.
Оператор тождественности используется в тех случаях, когда нужно обойти присущее JavaScript автоматическое изменение типа операнда.
Самыми простыми формами выражений являются литералы (такие как числа и строки) и переменные, которые просто вычисляются в самих себя.
К трем условным инструкциям относятся i f, switch и оператор ?.
Большинство условных выражений в инструкциях i f и whi 1е являются литералами (или булевыми выражениями), и поэтому они инициируют выполнение кода только в том случае, если вычисляются как true. Числовые выражения инициируют выполнение, когда результатом их вычисления является ненулевое значение. Строковые выражения инициируют выполнение, когда вычисляются как непустая строка. Значение NULL вычисляется как ложное, поэтому оно не инициирует выполнение кода.
Циклы, в которых используется инструкция for, считаются более мощными по сравнению с циклами whi 1е, потому что они поддерживают два дополнительных параметра, которые управляют работой цикла.
Инструкция with воспринимает в качестве своего параметра объект. При се использовании объект указывается только один раз, а затем внутри блока устанавливается связь этого объекта с каждой инструкцией.
Чтобы>1изящноэобрабатыватььошибки, нужноэвоспользоватьсяяфункциеййtry, которая отправит любую возникшую ошибку соответствующей функции catch, где вы можете устранить ошибку или предоставить альтернативный код. Код можно также привязать к событию опеггог.
Ответы на вопросы главы 15
Имена функций и переменных в JavaScript чувствительны к регистру используемых в них букв. Имена Count, count и COUNT представляют совершенно разные переменные.
Для создания функции, которая воспринимает и обрабатывает неограниченное число параметров, доступ к параметрам организуется через массив arguments, являющийся составной частью всех функций.
Один из способов возвращения из функции нескольких значений заключается в помещении всех этих значений в массив с последующим его возвращением.
При определении класса для ссылки на текущий объект используется ключевое слово this.
Методы класса не обязательно должны определяться внутри определения самого класса. Если метод класса определяется за пределами конструктора, имя этого метода должно быть присвоено объекту this внутри определения класса.
Новый объект создается с помощью ключевого слова new.
Доступность свойства или метода может быть обеспечена всем объектам класса без тиражирования этого свойства или метода внутри объекта путем использования ключевого слова prototype для создания единственного экземпляра, который затем передается по ссылке всем объектам класса.
Для создания многомерного массива внутри основного массива помещается подмассив.
Синтаксис, который следует использовать для создания ассоциативного массива, имеет структуру ключ : значение, заключенную в фигурные скобки, как показано в следующем примере:
assocarray = {"forename" "Paul", "surname" "McCartney".
"group" : "Beatles"}
Инструкция, используемаяядляясортировкииввубывающемVIпорядкеемассива,1, состоящего из чисел, будет иметь следующий вид:
numbers.sort(function(a.b){return b a}) )
Ответы на вопросы главы 16
Отправить данные формы на проверку до их отправки на сервер можно добавлением к тегу <form> JavaScript-метода onSubmit. Функция проверки должна возвращать true, если форма должна быть отправлена на сервер, или fal se, если она не прошла проверку.
Для проверки соответствия строки регулярному выражению в JavaScript используется метод test.
Регулярные выражения, соответствующие символам, не использующимся в словах, могут иметь вид /[A\w]/, /[\W]/, /[xa-zA-Z0-9_]/ и т. д.
Для проверки соответствия как слову fox, так и слову fix можно воспользоваться регулярным выражением /f[oi ]х/.
Регулярное выражение, соответствующее любому отдельному слову, за которым следует символ, не использующийся в словах, может иметь следующий вид: /\w+\W/g.
Функция JavaScript, использующая регулярное выражение для проверки наличия слова fox в строке The quick brown fox, может иметь следующий вид:
document.write(/fox/.test("The quick brown fox"))
Функция PHP, использующая регулярное выражение для замены всех экземпляров слова the в строке The cow jumps over the moon словом my, может иметь следующий вид:
$s=preg_replace(,7the/i". "my". "The cow jumps over the moon"):
Для предварительного заполнения полей формы значениями используется ключевое слово HTM L value, которое помещается в тег <input> и принимает там форму val ие="значение".
Ответы на вопросы главы 17
Необходимость создания функции для создания новых объектов XMLHTTPRequest обусловлена тем, что браузеры Microsoft используют два разных метода их создания, а все остальные основные браузеры применяют третий, совершенно иной метод. За счет создания функции, тестирующей используемый браузер, можно обеспечить работу кода на всех основных браузерах.
Цель применения конструкции try. . .catch состоит в настройке на перехват ошибки при выполнении кода, находящегося внутри инструкции try. Если его выполнение вызовет ошибку, то вместо выдачи общей ошибки будет выполнен код блока catch.
У объекта XMLHTTPRequest имеется шесть свойств и шесть методов (см. табл. 17.1 и 17.2).
Определить завершение Ajax-вызова можно но значению 4, которое примет свойство readyState.
Когда Ajax-вызов успешно завершится, принадлежащее объекту свойство status получит значение 200.
Значение, возвращенное успешно завершенным Ajax-вызовом, содержится в свойстве responseText объекта XMLHTTPRequest.
DOM -дерево, созданное из XML, возвращенного успешно завершенным Ajax- вызовом, содержится в свойстве responseXML объекта XMLHTTPRequest.
Для указания функции обратного вызова, обрабатывающей Ajax-ответы, нужно присвоить имя функции свойству onreadystatechange объекта XMLHTTPRequest. Можно также воспользоваться безымянной встроенной функцией.
Для инициирования Ajax-запроса вызывается метод send объекта XMLHTTPRequest.
Основное различие между Ajax GET- и POST-запросами состоит в том, что GET- запросы присоединяют данные к U RL-адресу, а POST-запросы передают данные в качестве параметра метода send и требуют правильной формы заголовков, отправляемых в первоочередном порядке.
Ответы на вопросы главы 18
Чтобы импортировать одну таблицу стилей в другую, используется инструкция @import, например: @import url('styles.css'):.
Чтобы импортировать таблицу стилей в документ, можно воспользоваться HTM L-тегом <link />, например: <link rel =' sty 1esheet * type='text/css ' href=’styles.css’ />.
Чтобы непосредственно встроить стиль в элемент, используется атрибут sty 1е, например: <di v styl е=' col or: bl ue: ’>.
Разница между идентификатором CSS и классом CSS заключается в том, что идентификатор применяется только к одному элементу, а класс можно применить ко многим элементам.
В CSS-объявлениях для имен идентификаторов в качестве префикса используется символ решетки (#), а для имен классов применяется символ точки (.), например#myid и .myclass.
Точка с запятой в CSS используется в качестве разделителя объявлений.
Чтобы добавить к таблице стилей комментарий, его нужно поставить между маркерами открытия и закрытия комментария, /* и */.
В CSS указать на соответствие любому элементу можно с помощью универсального селектора *
Чтобы выбрать в CSS группу разных элементов и (или) типов элементов, между каждым элементом, идентификатором, классом и т. д. нужно ставить запятую.
Чтобы одно CSS-объявление из пары, имеющей одинаковые приоритеты, получило преимущество над другим, к нему добавляется объявление {important, например: р{ col or :#"ffOOOO important: }.
Ответы на вопросы главы 19
CSS3-onepaTopi>i А, $ и * в указанном порядке соответствуют началу, концу и любой части строки.
Для указания размера фонового изображения используется свойство backgroundsize, например: background-size:800px 600рх:.
Радиус границы можно указать с помощью свойства border-radius, например: border-radius:20px;.
Перетекание текста по нескольким колонкам можно задать с помощью свойств column-count, column-gap и column-rule или их вариантов, характерных для того или иного браузера, например: column-count: 3; column-gap:lem: column-rule:lpx solid black:.
Четырьмя функциями, с помощью которых можно указать CSS-цвета, являются hsl, hsl a, rgb и rgba. Например: со1ог:гдЬа(0Я.60Я,40Я.0.4):.
Чтобы создать серую тень под каким-нибудь текстом, с диагональным отступом вправо и вниз на 5 пикселов и с размытостью 3 пиксела, можно воспользоваться следующим объявлением: text-shadow: 5рх 5рх Зрх #888:.
Показать с помощью многоточия, что текст усечен, можно с помощью следующего объявления: text-overf1ow: el 1i psi s :.
Чтобы включить в состав своей веб-страницы веб-шрифты Google, нужносначала их выбрать на веб-сайте http://google.com/webfbnts. Затем, если выбран, предположим, шрифт Lobster, он включается в тег <1 i nk>, например: <1 i nk href=' http: //fonts. googleapis.com/css7family4obster' />. Можно также сослаться на шрифт в CSS- объявлении, например в таком: hi { font-family:'Lobster'. arial. serif; }.
Для поворота объекта на 90° нужно воспользоваться CSS-объявлением transform: rotate(90deg);.
Чтобы11указатььпереходаобъектаатакиммобразом, чтобы>1прииизмененииклюбогоо из его свойств переход осуществлялся сразу в линейном режиме в течение половины секунды, нужно воспользоваться следующим объявлением: transi ti on: all .5s linear;.
Ответы на вопросы главы 20
Функция 0 возвращает объект по его идентификатору, функция S возвращает свойство стиля объекта, а функция С возвращает массив всех объектов, к которым обращается заданный класс.
Изменить CSS-атрибут объекта можно с помощью функции setAttri bute, например: myobject.setAttribute(' font-size'. '16pt'). Можно также изменить атрибут непосредственно (что обычно и делается), используя немного измененные имена там, где это требуется, например: myobject. fontSi ze = ' 16pt'. (Следует напомнить, что в JavaScript дефис зарезервирован для использования в качестве математического оператора, поэтому при обращении к CSS-свойству, в имени которого
используется дефис, этот дефис нужно опустить, а символ, за которым он следовал, указать в верхнем регистре.)
Свойствами, предоставляющими доступную в окне браузера ширину и высоту, являются window. innerHeight и window.innerWidth.
Задать какие-нибудь действия при прохождении указателя мыши над объектом, а затем при выходе за границы объекта можно, привязав код, совершающий эти действия к событиям onmouseover и onmouseout.
Для создания нового элемента нужно воспользоваться таким кодом, как, например, elem = document.createElement( 'span'), а для добавления нового элемента к DOM применяется такой код, как, например, document .body .appendChi ld(elem).
Чтобы сделать элемент невидимым, нужно установить для сто свойства vi s i bi 1i ty значение 'hidden' (а для возвращения ему видимости нужно задать значение 'visible'). Чтобы сжать элемент до нулевых размеров, следует выбрать для его свойства di spl ay значение ’ none' (а для восстановления его размеров — значение 'block').
Чтобы задать одиночное событие в будущем времени, нужно вызвать функцию setTimeout, передав ей код или имя функции для выполнения, и значение задержки в миллисекундах.
Для установки повторяющегося события через указанный интервал времени нужно вызвать функцию set Interval, передав ей код или имя функции для выполнения, и значение задержки между повторениями — в миллисекундах.
Чтобы освободить элемент от его места на веб-странице, позволив ему перемещаться, нужно установить для его свойства position значение relati i ve', 'absolute' или 'fixed'. Для восстановления элемента на его исходном месте значение этого свойства следует задать в ' stati с'.
Дляяполученияяскоростияанимациия 50 3 кадроввв3секундуунужнояустановитьI задержку между прерываниями равной 20 миллисекундам. Для вычисления этого значения необходимо разделить 1000 миллисекунд на желаемую скорост ь анимации.
Приложение Б.
Интернет-ресурсы
В этом приложении перечислены полезные веб-сайты, предоставляющие материал, использованный в данной книге, или другие ресурсы для совершенствования ваших веб-программ.
Веб-сайты, относящиеся к РНР
О http://codewalkers.com.
О http://developer.yahoo.com/php/.
О http://easyphp.org.
О http://forums.devshed.com.
О http://free-php.net.
О http://hotscripts.com/category/php/.
О http://htmlgoodies.com/beyond/php/.
О http://php.net.
О http://php.resourceindex.com.
О http://php-editors.com.
О http://phpbuilder.com.
О http://phpfreaks.com.
О http://phpunit.de.
О http://w3schools.com/php/.
О http://zend.com.
Веб-сайты, относящиеся к MySQL
О http://code.google.com/edu/toolsl01/mysql.html.
О http://launchpad.net/mysql/.
О http://mysql.com.
О http://php.net/mysql.
О http://planetmysql.org.
О http://sun.com/software/products/mysql/.
О http://sun.com/systems/solutions/mysql/resources.jsp.
О http://w3schools.com/PHP/php_mysql_intro.asp.
Веб-сайты, относящиеся к JavaScript
О http://developer.mozilla.org/en/JavaScript.
О http://dynamicdrive.com.
О http://javascript.about.com.
О http://javascript.internet.com.
О http://javascript.com.
О http://javascriptkit.com.
О http://w3schools.com/JS/.
О http://www.webreference.com/js/.
Веб-сайты, относящиеся к Ajax
О http://ajax.asp.net.
О http://ajaxian.com.
О http://ajaxmatters.com.
О http://developer, moziIla. org/еп/AJAX.
О http://developer.yahoo.com/yui/.
О http://dojotoolkit.org.
О http://jquery.com.
О http://mochikit.com.
О http://mootools.net.
О http://openjs.com.
О http://prototypejs.org.
О http://sourceforge.net/projects/clean-ajax.
О http://w3schools.com/Ajax/.
Веб-сайты с разнообразными ресурсами
О http://apachefriends.org.
О http://easyphp.org.
О http://eclipse.org.
О http://editra.org.
О http://fireftp.mozdev.org.
О http://sourceforge.net/projects/glossword/.
О http://mamp.info/en/.
О http://pear.php.net.
О http://programmingforums.org.
О http://putty.org.
О http://smarty.net.
О http://wampserver.com/en/.
Веб-сайты с ресурсами издательства O'Reilly
О http://onlamp.com.
О http://onlamp.com/php/.
О http://onlamp.com/onlamp/general/mysql.csp.
О http://oreilly.com/ajax/.
О http://oreilly.com/javascript/.
О http://oreilly.com/mysql/.
О http://oreilly.com/php/.
О http://oreillynet.com/javascript/.
Приложение В. MySQL's FULLTEXT Stopwords
В этом приложении содержится более 500 стоповых слов (stopwords), упоминавшихся в пункте «Создание индекса FU LLTEXT» раздела «Индексы» главы 8. Стоповыми называются слова, которые считаются настолько распространенными, что не могут представлять ценности для поиска или хранения в индексе FU LLTEXT Теоретически игнорирование этих слов слабо влияет на результаты большинства поисков в режиме FULLTEXTI, но позволяет существенно сократить объем и повысить эффективность работы баз данных MySQL. Здесь приведены слова в нижнем регистре, но этот перечень касается также слов, представленных в верхнем и смешанном регистрах.
А
As, able, about, above, according, accordingly, across, actually, after, afterwards, again, against, ain’t, all, allow, allows, almost, alone, along, already, also, although, always, am, among, amongst, an, and, another, any, anybody, anyhow, anyone, anything, anyway, anyways, anywhere, apart, appear, appreciate, appropriate, are, aren’t, around, as, aside, ask, asking, associated, at, available, away, awfully.
В
Be, became, because, become, becomes, becoming, been, before, beforehand, behind, being, believe, below, beside, besides, best, better, between, beyond, both, brief, but, by.
С
C’mon, c’s, came, can, can’t, cannot, cant, cause, causes, certain, certainly, changes, clearly, со, com, come, comes, concerning, consequently, consider, considering, contain, containing, contains, corresponding, could, couldn’t, course, currently.
D
Definitely, described, despite, did, didn’t, different, do, does, doesn’t, doing, don’t, done, down, downwards, during.
E
Each, edu, eg, eight, either, else, elsewhere, enough, entirely, especially, et, etc, even, ever, every, everybody, everyone, everything, everywhere, ex, exactly, example, except.
F
Far, few, fifth, first, five, followed, following, follows, for, former, formerly, forth, four, from, further, furthermore.
G
Get, gets, getting, given, gives, go, goes, going, gone, got, gotten, greetings.
H
Had, hadn’t, happens, hardly, has, hasn’t, have, haven’t, having, he, he’s, hello, help, hence, her, here, here’s, hereafter, hereby, herein, hereupon, hers, herself, hi, him, himself, his, hither, hopefully, how, howbeit, however.
I
I’d, i’ll, i’m, i’ve, ie, if, ignored, immediate, in, inasmuch, inc. indeed, indicate, indicated, indicates, inner, insofar, instead, into, inward, is, isn’t, it, it’d, it’ll, it’s, its, itself.
J
Just.
К
Keep, keeps, kept, know, knows, known.
L
Last, lately, later, latter, latterly, least, less, lest, let, let’s, like, liked, likely, little, look, looking, looks, ltd.
M
Mainly, many, may, maybe, me, mean, meanwhile, merely, might, more, moreover, most, mostly, much, must, my, myself.
N
Name, namely, nd, near, nearly, necessary, need, needs, neither, never, nevertheless, new, next, nine, no, nobody, non, none, noone, nor, normally, not, nothing, novel, now, nowhere.
О
Obviously, of, off, often, oh, ok, okav, old, on, once, one, ones, only, onto, or, other, others, otherwise, ought, our. ours, ourselves, out, outside, over, overall, own.
P
Particular, particularly, per, perhaps, placed, please, plus, possible, presumably, probably, provides.
Q
Que, quite, qv.
R
Rather, rd, re, really, reasonably, regarding, regardless, regards, relatively, respectively, right.
S
Said, same, saw, say, saying, savs, second, secondly, see, seeing, seem, seemed, seeming, seems, seen, self, selves, sensible, sent, serious, seriously,, seven, several, shall, she, should, shouldn’t, since, six, so, some, somebody, somehow, someone, something, sometime, sometimes, somewhat, somewhere, soon, sorry, specified, specify, specifying, still, sub, such, sup, sure.
T
T’s, take, taken, tell, tends, th, than, thank, thanks, thanx, that, that’s, thats, the, their, theirs, them, themselves, then, thence, there, there’s, thereafter, thereby, therefore, therein, theres, thereupon, these, they, they’d, they’ll, they’re, they’ve, think, third, this, thorough, thoroughly, those, though, three, through, throughout, thru, thus, to, together, too, took, toward, towards, tried, tries, trulv, try, trying, twice, two.
U
(Jn, under, unfortunately, unless, unlikely, until, unto, up, upon, us, use, used, useful, uses, using, usually.
V
Value, various, very, via, viz, vs.
W
Want, wants, was, wasn’t, way, we, we’d, we’ll, we’re, we’ve, welcome, well, went, were, weren’t, what, what’s, whatever, when, whence, whenever, where, where’s, whereafter, whereas, whereby, wherein, whereupon, wherever, whether, which, while, whither, who, who’s, whoever, whole, whom, whose, why, will, willing, wish, with, within, without, won’t, wonder, would, would, wouldn’t.
Y
Yes, yet, you, you’d, you’ll, you’re, you’ve, your, yours, yourself, yourselves.
Z
Zero.
Приложение Г. Функции MySQL
За счет функций, встроенных в MySQL, существенно сокращается время выполнения сложных запросов и упрощается их конструкция. Если есть желание получить более полную информацию обо всех доступных функциях, можно обратиться к материалам веб-сайтов по следующим U RL-адресам:
О строковые функции: http://dev.mysql.eom/doc/refman/5.0/en/string-functions.html;
О функции даты и времени: http://dev.mysql.com/doc/refman/5.0/en/date-and-time- functions.html.
Но для ускорения далее приводятся описания наиболее востребованных функций MySQL.
Строковые функции
CONCAT0
CONCATCstrl, strZ, ...)
Возвращает результат объединения str1> str2 и любых других параметров (или NULL, если все аргументы имеют значение NULL). Если какой-нибудь из аргументов имеет двоичную форму (binary), то результат будет возвращен в виде двоичной последовательности, в противном случае результат будет в виде строки, не имеющей двоичного формата. Следующий код возвращает строку MySQL:
SELECT CONCATC Му'. ’S'. ’QL ’):
CONCAT_WS()
C0NCAT_WS(separator, strl. str2. ...))
Эта функция работает так же, как и C0NCAT, за исключением того, что она между объединяемыми элементами вставляет разделитель separator. Если разделитель имеет значение NULL, то результат тоже будет NULL, но значения NULL могут использоваться
и в качестве других аргументов, которые в таком случае будут пропущены. Следующий код возвращает строку Truman, Harry, S:
SELECT CONCAT_WS(1.' 'Truman', 'Harry', 'S'):
LEFT()
LEFT(str, len)
Возвращает len самых левых символов из строки str (или NULL, если какой-нибудь из аргументов имеет значение NULL). Следующий код возвращает строку Chri s:
SELECT LEFT('Christopher Columbus’. '5'):
RIGHTO
RIGHT(str, len)
Возвращает len самых правых символов из строки str (или NULL, если какой-нибудь из аргументов имеет значение NULL). Следующий код возвращает строку Col umbus:
SELECT RIGHT('Christopher Columbus'. '8');
MID()
MID(str. pos, len)
Возвращает до len символов из строки str, начиная с позиции pos. Если аргумент len опущен, то возвращаются все символы до конца строки. Для аргумента pos можно использовать отрицательное значение, тогда он будет представлять позицию символа, вычисляемую с конца строки. Первой позицией в строке является 1. Следующий код возвращает строку stop:
SELECT MID('Christopher Columbus'. '6'. '4'):
LENGTHQ
LENGTH(str)
Возвращаетдлину строки str в байтах. Имейте в виду, что при встрече многобайтовых символов учитываются все их байты. Если нужно узнать число символов в строке, следует воспользоваться функцией CHARJ.ENGTH. Следующий код возвращает значение 15:
SELECT LENGTHС Mark Zuckerberg');
LPAD()
LPAD(str. len, padstr)
Возвращает строку str, дополненную до длины len символами padstr, добавляемыми в начало строки. Если строка str длиннее, чем len, то строка возвращается усеченной до len символов. Этот код:
SELECT LPADCJanuary’ . '8'. ' '):
SELECT LPADC February'. ’8'. ' ’):
SELECT LPADC March ’, '8'. ' ’);
SELECT LPADCApriV. '8'. ' ’);
SELECT LPADC May'. ’8'. ' ');
вернет следующие строки:
January
February
March
April
May
Обратите внимание на то, как все строки были дополнены до восьми симво-
лов.
RPAD
RPAD(str. len, padstr)
Эта функция работает так же, как и функция LPAD, за исключением того, что она возвращает строку, дополненную символами не слева, а справа. Следующий код возвращает строку Hi!!! :
SELECT RPADCHi ’ . '5' . '! '):
LOCATE()
LOCATE(substr. str. pos)
Возвращает позицию первой же встреченной подстроки substr в строке str. Если функции передан параметр pos, то поиск начинается с позиции pos. Если substr не была найдена в строке str, возвращается значение 0.
Следующий код возвращает значения 5 и 11, поскольку при вызове первой функции возвращается позиция первого встреченного слова unit, а вторая функция начинает поиск только с седьмого символа и поэтому возвращает позицию второго появления этого слова в строке:
SELECT LOCATE(’uniIt'. 'Community unit');
SELECT LOCATE С uniIt'. 'Community uniIt'. 7):
LOWER()
LOWER(str)
Эта функция является прямой противоположностью функции UPPER. Она возвращает строку str, все буквы которой переводятся в нижний регистр. Следующий код возвращает строку queen el i zabeth i i :
SELECT L0WER('Queen Elizabeth I D :
UPPER()
UPPER(str)
[)тл функция является прямой противоположностью функции LOWER. Она возвращает строку .vfr; все буквы которой переводятся в верхний регистр. Следующий код возвращает строку I CAN’T HFLP SHOUTING:
SbLEC.T UPPER(' I can't help shouting’):
QUOTE()
QUOTE(sir)
Возвращает строку, помещенную в кавычки, которая будет готова к использованию в инструкции SQL, для чего в ней отключаются все неоднозначно толкуемые символы. Возвращаемая строка заключается в одинарные кавычки, а перед всеми имеющимися в пей одинарными кавычками, обратными слешами, ASCII-символами NU L и Ctrl-Z устанавливается обратный слеш. Если аргумент имеет значение NULL, возвращаемое значение является словом NULL, не заключенным в кавычки. Код примера возвращает следующую строку:
' I\ ’mhungry'
Обратите внимание на то, как символ одинарной кавычки (') был заменен символами /'.
SELECT QUOTE("I'm hungry”):
REPEAT()
REPEAK str. count)
Возвращает строку, содержащую count копий строки str. Если count меньше единицы, возвращается пустая строка. Если какой-нибудь из параметров имеет значение NULL, возвращается NULL. Следующий код возвращает строки Но Но Но и Merry Christmas:
SELECT REPFATC'Но'. 3). 'Merry Christmas':
REPLACE()
REPLACE(str. from, to)
Возвращает строку str, в которой все появления строки from заменены строкой to. При проведении поиска подстроки from поиск и замена чувствительны к регистру. Следующий код возвращает строку Cheeseburger and Coke:
SELECT REPLACE('Cheeseburger and Fries'. ’Fries'. 'Coke'):
TRIM()
TRWdspecifier remove FROM] str)
Возвращает строку str, из которой удалены все префиксы и суффиксы, имеющие значение remove. В качестве specifier может быть указан один из спецификаторов BOTH (оба), LEADING (ведущие) или TRAILING (замыкающие). Если спецификатор не указан, предполагается спецификатор BOTH. Строка remove является необязательным параметром, и если она опущена, то удаляются пробелы. Следующий код возвращает строки No Padding и Не! 1о_:
SELECT TRIMC No Padding ');
SELECT TRIMC LEADING FROM '_Hello_');
LTR1M() и RTRIM()
LTRIM(str) и RTRIM(str)
Функция RTRIM возвращает строку str, у которой удалены все пробелы в начале, а функция RTRIM делает то же самое, но в отношении замыкающих пробелов. Следующий код возвращает строки «No Padding » и « No Padding»:
SELECT LTRIMC No Padding ');
SELECT RTRIMC No Padding '):
Функции для работы с датами
Даты являются важной составной частью большинства баз данных. При проведении финансовых транзакций должны записываться даты, при повторных выставлениях счетов должны учитываться сроки истечения действия кредитных карт и т. д. Поэтому неудивительно, что в MySQL имеется широкий спектр функций для работы с датами.
CURDATEO
CURDATEO
Возвращает текущую дату в формате YYYY-MM-DD или YYYMMDD в зависимости от того, в каком контексте используется функция, строковом или числовом. 2 мая 2016 года следующий код вернул бы значения 2016-05-02 и 20160502:
SELECT CURDATEO;
SELECT CURDATEO + 0;
DATE()
DATE(expr)
Извлекает дату из выражения DATETIM E, переданного в аргументе ехрг. Следующий код возвращает значение 1961-05-02:
SELECT DATEC1961-05-02 14:56:23’):
DATE_ADD()
DATE _ ADD№te, INTERVAL expr unit)
Возвращает результат добавления выражения expr, в котором к дате применяется единица измерения unit. Аргумент date является стартовой датой или значением DATETIME, а expr для отрицательных интервалов может начинаться с минуса (-). В табл. ПГ.1 показаны типы интервалов, поддерживаемые и ожидаемые в качестве значений expr. Обратите внимание на приведенные в этой таблице примеры, которые показывают, в каких случаях значение expr должно быть заключено в кавычки, чтобы база данных MySQL смогла их правильно интерпретировать (при любых сомнениях лучше добавить кавычки, которые не помешают работе).
Таблица ПГ.1. Ожидаемые значения expr
Тип
Ожидаемое значение expr
Пример
MICROSECOND
МИКРОСЕКУНДЫ
111111
SECOND
СЕКУНДЫ
11
MINUTE
МИНУТЫ
11
HOUR
ЧАСЫ
11
DAY
ДНИ
11
WEEK
НЕДЕЛИ
11
MONTH
МЕСЯЦЫ
11
QUARTER
КВАРТАЛЫ
1
YEAR
ГОДЫ
И 11
SECOND_MICROSECOND
'СЕКУНДЫ.МИКРОСЕКУНДЫ'
11.22
MINUTE_MICROSECOND
'МИНУТЫ.МИКРОСЕКУНДЬГ
11.22
MINUTE_SECOND
'МИНУТЫ :СЕКУНДЬГ
'11:22'
HOUR_MICROSECOND
'ЧАСЫ. М И КРОСЕКУНДЫ'
11.22
HOUR_SECOND
'ЧАСЫ: М И НУТЫ: СЕКУНДЫ'
'11:22:33'
HOUR_MINUTE
'ЧАСЫ:МИНУТЫ!
'11:22'
DAY_MICROSECOND
'ДНИ.МИКРОСЕКУНДЫ'
11.22
DAY_SECOND
'ДНИ ЧАСЫ: МИ НУТЫ: СЕКУНДЫ1
'11 22:33:44'
DAY.MINUTE
'ДНИ ЧАСЫ:МИНУТЫ'
'Иi 22:33'
DAYJHOUR
'ДНИ ЧАСЫ'
'Иi 22'
YEAR_MONTH
'ГОДЫ-МЕСЯЦЫ'
'11-2'
Для вычитания интервала дат можно также воспользоваться функцией DATE_SUB. Но функциями DATE ADD и DATE_SUB можно вообще не пользоваться, поскольку MySQL допускает использование непосредственных арифметических операций с датами. Данный код:
SELECT DATE_ADD('1975-01-01’ . INTERVAL 77 DAY);
SELECT DATE_SUBC 1982-07-04' . INTERVAL '3-11' YEAR_MONTH);
SELECT '2016-12-31 23:59:59' + INTERVAL 1 SECOND:
SELECT '2000-01-01' - INTERVAL 1 SECOND:
возвращает следующие значения:
1975-03-19
1978-08-04
2017-01-01 00:00:00
1999-12-31 23:59:59
Обратите внимание на то, как в последних двух командах используются непосредственные арифметические операции с датами без обращения к функциям.
DATE_FORMAT()
DATE_FORMAT(ctate. format)
Эта функция возвращает значение даты date, отформатированное в соответствии со строкой форматирования format. В табл. ПГ.2 показаны спецификаторы, которые могут использоваться в строке форматирования format. Учтите, что символ %нужно ставить так, как показано в таблице, то есть впереди каждого спецификатора. Следующий код возвращает заданную дату и время в виде «Thursday May 4th 2016 03:02 AM»:
SELECT DATE_FORMAT '2016-0 5-04 03:02:01’. 'XW *M XD *Y Xh:X1 Xp');
Таблица ПГ.2. Спецификаторы, использующиеся в функции DATE_FORMAT
Спецификатор
Описание
%а
Сокращенное название дня недели (Sun — Sat)
%Ь
Сокращенное название месяца (Jan — Dec)
%с
Месяц в числовом формате (0-12)
%D
День месяца с английским суффиксом (Oth, 1st, 2nd, 3rd...)
%d
День месяца в числовом формате (00-31)
%е
День месяца в числовом формате (0-31)
%f
Микросекунды (000000-999999)
%Н
Час (00-23)
%h
Час (01-12)
%I
Час (01-12)
%i
Минуты в числовом формате (00-59)
%j
День года (001-366)
%k
Час(0-23)
%l
Час (1-12)
%M
Название месяца (January — December)
%m
Месяц в числовом формате (00-12)
%p
AM или РМ (до или после полудня)
%r
Время в 12-часовом формате (hh:mm:ss, за которыми следует AM или РМ)
%S
Секунды (00-59)
%s
Секунды (00-59)
%T
Время в 24-часовом формате (hh:mm:ss)
%U
Неделя (00-53), когда первым днем недели считается воскресенье
%u
Неделя (00-53), когда первым днем недели считается понедельник
Продолжение &
Таблица ПГ.2 (продолжение)
Спецификатор
Описание
%v
Неделя (00-53), когда первым днем недели считается воскресенье; используется со спецификатором %Х
%v
Неделя (00-53), когда первым днем недели считается понедельник; используется со спецификатором %х
%W
Название дня недели (Sunday — Saturday)
%w
День недели (0 — воскресенье — 6 — суббота)
%X
Год для недели, в которой первым днем считается воскресенье, в числовом формате, четыре цифры; используется вместе с %V
%x
Год для недели, в которой первым днем считается понедельник, в числовом формате, четыре цифры; используется вместе с %v
%Y
Год в числовом формате, четыре цифры
%y
Год в числовом формате, две цифры
%%
Символ % как таковой
DAY()
DAY(date)
Возвращает для даты date день месяца в диапазоне от 1 до 31 или возвращает О для дат, содержащих нулевую составляющую дней, таких как «0000-00-00» или «2010-00-00». Для возвращения таких же значений можно также воспользоваться функцией DAY0FM0NTH. Следующий код возвращает значение 3:
SELECT DAY('2016-02-03');
DAYNAME()
DAYNAME(date)
Возвращает название дня недели для даты date. Следующий код возвращает строку Saturday:
SELECT DAYNAME('2016-02-03'):
DAYOFWEEK()
DAYOFWEEK(date)
Возвращает номер дня недели для даты date в диапазоне от 1 для воскресенья до 7 для субботы. Следующий код возвращает значение 7:
SELECT DAY0FWEEKC2016-02-03’):
DAYOFYEARO
DAYOFYEARWcJte)
Возвращает день года для даты date в диапазоне от 1 до 366. Следующий код возвращает значение 34:
SELECT DAY0FYEARC2016-02-03'):
LAST_DAY()
LAST_DAY(date)
Возвращает последний день месяца для заданной в формате DATETIM E даты date. Если аргумент имеет неправильный формат, возвращает NULL. Данный код:
SELLC1 LAST_DAYC2016-02-03’);
SELECI LAST_DAYC2016-03-11'):
SFLtCl I AST_DAYC2016-04-26');
возвращает следующие значения:
2016-02-29
2016-03 31
2016-04 30
Оправдывая все ожидания, функция корректно возвращает 29-й день февраля, 31-й день марта и 30-й день апреля 2011 года.
MAKEDATEO
MAKFDATF(year, dayofyear)
Возвращает дату, соответствующую предоставленному году year и дню года dayofyear. Если dayofyear имеет нулевое значение, результат будет иметь значение NULL. Следующий код возвращает дату «2016-10-01»:
SELECT MAKEDATE(2016.274):
MONTHO
MONTH(date)
Возвращает месяц даты date в диапазоне от 1 до 12 с января по декабрь. Для дат, у которых часть, относящаяся к месяцу, имеет нулевое значение, например «0000-00- 00» или «2012-00-00», возвращает нуль. Следующий код возвращает значение 7:
SELECT M0NTHC2016-07-11'):
MONTHNAME()
MONTHNAME(tMe)
Возвращает полное название месяца для даты date. Следующий код возвращает строку July:
SELECT MONTHNAME('2016-07-11');
SYSDATEO
Возвращает текущую дату и время в виде значения в формате либо YYYY-M M -D D HH:MM:SS, либо YYYYM M DDHHMIM SS, в зависимости оттого, в каком контексте
используется функция, строковом или числовом. Аналогичным образом работает функция NOW, за исключением того, что она возвращает время и дату только па момент запуска текущей инструкции, а функция SYSDATK возвращает время и дату именно на момент вызова самой функции. 19 декабря 2016 года следующий код вернет значения 2016-12-19 19:11:13 и 20161219191113:
SH tCT SYSDAII ( ').
SH к: I SYSDAI t() +():
YEAR()
YEAR(ofdte)
Возвращает год для латы date в диапазоне от 1000 до 9999 или 0 для нулевой даты. Следующий код возвращает гол 1999:
Sf I К! I YFAR ( ’ 1999 08-07'):
WEEK()
WEEKtdate I . mode])
Возвращает номер недели для даты date. Если функции передан необязательный параметр mode, возвращенный номер недели будет модифицирован в соответствии с описанием, приведенным в табл. ПГ.З. Можно также воспользоваться функцией WEEK0FYEAR, работа которой эквивалентна работе функции WEEKпри использовании режима 3. Следующий код возвращает номер недели, равный 14:
SELECT WEEK(’2016-04-04'. 1):
Таблица ПГ.З. Режимы работы, поддерживаемые функцией WEEK
Режим
Первый день недели
Диапазон
Когда неделя 1 - это первая неделя...
0
Воскресенье
0-53
С воскресеньем в этом году
1
Понедельник
0-53
С более чем тремя днями в этом году
2
Воскресенье
1-53
С воскресеньем в этом году
3
Понедельник
1-53
С более чем тремя днями в этом году
4
Воскресенье
0-53
С более чем тремя днями в этом году
5
Понедельник
0-53
С понедельником в этом году
6
Воскресенье
1-53
С более чем тремя днями в этом году
7
Понедельник
1-53
С понедельником в этом году
WEEKDAY()
WEEKDAY(date)
Возвращает номер дня недели для даты date в диапазоне от 0 (понедельник) до
6 (суббота). Следующий код возвращаем значение 1:
SFI ЕСТ WFFKDAYC'2016 04-04’):
Функции для работы с временем
Иногда приходится работать не с датой, а с временем, и MySQL предоставляет для этого большое количество функций.
CURTIME()
CURTIMEO
Возвращает текущее время в виде значения, имеющего формат HH:MM:SS или HHMMSS.uuuuuu в зависимости от того, в каком контексте используется функция, строковом или числовом. Значение дается с учетом текущего часового пояса. При текущем времени 11:56:23 следующий код возвращает значения 11:56:23 и 115623.000000:
SELECT CURTIMEO
SELECT CURTIMEO + 0:
HOURO
Н0Ш time)
Возвращает значение часа для времени time. Следующий код возвращает значение 11:
SELECT H0URC11:56:23'):
MINUTE()
МШТЕШте)
Возвращает значение минуты для времени time. Следующий код возвращает значение 56:
SELECT MINUTE('11:56:23'):
SECOND()
SEC0ND(t7'me)
Возвращает значение секунды для времени time. Следующий код возвращает значение 23:
SELECT SEC0NDC'11:56:23'):
МАКЕТ1МЕ()
МАКЕТIME(hour, minute, second)
Возвращает значение времени, вычисленное на основе аргументов часа hour, минуты minute и секунды second. Следующий код возвращает время 11:56:23:
SELECT МАКЕТ1МЕШ. 56. 23):
TIMEDIFFO
TIMEDIFF(exprI. expr2)
Возвращает разницу между exprl и expr2 (exprl - ехрг2) в виде значения времени. Оба аргумента, exprl и ехрг2, должны быть выражениями одинакового типа в формате TIME или DATETIME. Следующий код возвращает значение 01:37:38:
SELECT TIMEDIFFC2000-01-01 01:02:03'. '1999-12-31 23:24:25'):
UNIX_TIMESTAMP()
UNIX_TIMESTAMP([da£e])
Эта функция при вызове без необязательного аргумента date возвращает в формате беззнакового целого числа то количество секунд, которое прошло с нуля часов, нуля минут и нуля секунд универсального синхронного времени (UTC) 1 января 1970 года. Если функции передается параметр date, тогда возвращаемое значение содержит количество секунд, которое прошло с 1970 года по указанную дату date. Следующий код возвращает значение 946684800 (количество секунд, которое прошло до начала нового тысячелетия), а затем возвращает отметку времени TIMESTAMP, представляющую текущее время системы Unix на момент запуска функции:
SELECT UNIX_TIMESTAMP('2000-01-01');
SELECT UNIX_TIMESTAMP();
FROM_UNIXTIME()
FR0M_UNI XTI ME(t/л ix_t imes tamp [, format])
Возвращает параметр unbcjkimestamp либо в формате строки YYYY-M M-DD HH:MM:SS, либо в формате числа YYYYIMMDDHHMMSS.uuuuuu в зависимости от того, в каком контексте используется функция, строковом или числовом. Если задан необязательный параметр format, результат форматируется в соответствии со спецификаторами, показанными в табл. 8.11. Следующий код возвращает строки «2000-01-01 00:00:00» и «Saturday January 1st 2000 12:00 AM»:
SELECT FR0MJJNIXTIME(946684800);
SELECT FR0M_UNIXTIME(946684800. 'XWSM %D XY %h:%i Xp');