Знакомство с РНР! Серьезно, а существовала ли разумная жизнь до возникновения Web? Тот, кто вырос в наше время, может в этом усомниться. Пришествие Интернета заложило основу для развития коммуникационных технологий, не имеющих аналогов в истории человечества. И молодежь, и пожилые люди идут в Интернет за покупками, знаниями и общением. Всего за несколько лет произошла масса событий: талантливые предприниматели зарабатывали целые состояния, возникали и рушились корпоративные империи, процветала экономика целых стран - и все благодаря провидческой идее Тима Бернерса-Ли и его коллег о том, что в один прекрасный день мир объединится при помощи гиперссылок. Конечно, за последние десять лет Всемирная паутина прошла долгий путь - из вспомогательного инструмента научных исследований она превратилась в конгломерат, в котором задействованы прямо-таки головокружительные объемы информации. Возможно, самым важным фактором в развитии Web стала именно простота публикации информации в Web. Даже обладая минимальными познаниями, человек может установить на своем компьютере текстовый редактор, FTP-клиент и web-браузер и открыть свое «представительство» в мире электронной информации. Впрочем, с процессом создания и сопровождения динамических, крупномасштабных web-сайтов дело обстоит сложнее. При разработке профессионального web-сервиса обычно приходится решать такие задачи, как взаимодействие с пользователем, поиск в базе данных и обеспечение доступности на разных платформах, что заметно усложняет вашу работу. Если вы хотите больше узнать о том, как реализуются и используются на практике подобные возможности, - эта диск написан для вас ГЛАВА 1 Знакомство с РНР Последние пять лет ознаменовались фантастическим развитием Интернета и новых способов общения между людьми. На переднем крае этого явления находится World Wide Web (WWW). Ежедневно в этой новой коммуникационной среде открываются тысячи новых сайтов, а потребителям предлагаются новые виды услуг. Вместе с бурным развитием рынка появился огромный спрос на новые технологии и разработчиков, владеющих ими. Если вы читаете этот абзац, вероятно, вы уже являетесь или скоро станете web-разработчиком. Впрочем, какой бы ни была ваша профессия, вы выбрали эту книгу потому, что слышали о замечательной новой технологии - РНР. В этой главе вы познакомитесь с языком РНР, получите представление о его истории и возможностях, а также основную информацию, необходимую для разработки сайтов с поддержкой РНР. Надеюсь, приведенные примеры пробудят ваш энтузиазм и наглядно покажут, какие перспективы РНР открывает перед вами и вашей организацией. Вы узнаете, как установить и настроить программное обеспечение РНР на компьютерах с Linux/UNIX или Windows и как внедрить РНР в HTML. К концу главы вы будете готовы к тому, чтобы углубленно заняться изучением других важных аспектов языка РНР. Итак, зажигайте свет, включайте свою любимую музыку и устраивайтесь поудобнее. Пришла пора познакомиться с тем, что станет одним из важнейших пунктов в вашем резюме, - с программированием на РНР. Краткая история История РНР начинается с 1995 года, когда независимый программист-контрактник по имени Расмус Лердорф (Rasmus Lerdorf) написал сценарий Perl/CGI для подсчета количества посетителей сайта, прочитавших его онлайновое резюме. Его сценарий решал две задачи: регистрацию данных посетителя и вывод количества посетителей на web-странице. Развитие WWW еще только начиналось, никаких специальных средств для решения этих задач не было, и к автору хлынул поток сообщений с вопросами. Лердорф начал бесплатно раздавать свой инструментарий, названный Personal Home Page (РНР) или Hypertext Processor (гипертекстовый процессор). Шумный успех инструментария РНР заставил Лердорфа приступить к разработке расширений РНР. Одно из расширений преобразовывало данные, введенные на форме HTML, в символические переменные, что позволяло экспортировать их в другие системы. Чтобы добиться поставленной цели, Лердорф решил в дальнейших разработках перейти с Perl на С. Расширение существующего инструментария РНР привело к появлению РНР 2.0, или PHP-FI (Personal Home Page - Form Interpretator). В усовершенствовании версии 2.0 принимали участие программисты со всего мира. Новая версия РНР пользовалась исключительной популярностью, и вскоре образовалась основная команда разработчиков. Они сохранили исходную концепцию внедрения программного кода прямо в HTML и переписали заново механизм лексического анализа, что привело к появлению РНР 3.0. К моменту выхода версии 3.0 в 1997 году свыше 50 000 пользователей применяли РНР для улучшения своих web-страниц. В 1997 году было решено, что сокращение РНР должно означать не «Personal Home page», а «РНР Hypertext Processor» В течение следующих двух лет стремительное развитие РНР продолжалось. В язык добавлялись сотни новых функций, а количество пользователей стремительно росло. В начале 1999 года служба Netcraft (http://www.netcraft.com) сообщила о том, что, по минимальным оценкам, число пользователей РНР превысило 1 000 000, в результате чего РНР стал одним из самых популярных сценарных языков в мире. В начале 1999 года было объявлено о предстоящем выходе РНР 4.0. Хотя одной из сильнейших сторон РНР была эффективность выполнения сценариев, при первоначальных разработках не предполагалось, что на базе РНР будут строиться крупномасштабные приложения. По этой причине была начата работа над более устойчивым механизмом лексического анализа, больше известным под названием Zend (http://www.zend.com). Работа шла быстро и завершилась 22 мая 2000 года выпуском РНР версии 4.0. Кроме лексического анализатора Zend, компания Zend Technologies (Израиль) распространяет оптимизатор Zend, который повышает выигрыш в быстродействии от применения лексического анализатора Zend. Тесты показывают, что ускорение работы программы в результате использования оптимизатора составляет от 40 до 100 %. За дополнительной информацией обращайтесь на сайт Zend. На момент написания этой книги, по данным Netcraft (http://www.netcraft.com), программное обеспечение РНР было установлено более чем в 3,6 миллиона доменов. Будущее РНР выглядит светлым, поскольку продукт продолжает активно использоваться как на крупных web-сайтах, так и на компьютерах отдельных пользователей. РНР лучше всего охарактеризовать как работающий на стороне сервера встроенный язык сценариев Web, позволяющий разработчикам быстро и эффективно строить динамические web-приложения. С позиций грамматики и синтаксиса РНР напоминает язык программирования С, хотя разработчики не постеснялись включить в него средства из других языков, в том числе из Perl, Java и C++. Среди ценных заимствованных возможностей - поддержка регулярных выражений, мощные средства работы с массивами, объектно-ориентированная методология и обширная поддержка работы с базами данных. При написании приложений, выходящих за рамки традиционной, статической методологии разработки web-страниц (то есть HTML), РНР также может послужить ценным инструментом для создания и управления динамическим содержанием, который используется наряду с JavaScript, стилями, WML (Wireless Markup Language) и другими полезными языками. Благодаря наличию сотен стандартных функций РНР в состоянии решить практически любую задачу, которая может придти в голову разработчику. В нем имеется обширная поддержка создания графики и операций с ней, математических вычислений, средств электронной коммерции и таких популярных технологий, как XML (Extensible Markup Language), ODBC (Open Database Connectivity) и Macromedia Shockwave. Широкий выбор возможностей избавляет от необходимости рутинной и непростой работы по подключению сторонних модулей, поэтому многие разработчики со всего мира останавливают свой выбор на РНР. Одним из главных достоинств РНР является тот факт, что он внедряется прямо в HTML-код, поэтому программисту не приходится писать программу с множеством команд для простого вывода HTML. Код HTML и РНР можно чередовать по мере необходимости. РНР позволяет написать фрагмент следующего вида: <? print "Hello world!"; ?> Сообщение "Hello world!" выводится в заголовке web-страницы. Интересно то, что команда print внутри конструкции, которая обычно называется экранирующими последовательностями РНР (), представляет собой законченную программу. Ни длинного кода инициализации, ни включения библиотек - программа состоит лишь из того кода, который непосредственно решает поставленную задачу! Конечно, для выполнения сценариев РНР необходимо предварительно установить и настроить программное обеспечение РНР на сервере. Этот процесс описан в разделе «Загрузка и установка РНР/Apache» настоящей главы. Разделу предшествуют фрагменты из отзывов нескольких пользователей, выступающих в пользу РНР, с кратким обзором языка и его истории. Но прежде чем браться за процесс установки, мы познакомимся с некоторыми характеристиками РНР. Этой теме посвящен следующий раздел. Характеристики РНР Как вы, вероятно, уже поняли, главным фактором при проектировании языка РНР является практичность. РНР должен предоставить программисту средства для быстрого и эффективного решения поставленных задач. Практический характер РНР обусловлен пятью важными характеристиками: традиционностью; простотой; эффективностью; безопасностью; гибкостью. Существует еще одна «характеристика», которая делает РНР особенно привлекательным: он распространяется бесплатно! Традиционность Язык РНР кажется знакомым программистам, работающим в разных областях. Многие конструкции языка позаимствованы из Си Perl, а нередко код РНР практически неотличим от того, что встречается в типичных программах С или Pascal. Это заметно снижает начальные усилия при изучении РНР. Простота Сценарий РНР может состоять из 10 000 строк или из одной строки - все зависит от специфики вашей задачи. Вам не придется подгружать библиотеки, указывать специальные параметры компиляции или что-нибудь в этом роде. Механизм РНР просто начинает выполнять код после первой экранирующей последовательности (). Если код имеет правильный синтаксис, он исполняется в точности так, как указал программист. Эффективность Эффективность является исключительно важным фактором при программировании для многопользовательских сред, к числу которых относится и WWW. В РНР 4.0 был реализован механизм выделения ресурсов и обеспечена улучшенная поддержка объектно-ориентированного программирования, а также средства управления сеансом. В последней версии появился и механизм подсчета ссылок (reference counting), предотвращающий выделение лишней памяти. Безопасность РНР предоставляет в распоряжение разработчиков и администраторов гибкие и эффективные средства безопасности, которые условно делятся на две категории: средства системного уровня и средства уровня приложения. Средства безопасности системного уровня В РНР реализованы механизмы безопасности, находящиеся под управлением администраторов; при правильной настройке РНР это обеспечивает максимальную свободу действий и безопасность. РНР может работать в так называемом безопасном режиме (safe mode), который ограничивает возможности применения РНР пользователями по ряду важных показателей. Например, можно ограничить максимальное время выполнения и использование памяти (неконтролируемый расход памяти отрицательно влияет на быстродействие сервера). По аналогии с cgi-bin администратор также может устанавливать ограничения на каталоги, в которых пользователь может просматривать и исполнять сценарии РНР, а также использовать сценарии РНР для просмотра конфиденциальной информации на сервере (например, файла passwd). Средства безопасности уровня приложения В стандартный набор функций РНР входит ряд надежных механизмов шифрования. РНР также совместим с многими приложениями независимых фирм, что позволяет легко интегрировать его с защищенными технологиями электронной коммерции (e-commerce). Другое преимущество заключается в том, что исходный текст сценариев РНР нельзя просмотреть в браузере, поскольку сценарий компилируется до его отправки по запросу пользователя. Реализация РНР на стороне сервера предотвращает похищение нетривиальных сценариев пользователями, знаний которых хватает хотя бы для выполнения команды View Source. Тема безопасности настолько важна, что ей посвящена целая глава. За подробной информацией о средствах безопасности РНР обращайтесь к главе 16. Гибкость Поскольку РНР является встраиваемым (embedded) языком, он отличается исключительной гибкостью по отношению к потребностям разработчика. Хотя РНР обычно рекомендуется использовать в сочетании с HTML, он с таким же успехом интегрируется и в JavaScript, WML, XML и другие языки. Кроме того, хорошо структурированные приложения РНР легко расширяются по мере необходимости (впрочем, это относится ко всем основным языкам программирования). Нет проблем и с зависимостью от браузеров, поскольку перед отправкой клиенту сценарии РНР полностью компилируются на стороне сервера. В сущности, сценарии РНР могут передаваться любым устройствам с браузерами, включая сотовые телефоны, электронные записные книжки, пейджеры и портативные компьютеры, не говоря уже о традиционных PC. Программисты, занимающиеся вспомога-тельными утилитами, могут запускать РНР в режиме командной строки. Поскольку РНР не содержит кода, ориентированного на конкретный web-сервер, пользователи не ограничиваются определенными серверами (возможно, незнакомыми для них). Apache, Microsoft IIS, Netscape Enterprise Server, Stronghold и Zeus - РНР работает на всех перечисленных серверах. Поскольку эти серверы работают на разных платформах, РНР в целом является платформенно-незави-симым языком и существует на таких платформах, как UNIX, Solaris, FreeBSD и Windows 95/98/NT. Наконец, средства РНР позволяют программисту работать с внешними компонентами, такими как Enterprise Java Beans или СОМ-объекты Win32. Благодаря этим новым возможностям РНР занимает достойное место среди современных технологий и обеспечивает масштабирование проектов до необходимых пределов. Бесплатное распространение Стратегия Open Source наделала немало шуму в программной отрасли. Распространение исходных текстов программ в массах оказало несомненно благотворное влияние на многие проекты, в первую очередь - Linux, хотя и успех проекта Apache сильно подкрепил позиции сторонников Open Source. Сказанное относится и к истории создания РНР, поскольку поддержка пользователей со всего мира оказалась очень важным фактором в развитии проекта РНР. Принятие стратегии Open Source и бесплатное распространение исходных текстов РНР оказало неоценимую услугу пользователям. Вдобавок, отзывчивое сообщество пользователей РНР является своего рода «коллективной службой поддержки», и в популярных электронных конференциях можно найти ответы даже на самые сложные вопросы. В следующем разделе «Рекомендации пользователей» приведены свидетельства трех видных профессионалов в области web-разработок. Из них становится ясно, почему они считают РНР такой замечательной технологией. Рекомендации пользователей «Мы в течение долгого времени поддерживали личные контакты с некоторыми разработчиками РНР и вели с ними обширную переписку. Когда у разработчиков РНР возникали какие-то проблемы, относящиеся к MySQL, мы всегда были готовы помочь им в поиске решения. Кроме того, мы включили в MySQL несколько новых возможностей лишь для того, чтобы улучшить его интеграцию с РНР. Результатом наших усилий стало то, что MySQL превосходно работает с РНР, - и мы позаботимся о том, чтобы это положение сохранилось и в будущем!» Майкл «Монти» Видениус (Michael «Monty» Widenius), разработчик MySQL http://www.mysql.com «Выбор РНР для реализации mp3.lycos.com был обусловлен несколькими причинами. Главной причиной стали сжатые сроки работы над проектом - ведь РНР ускоряет процесс разработки. Другой причиной была высокая эффективность - мы перешли от 0 к 1,4 миллиона посещений в сутки, и РНР с этим прекрасно справился. Третья причина заключалась в том, что я твердо знал: если на стадии тестирования с повышенной нагрузкой в РНР обнаружатся какие-либо ошибки, я смогу их самостоятельно исправить, поскольку РНР распространяется вместе с исходными текстами». Стиг Баккен (Stig Bakken), FAST Search & Transfer ASA http://www.fast.no «Я использовал РНР с первых дней, еще с версии PHP/FI 1.x. Мне понравилось, что я могу обрабатывать формы и настраивать страницы «на ходу» при помощи такого простого языка. Вместе с потребностями моей компании развивался и РНР. В наши дни РНР обладает исключительно богатыми возможностями. Мы используем его практически во всех создаваемых web-сайтах, включая 32bit.com и DevShed.com. Мы даже воспользовались им в Info West для реализации службы поддержки, управления учетными записями и отслеживания портов. Эволюция РНР и признание его мировым сообществом - классический пример успешного ведения проекта с открытыми исходными текстами. Широта взглядов создателей, поддержка сообщества и хорошее сопровождение кодовой базы привели РНР к успеху, о котором многие коммерческие проекты могут лишь мечтать. Я с оптимизмом смотрю в будущее РНР и рекомендую каждому web-разработчику попробовать его в деле. Возможно, вы, как и я, уже не расстанетесь с ним». Рэнди Косби (Randy Cosby), президент nGenuity, Inc. DevShed http://www.devshed.com Вводный пример Пример, приведенный в листинге 1.1, наглядно показывает, как легко РНР интегрируется с HTML-кодом. Листинг 1.1. Создание динамической страницы РНР <? print $site_title; ?> Greetings, $user_name!
"; ?> На рис. 1.1 показано, как выглядит сценарий при выполнении в браузере. Рис. 1.1. Результаты выполнения сценария в браузере Неплохо, правда? Я уверен, что читатель уже перебирает в уме новые возможности. Но не будем торопиться - возможно, вам еще придется установить и настроить РНР на своем компьютере. Этой теме посвящены следующие разделы. Загрузка РНР/Apache Прежде чем следовать дальше, я рекомендую потратить немного времени на загрузку, установку и настройку РНР и web-сервера на вашем компьютере. Хотя РНР совместим с разными web-серверами, я предполагаю, что вы используете Apache - во-первых, это самый популярный web-сервер на сегодняшний день, во-вторых, он чаще всего работает с РНР. Впрочем, в целом процессы установки для разных web-серверов имеют много общего. Поставку РНР можно загрузить с официального сайта РНР или с любого из «зеркальных» сайтов по всему миру. Самый свежий список «зеркальных» сайтов находится по адресу http://www.php.net. При загрузке РНР можно выбрать один из двух форматов: исполняемый формат Win32; исходный текст. Исполняемый формат Win32 предназначен для пользователей Windows 95/98/ NT/2000. Хотя исходный текст можно откомпилировать и на платформе Windows, для большинства пользователей это необязательно. Впрочем, если вы настаиваете на компиляции (кстати, в книге этот процесс не рассматривается), вам понадобится компилятор Visual C++ одной из последних версий. За подробностями компиляции обращайтесь по адресу http://www.php.net/ version4/win32build.php. Установка исполняемых файлов Win32 описана далее в этой главе. Пользователям других систем придется самостоятельно откомпилировать исходный текст программы. Хотя многих новичков эта перспектива приводит в ужас, на самом деле это довольно просто. Возможно, вас интересует, распространяется ли РНР в формате RPM (RedHat Package Manager)? Да, распространяется, хотя эти пакеты не представлены на официальном сайте РНР. За инструкциями и дополнительной информацией о местонахождении различных поставок обращайтесь к материалам электронных конференций. Обобщенный процесс компиляции рассматривается далее. Зайдите на сайт http://www.php.net и загрузите с него ту поставку, которая лучше всего отвечает вашим потребностям. Время загрузки зависит от типа и скорости подключения. Кроме того, имеется возможность получить документацию. Я настоятельно рекомендую выбрать самую новую версию. На момент издания книги последней устойчиво работающей версией был РНР 4.0.3. Конечно, пакет РНР постоянно развивается, и номер версии непременно изменится. Я рекомендую загрузить самую свежую надежную версию продукта. Если вы еще не установили сервер Apache, вам также следует выбрать его последнюю версию. Пакеты находятся в каталоге http://www.apache.org/dist/binaries, содержащем подкаталоги для разных операционных систем. Загрузите вариант, соответствующий вашим потребностям. Подробное описание настройки РНР для всех существующих платформ и web-серверов выходит за рамки этой книги, поэтому я уделю основное внимание серверу Apache. Независимо от того, какой web-сервер вы собираетесь использовать, я рекомендую прочитать дальнейшие разделы, посвященные настройке, - вы получите некоторое представление о тех общих проблемах, с которыми можете столкнуться. Установка новых программ нередко превращается в непростое испытание для новичков. Однако разработчики РНР предприняли дополнительные усилия для того, чтобы установка РНР проходила относительно просто. В следующих разделах перечислены действия, которые необходимо выполнить для установки и настройки РНР в Win32 и на других платформах. В дальнейших главах вы познакомитесь с сервером баз данных MySQL, и на примере этого популярного продукта будет продемонстрирован процесс интеграции web-приложений с базами данных. Чтобы поэкспериментировать с этими примерами, вам придется установить пакет MySQL (http://www.mysql.com). MySQL, как и РНР, существует в версиях для Windows и для других платформ. Хотя в документации MySQL приведены подробные инструкции по установке, возможно, вам стоит предварительно просмотреть начало главы 11, где приводится общая информация о сервере баз данных MySQL. Установка и настройка Предполагается, что к настоящему моменту вы успешно загрузили РНР и Apache. Следующий шаг - выбор способа установки. Для компьютеров, не использующих систему Windows, существует три варианта: двоичный файл CGI, статический модуль Apache и динамический модуль Apache. Скорее всего, вы не захотите строить РНР в виде двоичного файла CGI. Более того, построение РНР в виде серверного модуля имеет некоторые преимущества, поэтому я уделю внимание построению РНР в виде статического и динамического модуля. Главное различие между этими двумя вариантами заключается в том, что при любых изменениях в статическом модуле РНР придется заново компилировать и Apache, и РНР, а изменения в динамическом модуле РНР потребуют компиляции только РНР, без сервера. На компьютерах с системой Windows РНР может устанавливаться в виде либо двоичного файла CGI, либо статического модуля Apache. На этот раз я опишу построение двоичного файла CGI, потому что пользователи Windows обычно используют вместо Apache другие web-серверы (например, Microsoft Internet Information Server или Microsoft Personal Web Server). CGI-версия легко интегрируется с этими серверами. Хотя описанный процесс установки относится к РНР/Apache, установка для перечисленных выше web-серверов выполняется практически так же. Как говорилось ранее, РНР4 поддерживает разнообразные web-серверы, в том числе AOL Server, Netscape Enterprise Server, Microsoft IIS, Zeus и многие другие. Впрочем, я ограничусь описанием установки для сервера Apache. За подробными инструкциями о том, как установить РНР для других серверов, обращайтесь к документации РНР по адресу http://www.php.net. Системы, не входящие в семейство Windows Независимо от того, какой вариант был выбран, установка начинается с распаковки архивов. Для распаковки выполните два простых действия: Выполните следующие команды: gunzip apache_1.3.9.tar.gz gunzip php-4.0.0.tar.gz После завершения распаковки остаются файлы с расширением *.tar. Извлеките файлы поставки из архивов: tar -zxvf apache_1.3.x.tar tar -zxvf php-4.0.x.tar С этого момента начинается основной процесс установки. Модуль Apache Установка РНР в виде модуля Apache выполняется довольно просто. Ниже подробно описаны все необходимые действия: Перейдите в каталог Apache: cd apache_1.3.x Настройте Apache. Выберите путь по своему усмотрению, но помните, что за ним не должна следовать косая черта: ./configure -prefix=[путь] Перейдите в каталог РНР; настройте, соберите и установите поставку. Параметр -with-config-file-path задает каталог, в котором будет находиться файл конфигурации РНР. Обычно этот файл находится в каталоге /usr/local/lib, но вы можете выбрать другой каталог по своему усмотрению: ./configure -with-apache=../apache_1.3.x -with-config-file-path=[путь] make make install Вернитесь в каталог Apache. Теперь вы можете изменить конфигурацию, собрать и установить Apache. Параметр -other-configuration-options относится к любым специальным параметрам конфигурации, которые вы хотели бы передать web-серверу Apache. Данная тема выходит за рамки книги. Полное описание параметров приведено в документации Apache: ./configure -activate-module=src/modules/php4/libphp4.a -other-configuration-options make make install На последнем этапе происходит редактирование файла Apache httpd.conf. Одни модификации относятся к Apache, другие необходимы для того, чтобы сценарии РНР распознавались и передавались web-серверу. Сначала найдите такую строку: ServerName new.host.name Приведите ее к следующему виду: ServerName localhost Затем найдите строки #AddType application/x-httpd-php .php .php4 #AddType application/x-httpd-php-source .php .phps Чтобы файлы с поддержкой РНР правильно работали на сервере, эти строки необходимо раскомментировать. Для этого достаточно удалить знак фунта (#) в начале каждой строки. Сохраните файл и поднимитесь в иерархии каталогов на один уровень вверх. Запустите сервер Apache следующей командой: ./bin/apachectl start Voila! PHP и Apache готовы к работе. Для проверки сохраните приведенный ниже фрагмент в файле phpinfo.php в корневом каталоге документов Apache - каталоге htdocs, находящемся в установочном каталоге Apache: Откройте файл в браузере на сервере. В окне появляется длинный перечень сведений о конфигурации РНР. Вы успешно установили РНР в виде модуля Apache. Динамический модуль Apache Динамические модули удобны тем, что они позволяют обновлять поставку РНР без перекомпиляции web-сервера. Apache рассматривает поддержку РНР как один из своих многочисленных модулей вроде ModuleRewrite или ModuleSpelling. Этот вариант особенно хорош в ситуациях, когда в РНР позднее будет добавляться поддержка новых возможностей - например, шифрования. Все, что вам придется сделать - переконфигурировать/откомпилировать РНР с поддержкой шифрования, и вы сможете немедленно использовать новую возможность в web-приложениях. Процесс установки описан ниже: Перейдите в каталог Apache: cd apache_1.3.x Настройте Apache. Выберите путь по своему усмотрению, но помните, что за ним не должна следовать косая черта. Параметр -other-configuration-options относится к любым специальным параметрам конфигурации, которые вы хотели бы передать web-серверу Apache. Данная тема выходит за рамки книги. Полное описание параметров приведено в документации Apache. ./configure -prefix=[путь] -enable-module=so -other-configuration-options Соберите сервер Apache. После ввода команды make на экран выводится серия сообщений: make Установите сервер Apache. После ввода команды make install на экран выводится следующая серия сообщений. После успешного завершения установки на экране появляется соответствующее сообщение: make install Если предыдущие действия прошли без ошибок, можно переходить к редактированию файла httpd.conf. Этот файл находится в подкаталоге conf каталога, указанного на шаге 2. Откройте файл в текстовом редакторе и найдите строку ServerName new.host.name Приведите ее к следующему виду: ServerName local host Перейдите в тот каталог, где находится пакет РНР. Настройте, соберите и установите РНР. В параметрах указывается путь к каталогу, содержащему файл apxs, - подкаталогу bin каталога, путь к которому был указан на шаге 2: ./configure -with-apxs=[путь/k/apxs] make make install Откройте файл Apache httpd.conf для очередной модификации. Для обеспечения правильного лексического анализа входящих запросов на файлы с поддержкой РНР расширение должно совпадать с одним из расширений, указанных в файле конфигурации сервера Apache, httpd.conf. Файл содержит параметры, которые могут изменяться по усмотрению администратора; некоторые из них имеют прямое отношение к РНР. Откройте файл httpd.conf в своем текстовом редакторе. В конце файла присутствуют две строки следующего вида: #AddType application/x-httpd-php .php .php4 #AddType application/x-httpd-php-source .php .phps Чтобы файлы с поддержкой РНР правильно работали на сервере, эти строки необходимо раскомментировать. Для этого следует удалить'знак фунта (#) в начале каждой строки. Сохраните файл и перейдите на один уровень вверх в иерархии каталогов (командой cd). Запустите Apache следующей командой: ./bin/apachectl start Voila! РНР и Apache готовы к работе. Для проверки сохраните приведенный ниже фрагмент в файле phpinfo.php в корневом каталоге документов Apache - каталоге htdocs, находящемся в установочном каталоге Apache: Откройте файл в браузере на сервере. В окне появляется длинный перечень сведений о конфигурации РНР. Вы успешно установили РНР в виде динамического модуля Apache. Установка в Windows 95/98/NT Если вам когда-нибудь приходилось устанавливать новые приложения в операционной системе Windows, вероятно, это не вызывало у вас особых трудностей. Вы щелкаете на нескольких кнопках, отвечаете на несколько вопросов - и все готово. Так же обстоит дело и с установкой Apache и РНР на компьютерах с системой Windows. Дважды щелкните на значке исполняемого файла Apache. Запускается процесс установки, и на экране появляется начальное окно программ мастера (wizard) установки. Внимательно прочитайте текст лицензионного соглашения и подтвердите его. Мастер запрашивает каталог для установки и предлагает вариант по умолчанию (C:\Program Files\Apache Group\Apache). Возможно, вам захочется сократить его до C:\Apache - решайте сами. Затем вам будет предложено указать имя, под которым программа будет находиться в меню Пуск (Start). Введите имя по своему усмотрению или подтвердите предложенный вариант. Следующий вопрос относится к типу установки. Выберите вариант Typical (Стандартная установка). После того как решение будет принято, выполняется установка. На следующем шаге редактируется файл httpd.conf, находящийся в подкаталоге conf каталога, указанного на шаге 2. Откройте файл в текстовом редакторе и найдите строку ServerAdmin yourname@yoursite.com Замените yourname@yoursite.com правильными данными. Затем укажите правильное имя сервера (если его нет, воспользуйтесь именем localhost): ServerName localhost Запустите Apache и убедитесь в том, что все работает правильно. Конкретные действия зависят от того, в какой системе вы работаете. Если вы используете Windows NT, выберите в меню Пуск (Start) команду Install Apache as Service (NT Only). Затем вызовите панель управления, откройте окно Службы (Services), выберите Apache и щелкните на кнопке Пуск (Start). Apache запускается и будет запускаться автоматически при каждой последующей загрузке системы. Запустите браузер, установленный на сервере, и введите адрес http: /local host/. В окне открывается страница с сообщением о том, что установка была выполнена успешно. Следующий этап - установка РНР. Перейдите в каталог, где находится пакет РНР, и распакуйте его в произвольный каталог. Перейдите в каталог, в который были помещены распакованные файлы, и найдите в нем файл php.ini-dist. Переименуйте его в php.ini и переместите в каталог C:\Windows. Вернитесь в каталог РНР и найдите два других файла, php4ts.dll и Mscvrt.dll. Поместите эти файлы в каталог C:\Windows\System\. Возможно, файл Mscvrt.dll уже существует - если вам будет предложено его перезаписать, не соглашайтесь. Вернитесь к файлу Apache http.conf и откройте его в текстовом редакторе. Необходимо внести еще несколько изменений. Найдите строку ScriptAlias /cgi-bin/ "C:/Apache/cgi-bin/" Непосредственно под этой строкой введите следующую: ScriptAlias /php4/ "C:/php4/" Найдите секцию AddType. Вы увидите две закомментированные строки: #AddType application/x-httpd-php3. phtml #AddType application/x-httpd-php3-source .phps Непосредственно под этими строками добавьте следующие: AddType application/x-httpd-php .phtml .php AddType application/x-httpd-php-source .phps Прокрутите содержимое файла и найдите следующий закомментированный фрагмент: # # Action lets you define media types that will execute a script whenever # a matching file is called. This eliminates the need for repeated URL # pathnames for oft-used CGI file processors. # Format: Action media/type /cgi-script/location # Format: Action handler-name /cgi-script/location # Добавьте под этим фрагментом следующую строку: Action application/x-httpd-php /php4/php.exe РНР и Apache готовы к работе. Для проверки сохраните приведенный ниже фрагмент в файле phpinfo.php в корневом каталоге документов Apache - каталоге htdocs. Он находится в том каталоге, который был указан на шаге 2: Хотя при успешном завершении действий, описанных выше, вы сможете использовать web-сервер и РНР для тестирования, это еще не значит, что ваш web-сервер будет доступен из World Wide Web. За информацией по этому вопросу обращайтесь на официальный сайт Apache (http://www.apache.org). Более того, хотя описанная процедура позволяет работать с пакетом РНР, вероятно, вы захотите изменить конфигурацию РНР так, чтобы она лучше соответствовала вашим потребностям. Эта тема рассматривается в следующем разделе «Конфигурация РНР». Откройте файл в браузере на сервере - в окне появляется длинный перечень сведений о конфигурации РНР. Конфигурация РНР РНР будет правильно работать и при конфигурации, выбранной по умолчанию, однако вы можете внести некоторые изменения, чтобы работа пакета лучше соответствовала вашим целям. Все параметры конфигурации находятся в файле php.ini, который по умолчанию копируется в каталог /usr/local/lib/ в процессе установки. Независимо от платформы и web-сервера, используемого в сочетании с РНР, файл php.ini содержит одинаковый набор стандартных параметров, позволяющих управлять важными аспектами работы РНР. Этот файл содержит все параметры, определяющие поведение пакета при выполнении сценария РНР. Содержимое файла php.ini читается при запуске РНР. В версии 3.0 файл конфигурации назывался php3.ini, но в версии 4.0 ему было присвоено имя php.ini. Общие параметры конфигурации Подробное описание всех конфигурационных параметров выходит за рамки этой книги, но некоторые директивы используются особенно часто и заслуживают отдельного упоминания. Другие параметры упоминаются в соответствующих местах следующих глав. short_open_tag [on | off] Параметр short_open_tab [on | off] определяет возможность использования коротких тегов наряду со стандартными тегами. asp_tags [on | off] Параметр asp_tags [on | off] определяет возможность использования тегов в стиле ASP наряду со стандартными тегами. При использовании тегов в стиле ASP фрагменты кода РНР оформляются следующим образом: <% print "This is РНР code."; %> precision [integer] Параметр precision [integer] задает количество значащих цифр, отображаемых в вещественных числах. safe_mode [on | off] Безопасный режим особенно удобен в случае, если в вашей системе работают несколько пользователей. Включение безопасного режима гарантирует, что пользователь не сможет применить сценарий РНР для получения доступа к другому файлу в системе - например, файлу passwd на компьютере Linux. Параметр safe_mode работает только в CGI-версии РНР. За дополнительной информацией по этой теме обращайтесь к главе 16. max_execution_time [integer] Параметр max_execution_time [integer] определяет максимальную продолжительность выполнения сценариев РНР в секундах. Такое ограничение предотвращает поглощение ценных системных ресурсов сценариями, содержащими ошибки. error_reporting [1-8] Параметр error_reporting [1-8] определяет уровень выдачи сообщений об ошибках в РНР. Чем выше значение, тем «чувствительнее» РНР реагирует на ошибки. Значение Чувствительность 1 Обычные ошибки 2 Обычные предупреждения 4 Ошибки лексического анализатора 8 Замечания display_errors [on | off] Параметр display_errors[on | off] управляет выводом информации об ошибках в браузере. log_errors Параметр log_errors определяет, следует ли регистрировать ошибки в файле. При включении параметра log_errors файл, в котором регистрируются ошибки, назначается при помощи параметра error_log. error_log [filename] Параметр error_log определяет файл, в котором регистрируются ошибки при включенном параметре log_errors. magic_quotes_gpc При активизации параметра magic_quotes_gpc все специальные символы, содержащиеся в пользовательских данных или в базе, автоматически экранируются обратной косой чертой. Кстати, сокращение gрс означает «get/post/cookie». Лично я считаю, что параметр magic_quotes_gpc эффективнее отключить и использовать явное экранирование специальных символов. Какой бы способ вы ни выбрали, любые компромиссы приведут к порче данных. Если параметр magic_quotes_gpc включен, никогда не экранируйте специальные символы обратной косой чертой; в противном случае не забывайте делать это всегда. track_vars Параметр track_vars включает отслеживание нескольких важных массивов сеансовых переменных, в том числе $HTTP_GET_VARS[], $HTTP_POST_VARS[], $HTTP_POST_FILES, $HTTP_COOKIE_VARS[], $HTTP_ENV_VARS[] и $HTTP_SERVER_VARS[]. Эти массивы подробно описаны в главе 13. Помните, что кроме перечисленных параметров существует множество других. Выше перечислены лишь те параметры, которые часто применяются большинством пользователей. Многие параметры будут упоминаться в последующих главах. Основные конструкции РНР Прежде чем переходить к изложению основного материала, занимающего оставшуюся часть книги, я представлю несколько вспомогательных концепций, относящихся к РНР. Переход в РНР Механизм лексического анализа должен как-то отличать код РНР от других элементов страницы. Идентификация кода РНР называется «переходом в РНР» (escaping to РНР). Существуют четыре варианта оформления перехода в РНР: стандартные теги; короткие теги; теги script; теги в стиле ASP. Стандартные теги Стандартные теги используются программистами РНР чаще остальных способов, что объясняется наглядностью и удобством этой формы записи: У стандартных тегов есть еще одно дополнительное преимущество: за открывающей конструкцией , интерпретируется как код РНР. Короткие теги Короткие теги обеспечивают наиболее компактную запись для перехода в РНР: По умолчанию короткие теги не используются, их нужно специально активизировать. Это можно сделать двумя способами: указать ключ -enable-short-tags при компиляции РНР; включить параметр short_open_tag в файл php.ini. Теги script Некоторые текстовые редакторы ошибочно принимают код РНР за код HTML (то есть визуально отображаемый текст), что нарушает работу над web-страницей. Проблема решается использованием тегов script: Теги в стиле ASP Четвертый и последний способ оформления внедренного кода РНР - теги в стиле ASP (Active Server Page). Они похожи на короткие теги, описанные выше, однако вместо вопросительного знака используется знак процента (%): <%php print "Welcome to the world of PHP!"; %> У тегов в стиле ASP есть одна особенность, делающая запись более компактной. Во внедренный код РНР не обязательно включать команду print - знак равенства (=), расположенный сразу же после открывающего тега в стиле ASP, приказывает лексическому механизму РНР вывести значение указанной переменной: <%=$variable %> Этот удобный стиль позволяет использовать в страницах фрагменты вида <% // Присвоить значение переменной $recipe $recipe = "Lasagna": %> Luigi's favorite recipe is <%=$recipe;%> Этот фрагмент содержит два разных сценария РНР. В первом сценарии переменной $recipe присваивается значение "Lasagna". Позднее, когда потребуется вывести значение $гесiре, специально для этой цели используются теги в стиле ASP. Кстати, такая возможность существует и для коротких тегов (). Внедрение HTML в код РНР Одной из самых замечательных особенностей HTML является простота использования в сочетании с другими языками - например, HTML и JavaScript (см. листинг 1.2). Листинг 1.2. Вывод кода HTML средствами РНР Basic PHP/HTML integration PHP/HTML integration is cool."; ?> Рис. 1.2. Форматирование даты с использованием функции РНР date() В листинге 1.2 показано, как код HTML интегрируется прямо в команды РНР. В данном примере в код РНР включаются теги заголовка третьего уровня (

...

). В итоговом документе эти теги ничем не отличаются от обычного кода HTML. В листинге 1.3 продемонстрировано включение динамической информации в web-страницу на примере вывода текущей даты в заголовке окна (рис. 1.2). Листинг 1.3. Динамический вывод даты PHP Recipes | <? print (date("F d, Y")); ?> Простая функция РНР date() форматирует дату одним из нескольких стандартных способов. Отформатированная дата вставляется в заголовок окна. РНР также позволяет изменять формат конструкций HTML - для этого соответствующая характеристика тега присваивается переменной, вставляемой в файл. В листинге 1.4 эта возможность продемонстрирована на примере присваивания характеристики шрифта (h3) переменной $big_font и ее последующего использования при выводе текста. Листинг 1.4. Динамические теги HTML PHP Recipes | <? print (date("F d, Y")); ?> PHP Recipes"; ?> Листинг 1.4 представляет собой разновидность листинга 1.3 - на этот раз тег заголовка (

...

) сначала присваивается переменной, а затем эта переменная используется в команде print. В итоговом документе эти теги ничем не отличаются от обычного кода HTML. Включение нескольких сценариев РНР Для обеспечения необходимой гибкости при построении динамических web-приложений можно внедрить в страницу несколько сценариев РНР (листинг 1.5). Листинг 1.5. Включение нескольких сценариев РНР в один документ <? print "Another PHP-enabled page"; Svariable = "Hello World!"; ?> Листинг 1.5 начинается как типичная (пусть несколько упрощенная) страница HTML. При внедрении нескольких сценариев переменные, значения которых были присвоены в одном сценарии, могут использоваться в другом сценарии той же страницы. Комментарии в коде РНР Комментарии следует использовать даже в относительно простых и незамысловатых сценариях. В РНР существуют два формата комментариев: Однострочные комментарии обычно используются для коротких пояснений или примечаний, относящихся к локальному коду. Многострочные комментарии обычно используются при оформлении алгоритмов на псевдокоде и в более подробных объяснениях. Оба способа в конечном счете приводят к одинаковому результату и совершенно не влияют на общее быстродействие сценария. Выбор варианта остается за вами. Однострочные комментарии При оформлении однострочных комментариев используется два стиля комментирования. Оба стиля работают абсолютно одинаково, но в них используются разные служебные символы. В одном случае комментарий начинается с двойного символа «косая черта» (//), а в другом - с символа фунта (#). Ниже приведены примеры обоих стилей: Конечно, оба стиля однострочных комментариев могут применяться для построения искусственных многострочных комментариев, как показано в следующем листинге: Многострочные комментарии В РНР существует возможность построения подробных комментариев, занимающих несколько строк. Такие комментарии оформляются в стиле языка С - их начало и конец обозначаются символами /* и */. Как видите, многострочные комментарии особенно удобны для вывода относительно длинной сводной информации обо всем сценарии или его части. Итоги В этой главе мы рассмотрели некоторые ключевые аспекты РНР: историю и особенности РНР; установку и конфигурацию; «переход» в РНР; комментирование кода РНР. Эти вопросы закладывают основу для материала следующих глав, в которых будут более подробно описаны проблемы программирования на языке РНР. В конце следующей главы вы будете знать о РНР достаточно, чтобы писать собственные программы. Новые знания будут применены на практике - мы создадим календарь, который можно будет легко вставить в существующую web-страницу. Этот проект подготовит вас к работе над web-приложением РНР Recipes. Глава 2 Переменные и типы данных Типы данных составляют основу любого языка программирования и являются средством, с помощью которого программист представляет разные типы информации. В РНР поддерживаются шесть основных типов данных: целые числа; вещественные числа; строки; массивы; объекты; логические величины. Одним из столпов любого языка программирования является поддержка числовых данных. В РНР поддерживаются как целые, так и вещественные числа (двойной точности). Разные числовые форматы подробно описываются в следующих разделах. Целые числа Целое число не имеет дробной части и представляется последовательностью из одной или нескольких цифр. Примеры целых чисел: 5 591 52 Восьмеричная и шестнадцатеричная запись В РНР поддерживается запись целых чисел в восьмеричной (по основанию 8) и шестнадцатеричной (по основанию 16) системах счисления. Восьмеричные числа начинаются с цифры 0, после которой следует серия цифр от 0 до 7. Примеры: 0422 0534 Шестнадцатеричные целые числа имеют префикс 0х или 0Х и могут состоять из цифр от 0 до 9 и букв от а (А) до f (F). Примеры: 0x3FF 0x22abc Вещественные числа Вещественные числа (числа с плавающей точкой) отличаются от целых наличием дробной части. Они используются для представления значений, требующих повышенной точности, - например, температур или денежных величин. В РНР поддерживаются два вещественных формата: стандартная и научная (экспоненциальная) запись. Стандартная запись Стандартная запись удобна для представления типичных вещественных чисел - скажем, денежных величин. Примеры: 12.45 98.6 Научная запись Научная запись лучше подходит для представления очень больших и очень малых чисел - скажем, межпланетных расстояний или размеров атомов. Примеры: Зе8 5.9736е24 Строковые значения Строкой (string) называется последовательность символов, которая рассматривается как единое целое, но при этом обеспечивает доступ к отдельным символам. Примеры строк: thesaurus 49ers abc &%/$# Обратите внимание: в РНР не поддерживается символьный тип данных. Строковый тип может рассматриваться как единое представление для последовательностей, состоящих из одного или нескольких символов. Строковое присваивание Строки делятся на две категории в зависимости от типа ограничителя - они могут ограничиваться парой кавычек (" ") или апострофов (' '). Между этими категориями существуют два принципиальных различия. Во-первых, имена переменных в строках, заключенных в кавычки, заменяются соответствующими значениями, а строки в апострофах интерпретируются буквально, даже если в них присутствуют имена переменных, Два следующих объявления дают одинаковый результат: $food = "meatloaf"; $food = 'meatloaf'; Однако результаты следующих объявлений сильно различаются: $sentence = "My favorite food is $food"; $sentence2 = 'My favorite food is $food'; Переменной $sentence присваивается строка My favorite food is meatloaf. Обратите внимание: переменная $food автоматически интерпретируется. С другой стороны, переменной $sentence2 присваивается строка My favorite food is $food. В отличие от переменной $sentence, в $sentence2 осталась не интерпретированная переменная $food. Различия обусловлены использованием кавычек и апострофов при присваивании переменным $sentence и $sentence2. Прежде чем рассматривать второе фундаментальное различие между строками, заключенными в апострофы и в кавычки, необходимо познакомиться со служебными символами, используемыми в строках РНР. В РНР, как и в большинстве современных языков программирования, строки могут содержать служебные символы (например, символы табуляции или новой строки), перечисленные в табл. 2.1. Таблица 2.1. Служебные символы в строкахПоследовательность Смысл \n Новая строка \r Возврат курсора \t Горизонтальная табуляция \\ Обратная косая черта \$ Знак доллара \" Кавычка \[0-7]{1,3} Восьмеричная запись числа (в виде регулярного выражения) \x[0-9A-Fa-f]{l,2} Шестнадцатиричная запись числа (в виде регулярного выражения) Второе принципиальное различие заключается в том, что в строках, заключенных в кавычки, распознаются все существующие служебные символы, а в строках, заключенных в апострофы, - только служебные символы «\\» и «\». Следующий пример наглядно демонстрирует различия между присваиванием строк, заключенных в кавычки и апострофы: $double_list = "item1\nitem2\nitem2"; $single_list = 'item1\nitem2\nitem2'; Если вывести обе строки в браузере, окажется, что строка в кавычках содержит внутренние символы новой строки, а в строке в апострофах последовательность \n выводится как обычные символы. Хотя многие служебные символы в браузерах несущественны, при форматировании для других условий они играют очень важную роль. Помните об этом, выбирая между кавычками и апострофами, и вам удастся избежать многих неожиданностей. Синтаксис встроенной документации Второй вариант синтаксиса ограничения строк, представленный в HTML4, называется встроенной документацией (here doc). В этом варианте синтаксиса строка начинается с символов <<<, за которыми следует некоторый идентификатор по вашему выбору, затем строка, присваиваемая переменной. Конструкция заканчивается вторым экземпляром того же идентификатора. Пример: $paragraph = << "Broiled Veal Chops", merlot => "Baked Ham", sauvignon => "Prime Rib", sauternes => "Roasted Salmon"); Отличается только способ создания массива pairings, а функциональные возможности остаются без изменений. Многомерные индексируемые массивы Многомерные индексируемые массивы работают практически так же, как и их одномерные прототипы, однако элементы в них определяются несколькими индексами вместо одного. Теоретически размерность индексируемого массива не ограничивается, хотя в большинстве приложений практически не встречаются массивы с размерностью выше 3. Обобщенный синтаксис элементов многомерного массива: $имя[индекс1][индекс2]..[индексN]; Пример ссылки на элемент двухмерного индексируемого массива: $position = $chess_board[5][4]; Многомерные ассоциативные массивы Многомерные ассоциативные массивы также существуют в РНР (и приносят определенную пользу). Допустим, в массиве $раirings из предыдущего примера должна храниться информация не только о сорте, но и о производителе вина. Это можно сделать следующим образом: $pairings["Martinelli"]["zinfandel"] = "Broiled Veal Chops"; $pairings["Beringer"]["merlot"] = "Baked Ham"; $pairings["Jarvis"]["sauvignon"] = "Prime Rib"; $pairings["Climens"]["sauternes"] = "Roasted Salmon"; Смешанное индексирование В многомерных массивах допускается смешанное индексирование (числовое и ассоциативное). Допустим, вы хотите расширить модель одномерного ассоциативного массива для хранения информации об игроках первого и второго состава футбольной команды. Решение может выглядеть следующим образом: $Buckeyes["quarterback"] [1] = "Bellisari"; $Buckeyes["quarterback"] [2] = "Moherman": $Buckeyes["quarterback"] [3] = "Wiley"; В РНР существует множество функций для создания массивов и операций с ними - эта тема настолько обширна, что заслуживает отдельной главы. Работа с массивами в РНР подробно описана в главе 13. Объекты К пятому типу данных РНР относятся объекты. Объект представляет собой переменную, экземпляр которой создается по специальному шаблону, называемому классом. Концепции объектов и классов являются неотъемлемой частью парадигмы объектно-ориентированного программирования (ООП). В отличие от других типов данных, поддерживаемых в языке РНР, объекты должны объявляться явно. Необходимо понимать, что объект - всего лишь конкретный экземпляр класса, используемого в качестве шаблона для создания объектов с конкретными характеристиками и функциональными возможностями. Следовательно, объявление класса должно предшествовать объявлению объектов, создаваемых на их основе. Пример объявления класса и последующего создания объектов на его основе: class appliance { var power: function set_power($on_off) { $this->power = $on_off; } } ... $blender = new appliance; Определение класса задает атрибуты и функции, связанные с некоторой структурой данных - в данном примере это структура с именем appliance (устройство). У этой структуры имеется всего один атрибут power (мощность). Для изменения этого атрибута создается метод set_power. Помните: определение класса - всего лишь шаблон, и выполнять операции с ним в программе невозможно; сначала нужно создать объекты на основе этого шаблона. Объекты создаются при помощи ключевого слова new. Например, в приведенном выше фрагменте создается объект $blender класса appliance. После создания объекта $blender можно задать его мощность при помощи метода set_power: $blender->set_power("on"); Объектно-ориентированное программирование занимает столь важное место в современных стандартах программирования, что его применение в РНР заслуживает отдельной главы. Реализация ООП в РНР описана в главе 6. Логические величины (истина/ложь) Логический тип данных принимает всего два значения: истинное (true) и ложное (false). Логические величины создаются двумя способами: при проверке условий и в виде значений переменных. Обе ситуации достаточно просты. Сравнения существуют в нескольких формах. Чаще всего они встречаются при использовании оператора = в условной команде if. Пример: if ($sum == 40) : ... Результатом проверки является либо истина, либо ложь: переменная $sum либо равна 40, либо не равна. Если переменная $sum равна 40, проверка дает истинный результат. В противном случае результат равен false. Логические величины также могут определяться явным присваиванием переменной истинного или ложного значения. Пример: $flag = TRUE; if ($flag == TRUE) : print "The flag is true!"; else : print "The flag is false!"; endif; Если переменная $flag истинна, выводится первое сообщение, а если ложна - второе сообщение. Возможен и другой вариант - представление истинных и ложных логических величин в виде значений 1 и 0 соответственно. В этом случае предыдущий пример выглядит так: $flag = 1; if ($flag == TRUE) ; print "The flag is true!"; else : print "The flag is false!"; endif; Наконец, существует еще один способ: $flag = TRUE: // При выполнении этой команды косвенно // проверяется условие "if ($flag == TRUE)" if ($flag) : print "The flag is true!"; else : print "The flag is false!"; endif: Идентификаторы Общий термин идентификатор применяется к переменным, функциям и другим объектам, определяемым пользователем. Идентификаторы РНР должны удовлетворять нескольким условиям: Идентификатор состоит из одного или нескольких символов и начинается с буквы или символа подчеркивания. Идентификатор может содержать только буквы, цифры, символы подчеркивания и другие ASCII-символы с кодами от 127 до 255. Примеры: Допустимые идентификаторы Недопустимые идентификаторы my_function This&that Size !counter _someword 4ward В идентификаторах учитывается регистр символов. Следовательно, переменная с именем $recipe отличается от переменных с именами $Recipe, $rEciPe и $recipE. Длина идентификаторов не ограничивается. Это удобно, поскольку программист может точно описать смысл идентификатора в его имени. Идентификатор не может совпадать с каким-либо из стандартных ключевых слов РНР. Переменные В примерах, приведенных выше, я попутно показал, как происходит присваивание и изменение значений переменных. И все же стоит четко сформулировать правила объявления переменных и выполнения операций с ними. Ниже приводится подробное описание этих правил. Объявление переменных Переменная представляет собой именованную область памяти, содержащую данные, с которыми можно выполнять операции во время выполнения программы. Имена переменных всегда начинаются со знака доллара, $. Ниже приведены примеры допустимых имен переменных: $соlоr $operating_system $_some_variable $model Имена переменных должны соответствовать тем же условиям, что и идентификаторы. Другими словами, имя переменной начинается с буквы или символа подчеркивания и состоит из букв, символов подчеркивания, цифр или других ASCII-символов в интервале от 127 до 255. Следует заметить, что переменные в РНР, как и в языке Perl, не требуют специального объявления. Вместо этого переменная объявляется при первом ее использовании в программе. Более того, тип переменной косвенно определяется по типу хранящихся в ней данных. Рассмотрим следующий пример: $sentence = "This is a sentence."; // $sentence интерпретируется как строка $price = 42.99: // $price интерпретируется как вещественное число $weight = 185; // $weight интерпретируется как целое число Переменные могут объявляться в любой точке сценария РНР, однако от расположения объявления зависит то, откуда можно обращаться к данной переменной. Область видимости переменных Область видимости (scope) определяется как область доступности переменной в той программе, в которой она была объявлена. В зависимости от области видимости переменные РНР делятся на четыре типа: локальные переменные; параметры функций; глобальные переменные; статические переменные. Локальные переменные Переменная, объявленная внутри функции, считается локальной; другими словами, на нее можно ссылаться только в этой функции. При любом присваивании вне функции будет использоваться совершенно другая переменная, которая не имеет ничего общего (кроме имени) с переменной, объявленной внутри функции. При выходе из функции, в которой была объявлена локальная переменная, эта переменная и ее значение уничтожаются. Основное достоинство локальных переменных - отсутствие непредвиденных побочных эффектов, связанных со случайной или намеренной модификацией глобальной переменной. Рассмотрим следующий пример: $х = 4; function assignx () { $х = 0; print "\$x inside function is $x.
"; } assignx(); print "\$x outside of function is $x.
"; При выполнении этого фрагмента выводится следующий результат: $х inside function is 0. $х outside of function is 4. Как видите, программа выводит два разных значения переменной $х. Дело в том, что переменная $х внутри функции assignx имеет локальную природу, и изменение ее значения никак не отражается на значении, существующем за пределами этой функции. Справедливо и обратное - модификация $х за пределами функции никак не отражается на локальных переменных функции assignx(). Параметры функций В РНР, как и во многих других языках программирования, любые параметры, передаваемые функции при вызове, должны быть объявлены в заголовке функции. Хотя параметрам присваиваются аргументы, переданные извне, после выхода из функции они становятся недоступными. Параметры объявляются в круглых скобках после имени функции. Объявление параметров практически не отличается от объявления типичной переменной: // Функция умножает переданное значение на 10 и возвращает результат function x10 ($value) { $value = $value * 10; return $value; } Хотя вы можете обращаться к параметрам в той функции, в которой они были объявлены, и выполнять с ними необходимые операции, после завершения функции параметры уничтожаются. Глобальные переменные Глобальные переменные, в отличие от локальных, доступны в любой точке программы. Но чтобы изменить значение глобальной переменной, необходимо специально объявить ее как глобальную в соответствующей функции. Для этого перед именем переменной ставится ключевое слово GLOBAL. Пример: $somevar = 15; function addit() { GLOBAL $somevar; $somevar++; print "Somevar is $somevar"; } addit(); Будет выведено значение $somevar, равное 16. Допустим, вы забыли включить следующую строку: GLOBAL $somevar; В этом случае $somevar будет присвоено значение 1, поскольку эта переменная будет считаться локальной по отношению к функции addit( ). Локальная переменная по умолчанию инициализируется 0, а затем к ней прибавляется 1; таким образом, будет выведено значение 1. Альтернативный способ объявления глобальных переменных связан с использованием массива РНР $GLOBALS( ). Давайте вернемся к предыдущему примеру и воспользуемся этим массивом для объявления глобальной переменной $somevar: $somevar = 15; function addit() { $GLOBALS["somevar"]; $somevar++; } addit(); print "Somevar is $somevar"; Каким бы способом ни обеспечивалась глобальная видимость переменной, помните, что неосторожное использование глобальных переменных нередко приводит к неожиданным результатам, причиняющим немало хлопот программистам. Таким образом, хотя глобальные переменные очень удобны, при их использовании необходима умеренность. Статические переменные Последний тип видимости переменных называется статическим. В отличие от переменных, объявленных параметрами и уничтожаемых при выходе из функции, статическая переменная сохраняет свое значение при повторном вызове. Для объявления статической переменной перед ее именем ставится ключевое слово STATIC: STATIC $somevar; Рассмотрим пример: function keep_track() { STATIC $count = 0; $count++; print $count; print "
"; } keep_track(); keep_track(); keep_track(); Как будут выглядеть результаты работы этого сценария? Если бы переменная $count не была объявлена статической (то есть являлась локальной), результат выглядел бы так: 1 1 1 Но поскольку переменная $count является статической, при каждом вызове функции будет сохраняться ее предыдущее значение, поэтому результат будет таким: 1 2 3 Статические переменные особенно удобны при написании рекурсивных функций - особого класса функций, которые многократно вызывают сами себя до выполнения некоторого условия. Рекурсивные функции рассматриваются в главе 4. Переключение типов Иногда бывает удобно использовать переменные способами, не предусмотренными при их создании. Допустим, вам захочется прибавить строковое значение "15" к целому числу 12. К счастью, тип переменных РНР может изменяться и без использования механизма явного преобразования. Этот процесс, независимо от того, выполняется ли он прямо или косвенно, называется переключением (juggling) типов. Лучше всего продемонстрировать сказанное на конкретных примерах. Предположим, вы суммируете две величины - строку и целое число. Как вы думаете, что при этом произойдет? Результат зависит от содержимого строки. Например, при суммировании целого числа со строковым представлением числа будет получено целое число: $variablel = 1; $variable2 = "1"; $variable3 = $variablel + $variable2; // $variable3 присваивается 4. Другой пример переключения типов - суммирование целого числа с вещественным. При этом целое число преобразуется к вещественному типу, чтобы избежать потери точности: $variablel = 3; $variable2 = 5.4; $variable3 = $variablel + $variable2; // $variablel интерпретируется как вещественное число. // и $variable3 присваивается 8.4. Следует упомянуть о некоторых малоизвестных особенностях переключения типов. Что произойдет при попытке суммирования целого числа и строки, содержащей целое число, но не являющейся строковым представлением? Рассмотрим следующий пример: $variablel = 5; $variable2 = "100 bottles of beer on the wall"; $variable3 = ;variable1 + $variable2; // $variable3 присваивается 105 В результате переменной ;variable3 присваивается значение 105. Это происходит из-за того, что лексический анализатор РНР определяет тип по началу строки. Допустим, мы привели переменную $variable2 к виду "There are 100 bottles of beer on the wall". Поскольку алфавитные символы трудно интерпретировать как целое число, строка интерпретируется как 0, и переменной $variable3 присваивается 5. Хотя в большинстве случаев переключение типов обеспечивает желаемый результат, существует способ явного приведения переменных к конкретному типу. Эта тема рассматривается в следующем разделе. Преобразование типов Явное приведение переменной к типу, отличному от того, который изначально предназначался для нее, называется преобразованием (casting) типа. Изменение типа может быть как временным, одноразовым, так и постоянным. Чтобы временно привести переменную к другому типу, достаточно воспользоваться оператором преобразования типа - указать нужный тип перед именем переменной в круглых скобках (табл. 2.2). Таблица 2.2. Операторы преобразования типа переменныхОператор преобразования типа Новый тип (int) или (integer) Целое число (real), (double) или (float) Вещественное число (string) Строка (array) Массив (object) Объект Простой пример преобразования типов: $variable1= 13; // $variable1 присваивается целое число 13 $variable2 = (double) $variable1; // $variable2 присваивается 13.0 Хотя переменная $variable1 первоначально содержала целое число 13, преобразование (double) преобразует ее к вещественному типу (поэтому число 13 превращается в 13.0). Полученное значение присваивается переменной $variable2. Из предыдущего раздела вы знаете, что при суммировании целого числа с вещественным получается вещественный результат. Однако тип результата можно изменить посредством явного преобразования типа: $variablel = 4.0; $variable2 = 5; $variable3 = (int) $variable1 + $variable2; // $variable3 = 9 Следует заметить, что преобразование вещественного типа к целому всегда сопровождается округлением: $variablel = 14.7: $variable2 = (int) $varlable1; // $variable2 = 14: Строку или переменную другого типа также можно преобразовать в элемент массива. В этом случае преобразованная переменная становится первым элементом массива: $variable1 = 1114; $array1 = (array) $varable1; print $array1[0]; // Выводится значение 1114 Наконец, любой тип данных можно преобразовать в объект. Переменная становится атрибутом объекта, и ей присваивается имя scalar: $model = "Toyota"; $new_obj = (object) $model; Ссылка на исходное строковое значение выглядит так: print $new_obj->scalar; Присваивание Вы уже знаете, как присвоить значение переменной в сценарии РНР. Тем не менее, некоторые тонкости, связанные с присваиванием, стоит выделить особо. Вероятно, вам хорошо знаком механизм присваивания по значению, при котором именованной переменной присваивается конкретное значение - например, целое число 1 или строка "ciao". Однако существует и второй механизм - присваивание по ссылке, также открывающее перед программистами немало полезных возможностей. В следующих разделах оба механизма рассматриваются более подробно. Присваивание по значению Это самый распространенный способ присваивания, при котором значение просто заносится в область памяти, представленную именем переменной. Примеры присваивания по значению: $vehicle = "car"; $amount =10.23; В результате выполнения этих двух команд по адресу памяти, представленному именем $vehicle, сохраняется строка "car", а по адресу, представленному именем $amount, - значение 10.23. Присваивание по значению также может выполняться в результате выполнения команды return в функциях: function simple () { return 5; } $return_value = simple(); Функция simple( ) всего лишь возвращает значение 5, которое присваивается некоторой переменной. В данном примере значение 5 будет присвоено переменной $return_value. Присваивание по ссылке Другой способ заключается в присваивании переменной ссылки на область памяти, занимаемую другой переменной. Вместо конкретного значения переменная-приемник связывается с указателем (или ссылкой) на область памяти, поэтому фактическое копирование не выполняется. Чтобы присвоить значение по ссылке, укажите перед именем переменной-источника символ & (амперсанд): $dessert = "cake"; $dessert2 = $Sdessert; $dessert2 = "cookies"; print "$dessert2
"; // Выводится строка cookies print Sdessert; // Снова выводится строка cookies Как видно из приведенного фрагмента, после связывания переменной $dessert2 со ссылкой на область памяти, занимаемую переменной $dessert, любые изменения $dessert2 приводят к автоматической модификации $dessert (и всех остальных переменных, ссылающихся на эту же область памяти). Переменные в переменных В некоторых ситуациях бывает удобно использовать переменные, содержимое которых может динамически интерпретироваться как имя другой переменной. Рассмотрим типичный случай присваивания: $recipe = "spaghetti"; Оказывается, строку "spaghetti" можно интерпретировать как имя переменной - для этого в команде присваивания перед именем исходной переменной ставится второй знак $: $$recipe = "& meatballs"; Эта команда присваивает строку "& meatballs" переменной с именем "spaghetti". Следовательно, следующие две команды выводят одинаковые результаты: print $recipe $spaghetti; print $recipe $($recipe); В обоих случаях будет выведена строка "spaghetti & meatballs". Стандартные переменные В РНР поддерживается ряд стандартных переменных, предоставляющих в распоряжение программиста довольно подробную информацию о внутренней конфигурации. Значения одних переменных задаются РНР, другие изменяются в зависимости от операционной системы и web-сервера, с которыми работает РНР. Вместо подробного описания всех стандартных переменных я выделю лишь те переменные и функции, которые используются на практике многими программистами. Чтобы получить полный список переменных web-сервера, окружения и РНР, определенных для вашей конфигурации системы, достаточно выполнить следующий фрагмент: while (list($var,$value) = each($GLOBALS)) : echo "
$var => $value"; endwhile; В результате выводится список наподобие приведенного ниже. Потратьте немного времени на просмотр полученных данных, а затем разберите приведенные примеры. GLOBALS => HTTP_GET_VARS => Array HTTP_COOKIE_VARS => Array HOSTSIZE => 1000 HOSTNAME => server1.apress.com LOGNAME => unstrung HISTFILESIZE => 1000 REMOTEHOST => apress.com MAIL -> /var/spool/mail/apress MACHTYPE => 1386 TERM => vt100 HOSTTYPE => i386-linux PATH => /usr/sbin:/sbin:/usr/local /bin:/bin:/usr/bin:/usr/X11R6/bin:/usr/local/Java/bin HOME => /root INPUTRC => /etc/inputrc SHELL => /bin/csh USER => nobody VENDOR => intel GROUP => root HOST => server1.apress.com OSTYPE => linux PWD => /www/bin SHLVL => 3_ => /www/bin/httpd DOCUMENT_ROOT => /usr/local/apress/site.apress HTTP_ACCEPT => */* HTTP_ACCEPT_ENCODING => gzip, deflate HTTP_ACCEPT_LANGUAGE => it.en-us;q=0.5 HTTP_CONNECTION -> Keep-Alive HTTP_HOST => www.apress.com HTTP_USER_AGENT => Mozilla/4.0 (compatible; MSIE 5.0: Windows 98; CNETHomeBuild051099) REMOTE_ADOR => 127.0.0.1 REMQTE_PORT => 3207 SCRIPT_FILENAME => /usr/local/apress/site.apress/j/environment_vars.php SERVER_ADDR => 127.0.0.1 SERVER_AOMIN => admin@apress.com SERVER_NAME => www.apress.com SERVERJORT => 80 SERVER SIGNATURE => Apache/1.3.12 Server at www.apress.com Port 80 SERVER_SOFTWARE => Apache/1.3.12 (Unix) PHP/4.0.1 GATEWAY_INTERFACE => CGI/1.1 SERVER_PROTOCOL => HTTP/1.1 REQUEST_METHOD => GET QUERY_STRING => REQUEST_URI => /j/environment_vars.php SCRIPT_NAME => /j/environment_vars.php PATH_TRANSLAETD => /usr/local/apress/site.apress/j/environment_vars.php PHP_SELF => /j/environment_vars.php argv => Array argc => 0 var => argc value => argc Как видите, стандартные переменные содержат разнообразные сведения - как полезные, так и не очень. Вы можете вывести любую из этих переменных по имени. Например, следующая команда выводит IP-адрес пользователя: print "Hi! Your IP address is: $REMOTE_ADDR"; IP-адрес выводится в числовой форме (например, 208.247.106.187). Кроме того, стандартные переменные могут использоваться для сбора информации о браузере и операционной системе пользователя. Команда print "Your browser is: $HTTP_USER_AGENT"; возвращает информацию следующего вида: Your browser is: Mozina/4.0 (compatible: MSIE 5.0; Windows 98: CNETHomeBuild051099) Информация о браузере и операционной системе, в которой он работает, может пригодиться при построении страниц, рассчитанных на специфические форматы конкретных браузеров. Для работы с массивами стандартных переменных необходимо включить директиву track_vars в файл php.ini. В РНР версии 4.0.3 директива track_vars включена постоянно. Константы Константой называется именованная величина, которая не изменяется в процессе выполнения программы. Константы особенно удобны при работе с заведомо постоянными величинами - например, числом ? (3,141592) или количеством футов в миле (5280). В РНР константы определяются функцией define( ). После того как константа будет определена, вы не сможете изменить (или переопределить) ее в этой программе. Например, определение числа я в сценарии РНР может выглядеть так: define("'PI", "3.141592"); Определенную константу можно использовать в программе: print "The value of pi is". PI."
"; $pi2 - 2 * PI: print "Pi doubled equals $pi2."; Результат работы этого фрагмента будет таким: The value of pi is 3.141592. Pi doubled equals 6.283184. В этом фрагменте следует обратить внимание на два обстоятельства. Во-первых, в именах констант не указывается знак доллара. Во-вторых, константу невозможно модифицировать (например, присвоить ей величину 2*РI); если константа используется в вычислениях, то результат приходится сохранять в другой переменной. Итоги В этой главе был изложен довольно обширный материал, необходимый для понимания и самостоятельного написания простых программ на РНР. В частности, мы рассмотрели следующие темы: допустимые типы данных (целые и вещественные числа, строки, массивы, объекты, логические величины); идентификаторы; переменные (объявление, область действия); переключение типов; преобразование типов; присваивание значений переменным (по значению, по ссылке); константы. Этот материал закладывает основу для создания более сложных сценариев. В следующей главе мы перейдем к подробному изучению выражений, операторов и управляющих конструкций языка РНР. К концу главы 3 ваших новых знаний хватит для того, чтобы построить первое приложение РНР - простейший календарь. ГЛАВА 3 Выражения, операторы и управляющие конструкции В этой главе представлены некоторые аспекты, играющие исключительно важную роль в любом языке программирования, - а именно, выражения, операторы и управляющие конструкции. Этот материал необходим в первую очередь при создании больших и сложных приложений РНР. Если вы уже знакомы с такими языками, как С и Java, эта глава всего лишь напомнит известные вам понятия. Если же вы впервые встречаетесь с этими терминами и понятиями, которые они обозначают, знание материала этой главы будет безусловно необходимо для понимания остальных глав книги. Выражения Выражение описывает некоторое действие, выполняемое в программе. Каждое выражение состоит по крайней мере из одного операнда и одного или нескольких операторов. Прежде чем переходить к примерам, демонстрирующим использование выражений, необходимо поближе познакомиться с операторами и операндами. Операнды Операнд представляет собой некоторую величину, обрабатываемую в программе. Операнды могут относиться к любому типу данных, представленному в главе 2. Вероятно, вы уже знакомы с концепциями обработки и использования операндов не только в повседневных математических вычислениях, но и по прежнему опыту программирования. Примеры операндов: $а++; // $а - операнд $sum = $val1 + $val2; // $sum. $val1 и $val2 - операнды Операторы Оператор представляет собой символическое обозначение некоторого действия, выполняемого с операндами в выражении. Многие операторы известны любому программисту, но вы должны помнить, что РНР выполняет автоматическое преобразование типов на основании типа оператора, объединяющего два операнда, - в других языках программирования это происходит не всегда. Приоритет и ассоциативность операторов являются важными характеристиками языка программирования (см. раздел «Ассоциативность операторов» этой главы). В табл. 3.1 приведен полный список всех операторов, упорядоченных по убыванию приоритета. Приоритет, ассоциативность и сами операторы подробно рассматриваются в разделах, следующих за таблицей. Таблица 3.1. Операторы РНРОператор Ассоциативность Цель ( ) - Изменение приоритета new - Создание экземпляров объектов ! ~ П Логическое отрицание, поразрядное отрицание ++ -- П Инкремент, декремент @ П Маскировка ошибок / * % Л Деление, умножение, остаток + - . Л Сложение, вычитание, конкатенация << >> Л Сдвиг влево, сдвиг вправо (поразрядный) < <= > >= - Меньше, меньше или равно, больше, больше или равно == != === <> - Равно, не равно, идентично, не равно & ^ | Л Поразрядные операции AND, XOR и OR && || Л Логические операции AND и OR ?: П Тернарный оператор = += *= /= .= П Операторы присваивания %= &= |= ^= <<= >>= AND XOR OR Л Логические операции AND, XOR и OR После знакомства с концепциями операторов и операндов следующие примеры выражений выглядят значительно понятнее: $а = 5; // Присвоить целое число 5 переменной $а $а = "5": // Присвоить строковую величину "5" переменной $а $sum = 50 + $some_int; // Присвоить сумму 50 + $some_int переменной $sum Swine = "Zinfandel"; // Присвоить строку "Zinfandel" переменной $wine $inventory++: // Увеличить значение $inventory на 1 Объединяя операторы и операнды, вы получите более сложные выражения для выполнения нетривиальных вычислений. Пример: $total_cost = $cqst + (Scost * 0.06): // прибавить к цене 6-процентный налог Приоритет операторов Приоритет является характеристикой операторов, определяющей порядок выполнения действий с окружающими операндами. В РНР используются те же правила приоритета, что и в школьном курсе математики. Пример: $total_cost = $cost + $cost * 0.06; Приведенная команда эквивалентна следующей: $total cost = $cost + ($cost * 0.06); Это объясняется тем, что оператор умножения обладает более высоким приоритетом по сравнению с оператором сложения. Ассоциативность операторов Ассоциативность оператора определяет последовательность выполнения операторов с одинаковым приоритетом (см. табл. 3.1). Выполнение может происходить в двух направлениях: либо слева направо, либо справа налево. При ассоциативности первого типа операции, входящие в выражение, выполняются слева направо. Например, команда $value = 3*4*5*7*2; эквивалентна следующей команде: $value = ((((3 * 4) * 5) * 7) * 2); Результат вычислений равен 840. Это объясняется тем, что оператор умножения (*) обладает левосторонней ассоциативностью. Операторы с правосторонней ассоциативностью и одинаковым приоритетом обрабатываются справа налево. Например, фрагмент $с = 5; $value = $а - $b - $с; эквивалентен фрагменту $c = 5; $value = ($а - ($b - $с)); При обработке этого выражения переменным $value, $a, $b и $с будет присвоено значение 5. Это объясняется тем, что оператор присваивания (=) обладает правосторонней ассоциативностью. Математические операторы Математические операторы (табл. 3.2) предназначены для выполнения различных математических операций и часто применяются в большинстве программ РНР. К счастью, их использование обходится без проблем. Таблица 3.2. Математические операторы Пример Название Результат $а + $b Сложение Сумма $а и $b $а-$b Вычитание Разность $а и $b $а*$b Умножение Произведение $а и $b $а/$b Деление Частное от деления $а на $b $а % $b Остаток Остаток от деления $а на $b РНР содержит широкий ассортимент стандартных математических функций для выполнения основных преобразований и вычисления логарифмов, квадратных корней, геометрических величин и т. д. За обновленным списком таких функций обращайтесь к документации. Операторы присваивания Операторы присваивания задают новое значение переменной. В простейшем варианте оператор присваивания ограничивается изменением величины, в других вариантах (называемых сокращенными операторами присваивания) перед присваиванием выполняется некоторая операция. Примеры таких операторов приведены в табл. 3.3. Таблица 3.3. Операторы присваиванияПример Название Результат $а = 5; Присваивание Переменная $а равна 5 $а += 5; Сложение с присваиванием Переменная $а равна сумме $а и 5 $а *= 5; Умножение с присваиванием Переменная $а равна произведению $а и 5 $а/=5; Деление с присваиванием Переменная $а равна частному отделения $а на 5 $а .= 5; Конкатенация с присваиванием Переменная $а равна конкатенации $а и 5 Умеренное использование операторов присваивания обеспечивает более наглядный и компактный код. Строковые операторы Строковые операторы РНР (табл. 3.4) обеспечивают удобные средства конкатенации (то есть слияния) строк. Существует два строковых оператора: оператор конкатенации (.) и оператор конкатенации с присваиванием (.=), описанный в предыдущем разделе «Операторы присваивания». Конкатенацией называется объединение двух и более объектов в единое целое. Таблица 3.4. Строковые операторы Пример Название Результат $a = "abc"."def" Конкатенация Переменной $а присваивается результат конкатенации $а и $b $а - "ghijkl" Конкатенация с присваиванием Переменной $а присваивается результат конкатенации ее текущего значения со строкой "ghijkl" Пример использования строковых операторов: // $а присваивается строковое значение "Spaghetti & Meatballs" $а = "Spaghetti" . "& Meatballs" // $а присваивается строковое значение "Spaghetti & Meatballs are delicious" $a .= "are delicious"; Конечно, два строковых оператора не исчерпывают всех возможностей РНР по обработке строк. За полной информацией об этих возможностях обращайтесь к главе 8. Операторы инкремента и декремента Удобные вспомогательные операторы инкремента (++) и декремента (--), приведенные в табл. 3.5, делают программу более наглядной и обеспечивают укороченную запись для увеличения или уменьшения текущего значения переменной на 1. Таблица 3.5. Операторы инкремента и декрементаПример Название Результат ++$а, $а++ Инкремент Переменная $а увеличивается на 1 --$а, $а-- Декремент Переменная $а уменьшается на 1 Интересный факт: эти операторы могут располагаться как слева, так и справа от операнда. Действия, выполняемые оператором, зависят от того, с какой стороны от операнда он находится. Рассмотрим следующий пример: $inventory = 15; // Присвоить Sinventory целое число 15 $old_inv = Sinventory--; // СНАЧАЛА присвоить $old_inv значение // Sinventory. а ЗАТЕМ уменьшить Sinventory. $orig_iinventory = ++inventory;// СНАЧАЛА увеличить Sinventory. а ЗАТЕМ // присвоить увеличенное значение Sinventory // переменной $orig_inventory. Как видите, расположение операторов инкремента и декремента оказывает сильное влияние на результат вычислений. Логические операторы Логические операторы (табл. 3.6) наряду с математическими операторами играют важную роль в любом приложении РНР, обеспечивая средства для принятия решений в зависимости от значения переменных. Логические операторы позволяют управлять порядком выполнения команд в программе и часто используются в управляющих конструкциях (таких, как условная команда i f, а также циклы for и while). Таблица 3.6. Логические операторы Пример Название Результат $а && $b Конъюнкция Истина, если истинны оба операнда , $aAND$b Конъюнкция Истина, если истинны оба операнда $а И $b Дизъюнкция Истина, если истинен хотя бы один из операндов $а OR $b Дизъюнкция Истина, если истинен хотя бы один из операндов !$а Отрицание Истина, если значение $а ложно NOT !$a Отрицание Истина, если значение $а ложно $а XOR $b Исключающая дизъюнкция Истина, если истинен только один из операндов Логические операторы часто используются для проверки результата вызова функций: file_exists("filename.txt") OR print "File does not exist!"; Возможен один из двух вариантов: файл filename.txt существует; будет выведено сообщение: «File does not exist!». Операторы равенства Операторы равенства (табл. 3.7) предназначены для сравнения двух величин и проверки их эквивалентности. Таблица 3.7. Операторы равенства Пример Название Результат $a==$b Проверка равенства Истина, если $а и $b равны $а != $b Проверка неравенства Истина, если $а и $b не равны $а === $b Проверка идентичности Истина, если $а и $b равны и имеют одинаковый тип Даже опытные программисты часто допускают одну распространенную ошибку - они пытаются проверять равенство двух величин, используя всего один знак равенства (например, $а = $b). Помните, при такой записи значение $b присваивается $а, и желаемый результат не будет достигнут. Операторы сравнения Операторы сравнения (табл. 3.8), как и логические операторы, позволяют управлять логикой программы и принимать решения при сравнении двух и более переменных. Таблица 3.8. Операторы сравнения Пример Название Результат $a<$b Меньше Истина, если переменная $а меньше $b $a>$b Больше Истина, если переменная $а больше $b $a <= $b Меньше или равно Истина, если переменная $а меньше или равна $b $a >= $b Больше или равно Истина, если переменная $а больше или равна $b ($a-12)?5: -1 Тернарный оператор Если переменная $а равна 12, возвращается значение 5, а если не равна - возвращается 1 Обратите внимание: операторы сравнения предназначены для работы только с числовыми значениями. Хотя возникает искушение воспользоваться ими для сравнения строк, результат, скорее всего, окажется неверным. В РНР существуют стандартные функции для сравнения строковых величин. Эти функции подробно рассматриваются в главе 8. Поразрядные операторы Поразрядные операторы выполняют операции с целыми числами на уровне отдельных битов, составляющих число. Чтобы лучше понять принцип их работы, необходимо иметь хотя бы общее представление о двоичном представлении десятичных чисел. В табл. 3.9 приведены примеры десятичных чисел и соответствующих им двоичных представлений. Таблица 3.9. Десятичные числа и их двоичные представленияДесятичное целое Двоичное представление 2 10 5 101 10 1010 12 1100 145 10010001 1 452 012 1011000100111111101100 Поразрядные операторы, перечисленные в табл. 3.10, представляют собой особый случай логических операторов, однако они приводят к совершенно иным результатам. Таблица 3.10. Поразрядные операторы Пример Название Результат $а&$b Конъюнкция С битами, находящимися в одинаковых разрядах $а и $b, выполняется операция конъюнкции $а|$Ь Дизъюнкция С битами, находящимися в одинаковых разрядах $а и $b, выполняется операция дизъюнкции $а^$b Исключающая С битами, находящимися в одинаковых разрядах $а и $b, выполняется операция исключающей дизъюнкции ~$b Отрицание Все разряды переменной $b инвертируются $а << $b Сдвиг влево Переменной $а присваивается значение $b, сдвинутое влево на два бита $а >> $b Сдвиг вправо Переменной $а присваивается значение $b, сдвинутое вправо на два бита Если вам захочется больше узнать о двоичном представлении и поразрядных операторах, я рекомендую обратиться к обширному электронному справочнику Рэндалла Хайда (Randall Hyde) «The Art of Assembly Language Programming», доступному по адресу http://webster.cs.ucr.edu/Page_asm/Page_asm.html. Это лучший из ресурсов, которые мне когда-либо встречались в Web. Управляющие конструкции Управляющие конструкции предоставляют в распоряжение программиста средства для построения сложных программ, способных проверять условия и реагировать на изменения значений входных данных во время работы. Короче говоря, эти структуры управляют выполнением программы. Проверка условий Управляющие конструкции обычно проверяют условия на истинность или ложность, и в зависимости от результата проверки выполняется то или иное действие. Рассмотрим выражение $а == $b. Это выражение истинно, если $а равно $b, и ложно в противном случае. Результат истинного выражения считается равным 1, а результат ложного выражения равен 0. Рассмотрим следующий фрагмент: $а = 5; $b = 5; print $а == $b; В результате выводится значение 1. Если изменить $а или $Ь и присвоить переменной значение, отличное от 5, выводится 0. if Команда if представляет собой разновидность команды выбора, которая вычисляет значение выражения и в зависимости от того, будет ли полученный результат истинным или ложным, выполняет (или не выполняет) блок программного кода. Существует две общих формы команды i f: if (выражение) { блок } и if (выражение) { блок } else { блок } Как упоминалось в предыдущем разделе, проверка условий дает либо истинный, либо ложный результат. Выполнение блоков зависит от результата проверки, причем блок может состоять как из одной, так и из нескольких команд. В следующем примере после проверки условия выбирается и выводится одно из двух утверждений: if ($cooking_weight < 200) { print "This is enough pasta (< 200g) for 1-2 people"; } else { print "That's a lot of pasta. Having a party perhaps?"; } Если в результате проверки условия выполняется всего одна команда, фигурные скобки не обязательны: if ($cooking_weight < 100) print "Are you sure this is enough?"; elseif Команда elseif добавляет в управляющую конструкцию if дополнительный уровень проверки и увеличивает количество условий, на основании которых принимается решение: if (выражение) { блок } elseif (выражение) { блок } В РНР существует альтернативное представление команды elself - в виде двух отдельных слов else if. Оба варианта приводят к одинаковым результатам, а альтернативное представление поддерживается исключительно для удобства. Команда elself особенно полезна в тех случаях, когда происходит последовательное уточнение проверяемых условий. Обратите внимание: условие elself вычисляется лишь в том случае, если все предшествующие условия if и elself оказались ложными. if ($cooking_weight < 200) { print "This is enough pasta (< 200g) for 1-2 people"; } elseif ($cooking_weight < 500) { print "That's a lot of pasta. Having a party perhaps?"; } } else { print "Whoa! Who are you cooking for, a football team?"; } Вложенные команды if Вложение команд i f обеспечивает максимальный контроль над проверкой условий. Давайте исследуем эту возможность, усовершенствовав пример из предыдущих разделов. Предположим, вес продукта должен проверяться лишь в том случае, если речь идет о пасте (макаронных изделиях): // Проверить значение $pasta if ($food == "pasta") { // Проверить значение $cooking_weight if ($cooking_weight < 200) { print "This is enough pasta (< 200g) for 1-2 people"; } elseif ($cooking_weight < 500) { print "That's a lot of pasta. Having a party perhaps?"; } else { print "Whoa! Who are you cooking for. a football team?"; } } Как видно из приведенного кода, вложенные команды if позволяют лучше управлять логикой работы программы. Вскоре, с увеличением объемов и сложности ваших программ, вы убедитесь, что вложение управляющих конструкций является неоценимым приемом в арсенале программиста. Вычисление нескольких условий При выборе логики работы программы в управляющей структуре можно проверять комбинацию сразу нескольких условий: if ($cooking_weight < 0) { print "Invalid cooking weight!"; } if ( ($cooking_weight > 0) && ($cooking_weight < 200) ) { print "This is enough pasta (< 200g) for 1-2 people"; } elseif ( ($cooking_weight > 200) && ($cooking_weight < 500) ) { print "That's a lot of pasta. Having a party perhaps?"; } else { print "Whoa! Who are you cooking for, a football team?"; } Проверка сложных условий позволяет устанавливать интервальные ограничения, обеспечивающие более четкий контроль над логикой выполнения программы и уменьшающие количество лишних управляющих конструкций, в результате чего программа становится более понятной. Альтернативное ограничение блоков В управляющих структурах используются специальные ограничители, определяющие границы блоков. Фигурные скобки ({ }) уже упоминались выше. Для удобства программистов в РНР поддерживается альтернативный формат ограничения блоков: if (выражение) : блок else : блок endif; Следовательно, две приведенных ниже команды if полностью эквивалентны: if ($а== $b) { print "Equivalent values!"; } if ($a == $b) : print "Equivalent values!"; endif; while Конструкция while предназначена для многократного (циклического) выполнения блока команд. Блок команды while выполняется до тех пор, пока условие цикла остается истинным. Общая форма цикла while выглядит так: while (выражение) : блок endwhile; Рассмотрим использование цикла while на примере вычисления факториала (n!), где n = 5: $n = 5; $nсору = $n; $factorial = 1; // Установить начальное значение факториала while ($n > 0) : $factorial - $n * $factorial; $n--; // Уменьшить $n на 1 endwhile; print "The factorial of $ncopy is $factorial."; Программа выводит следующий результат: The factorial of 5 is 120. В этом примере $n уменьшается в конце каждой итерации. Условие цикла не должно быть истинным в тот момент, когда переменная $n станет равна 0, поскольку величина $factorial умножится на 0 - конечно, этого быть не должно. В приведенном примере условие цикла следовало бы оптимизировать и привести его к виду $n > 1, поскольку умножать $factorial на 1 бессмысленно - число от этого не изменится. Хотя ускорение работы программы будет ничтожно малым, такие факторы всегда принимаются во внимание с ростом объема и сложности программ. do. .while Цикл do. .while работает почти так же, как и цикл while, описанный в предыдущем разделе, однако в do. .while условие проверяется не в начале, а в конце каждой итерации. Учтите, что цикл do. .while всегда выполняется хотя бы один раз, а цикл while может вообще не выполняться, если перед входом в цикл условие окажется ложным: do: блок while (выражение); Давайте пересмотрим пример с вычислением факториала и перепишем его с использованием конструкции do. .while: $n = 5: $ncopy = $n; $factorial = 1; // Установить начальное значение факториала do { $factorial = $n * $factorial; $n--: // Уменьшить Sn на 1 } while (Sn > 0); print "The factorial of Sncopy is $factorial."; При выполнении этого примера будет получен тот же результат, что и при выполнении его прототипа из предыдущего раздела. В цикле do. .while не поддерживается альтернативный синтаксис (ограничение блоков при помощи : и завершающего ключевого слова), поэтому блок может заключаться только в фигурные скобки. for Цикл for обеспечивает еще одну возможность многократного выполнения блоков. Он отличается от цикла while только тем, что условие изменяется в самой управляющей конструкции, а не где-то внутри блока команд. Как и в случае с циклом while, цикл выполняется до тех пор, пока проверяемое условие остается истинным. Общая форма конструкции for выглядит так: for (инициализация: условие; приращение) { блок } Условная часть цикла for в действительности состоит из трех компонентов. Инициализация выполняется всего один раз и определяет начальное значение управляющей переменной цикла. Условие проверяется в начале каждой итерации и определяет, должна ли выполняться текущая итерация или нет. Наконец, приращение определяет изменение управляющей переменной при каждой итерации. Возможно, термин «приращение» в данном случае неточен, поскольку переменная может как увеличиваться, так и уменьшаться в соответствии с намерениями программиста. Следующий пример демонстрирует простейший случай применения цикла for: for ($i = 10; $1 <- 100: $1 +=10) : // Обратная косая черта предотвращает print "\$i = $i
"; endfor; // возможную интерполяцию переменной $1 Выполнение этого фрагмента дает следующий результат: $i = 10 $i = 20 $i = 30 $i = 40 $i - 50 $i = 60 $i = 70 $i = 80 $i = 90 $i = 100 В этом примере управляющая переменная $i инициализируется значением 10. Условие заключается в том, что цикл продолжается до тех пор, пока $i не достигнет или не превысит пороговую величину 100. Наконец, при каждой итерации значение $i увеличивается на 10. В результате команда print выполняется 10 раз, каждый раз выводя текущее значение $i. Обратите внимание: для увеличения $i на 10 используется оператор сложения с присваиванием. Для этого есть веские причины, поскольку циклы for в РНР не поддерживают более традиционной записи $i = $i + 10. Кстати, этот пример можно записать и в другом виде, но с теми же результатами: for ($i = 10; $i <= 100; print "\$i - $i
". $i+=10); Многие новички не понимают, зачем создавать несколько разновидностей циклов в языке программирования, будь то РНР или какой-нибудь другой язык. Почему нельзя обойтись одной циклической конструкцией? Дело в том, что у цикла for существует несколько специфических особенностей. Например, вы можете инициализировать несколько переменных одновременно, разделяя команды инициализации запятыми: for ($x=0,$y=0: $x+$y<10; $x++) : $У += 2; // Увеличить $у на 2 print "\$y = $y
"; // Вывести значение $у $sum = $x + $y; print "\surn = $sum
"; // Вывести значение $sum endfor; Результат: $y = 2 $sum = 2 Sy = 4 $sum = 5 $y = 6 $sum = 8 $y = 8 $sum = 11 Этот пример выводит текущие значения $y и суммы $х и $у. Как видно из приведенных результатов, выводится значение $sum = 11, хотя эта сумма выходит за границы условия цикла ($х + $у < 10). Это происходит из-за того, что при входе в данную итерацию переменная $у была равна 6, а переменная $х была равна 2. Значения переменных соответствовали условию цикла, поэтому $х и $у были присвоены новые значения, в результате чего была выведена сумма И. При очередной проверке условия сумма 11 превысила пороговое значение 10 и цикл завершился. В управляющих выражениях циклов for могут отсутствовать любые компоненты. Например, вы можете передать ранее инициализированную переменную прямо в цикл, не присваивая ей определенного начального значения. Возможны и другие ситуации - например, приращение переменной цикла может осуществляться в зависимости от некоторого условия, определяемого в цикле. В этом случае приращение не должно указываться в управляющем выражении. Пример: $х = 5: for (: : $х +=2) : print " $х "; if ($x == 15) : break; // Выйти из цикла for endif; endfor; Результат выглядит так: 5 7 9 11 13 15 Хотя циклические конструкции for и while выполняют практически одинаковые функции, считается, что цикл for делает программу более наглядной. Это объясняется тем, что программист при виде команды for немедленно получает всю необходимую информацию о механике и продолжительности цикла. С другой стороны, в командах while приходится тратить лишнее время на поиск обновлений управляющих переменных - в больших программах это может занимать немало времени. foreach Конструкция foreach представляет собой разновидность for, включенную в язык для упрощения перебора элементов массива. Существуют две разновидности команды foreach, предназначенные для разных типов массивов: foreach (массив as $элемент) { блок } foreach (массив as $ключ => $элемент) { блок } Например, при выполнении следующего фрагмента: $menu = аrrау("pasta", "steak", "potatoes", "fish", "fries"); foreach ($menu as $item) { print "$item
"; } будет выведен следующий результат: pasta steak potatoes fish fries В этом примере следует обратить внимание на два обстоятельства. Во-первых, конструкция foreach автоматически возвращается в начало массива (в других циклических конструкциях этого не происходит). Во-вторых, нет необходимости явно увеличивать счетчик или иным способом переходить к следующему элементу массива - это происходит автоматически при каждой итерации foreach. Второй вариант используется при работе с ассоциативными массивами: $wine_inventory = array { "merlot" => 15, "zinfandel" => 17, "sauvignon" => 32 } foreach ($wine_inventory as $i => $item_count) { print "$item_count bottles of $i remaining
"; } В этом случае результат выглядит так: 15 bottles of merlot remaining 17 bottles of zinfandel remaining 32 bottles of sauvignon remaining Как видно из приведенных примеров, конструкция foreach заметно упрощает работу с массивами. За дополнительной информацией о массивах обращайтесь к главе 5. switch Принцип работы конструкции switch отчасти напоминает if - результат, полученный при вычислении выражения, проверяется по списку потенциальных совпадений. Это особенно удобно при проверке нескольких значений, поскольку применение switch делает программу более наглядной и компактной. Общий формат команды switch: switch (выражение) { case (условие): блок case (условие): блок ... default: блок } Проверяемое условие указывается в круглых скобках после ключевого слова switch. Результат его вычисления последовательно сравнивается с условиями в секциях case. При обнаружении совпадения выполняется блок соответствующей секции. Если совпадение не будет обнаружено, выполняется блок необязательной секции default. Как будет показано в следующих главах, одной из сильнейших сторон РНР является обработка пользовательского ввода. Допустим, программа отображает раскрывающийся список с несколькими вариантами и каждая строка списка соответствует некоторой команде, выполняемой в отдельной конструкции case. Реализацию очень удобно построить на использовании команды switch: $user_input = "recipes"; // Команда,выбранная пользователем switch ($user_input) : case("search") : print "Let's perform a search!"; break; case("dictionary") : print "What word would you like to look up?"; break; case("recipes") : print "Here is a list of recipes..."; break; default : print "Here is the menu..."; break; endswitch; Как видно из приведенного фрагмента, команда switch обеспечивает четкую и наглядную организацию кода. Переменная, указанная в условии switch (в данном примере - $user_input), сравнивается с условиями всех последующих секций case. Если значение, указанное в секции case, совпадает Со значением сравниваемой переменной, выполняется блок этой секции. Команда break предотвращает проверку дальнейших секций case и завершает выполнение конструкции switch. Если ни одно из проверенных условий не выполняется, активизируется необязательная секция default. Если секция default отсутствует и ни одно из условий не выполняется, команда switch просто завершается и выполнение программы продолжается со следующей команды. Вы должны помнить, что при отсутствии в секции case команды break (см. следующий раздел) выполнение switch продолжается со следующей команды до тех пор, пока не встретится команда break или не будет достигнут конец конструкции switch. Следующий пример демонстрирует последствия отсутствия забытой команды break: $value = 0.4; switch($value) : case (0.4) : print "value is 0.4
"; case (0.6) : print "value is 0.6
"; break; case (0.3) : print "value is 0.3
"; break; default : print "You didn't choose a value!"; break; endswitch; Результат выглядит так: value is 0.4 value is 0.6 Отсутствие команды break привело к тому, что была выполнена не только команда print в той секции, где было найдено совпадение, но и команда print в следующей секции. Затем выполнение команд конструкции switch прервалось из-за команды switch, следующей за второй командой print. Выбор между командами switch и if практически не влияет на быстродействие про-граммы. Решение об использовании той или иной конструкции является скорее личным делом программиста. break Команда break немедленно прерывает выполнение той конструкции while, for или switch, в которой она находится. Эта команда уже упоминалась в предыдущем разделе, однако прерывание текущего цикла не исчерпывает возможностей команды break. В общем виде синтаксис break выглядит так: break n; Необязательный параметр n определяет количество уровней управляющих конструкций, завершаемых командой break. Например, если команда break вложена в две команды while и после break стоит цифра 2, происходит немедленный выход из обоих циклов. По умолчанию значение n равно 1; выход на один уровень может обозначаться как явным указанием 1, так и указанием команды break без параметра. Обратите внимание: команда i f не относится к числу управляющих конструкций, прерываемых командой break. Об этом следует помнить при использовании необязательного параметра п. Рассмотрим пример использования команды break в цикле foreach: $arr = array(14, 12, 128, 34, 5); $magic number = 128: foreach ($arr as $val) : if (Sval == $magic_number) : print "The magic number is in the array!"; break; endif; print "val is Sval
"; endforeach; Если значение $magic_number присутствует в массиве $аrr (как в приведенном примере), поиск прерывается. Результат выглядит так: val is 14 val is 12 The magic number is in the array! Приведенный пример всего лишь демонстрирует использование команды break. В РНР существует стандартная функция in_array( ), предназначенная для поиска заранее заданной величины в массиве; эта функция подробно описана в главе 5. continue Остается рассмотреть еще одну управляющую конструкцию РНР - continue. При выполнении команды continue в цикле пропускаются все оставшиеся команды текущей итерации и немедленно начинается новая итерация. Синтаксис команды continue в общем виде: continue n; Необязательный параметр n указывает, на сколько уровней внешних циклов распространяется действие continue. Рассмотрим пример использования команды continue. Допустим, вы хотите сосчитать простые числа в интервале от 0 до некоторой заданной границы. Простоты ради предположим, что у нас имеется функция is_prime(), которая проверяет, является число простым или нет: $boundary = 558; for ($i = 0; $i <= $boundary: $i++) : if (! is_prime($i)) : continue; endif; $prime_counter++; endfor; Если проверяемое число является простым, блок команды if обходится и переменная $prime_counter увеличивается. В противном случае выполняется команда continue, в результате чего происходит немедленный переход в начало следующей итерации. Использование continue в длинных и сложных алгоритмах приводит к появлению запу- танного и невразумительного кода. В подобных случаях использовать continue не рекомендуется. Команда continue не является безусловно необходимой в программах, поскольку аналогичного эффекта можно добиться при помощи команды if. Проект: календарь событий Для практической демонстрации многих концепций, рассмотренных ранее, я завершаю эту главу описанием программы-календаря. В календаре хранится информация о последних кулинарных мероприятиях, семинарах по дегустации вин и любых других событиях, которые вы сочтете нужным в него включить. В этом проекте задействованы многие концепции, описанные в этой главе, а также представлен ряд новых концепций, которые будут рассматриваться в следующих главах. Информация о событиях хранится в обычном текстовом файле и выглядит примерно так: July 21, 2000|8 p. m.|Cooking With Rasmus|PHP creator Rasmus Lerdorf discusses the wonders of cheese. July 23, 2000|11 a. m.|Boxed Lunch|Valerie researches the latest ham sandwich making techniques (documentary) July 31, 2000|2:30p.m.|Progressive Gourmet|Forget the Chardonnay: iced tea is the sophisticated gourmet's beverage of choice. August 1, 2000|7 p.m.|Coder's Critique|Famed Food Critic Brian rates NYC's hottest new Internet cafes. August 3, 2000|6 p.m.|Australian Algorithms|Matt studies the alligator's diet. На рис. 3.1 изображен результат работы сценария РНР, приведенного в листинге 3.1. Рис. З.1. Примерный вид календаря Прежде чем переходить к подробному анализу кода, потратьте немного времени на изучение алгоритма: Открыть файл, содержащий информацию о событиях. Разделить каждую строку на 4 элемента: дату, время, название и краткое описание мероприятия. Отформатировать и вывести данные. Закрыть файл. Листинг 3.1. Сценарий для вывода содержимого events.txt в браузере " print""Events Calendar:"; // Читать, пока не будет найден конец файла while (! feof(Sevents)) : // Прочитать следующую строку файла events.txt $event = fgets($events. 4096); // Разделить компоненты текущей строки на элементы массива $event_info = explode("|". Jevent); // Отформатировать и вывести информацию о событии print "$event_info[0] ( $event_info[1] )
"; print "$event_info[2]
"; print "$event_info[3]

"; endwhile; // Завершить таблицу print ""; fclose ($events); ?> Этот короткий пример убедительно доказывает, что РНР позволяет даже неопытным программистам создавать реальные приложения с минимальными усилиями и затратами времени. Если какие-нибудь из представленных концепций покажутся непонятными, не огорчайтесь - на самом деле они очень просты и будут подробно описаны в следующих главах. А если вам не терпится узнать побольше об этих вопросах, обратитесь к главе 7 «Файловый ввод/вывод и файловая система» и главе 8 «Строки и регулярные выражения» поскольку большая часть незнакомого синтаксиса описана именно там. Итоги В этой главе были представлены выражения и управляющие конструкции - средства языка РНР, которые, вероятно, в той или иной форме присутствуют практически в любом сценарии. Мы рассмотрели некоторые вопросы использования этих средств, а именно: операторы; операнды; приоритет операторов; ассоциативность операторов; управляющие структуры (If, while, do. .while, for, foreach, switch, break, continue). В первых трех главах книги вы познакомились с базовыми компонентами языка РНР. Остальные пять глав первой части расширяют этот материал; в них вы найдете дополнительную информацию о работе с массивами, объектно-ориентированных возможностях, файловом вводе/выводе и строковых операциях РНР. Весь этот материал готовит читателя ко второй части книги, причем особое внимание уделяется средствам РНР, часто используемым при построении приложений. Итак, держитесь покрепче и не сходите с дистанции! ГЛАВА 4 Функции В этой главе представлены общие концепции функционального программирования - одного из самых влиятельных принципов в области разработки приложений. При помощи функций можно создавать компоненты многократного использования, которые легко изменяются при необходимости и оказываются особенно удобными при разработке web-приложений, не содержащих серьезных различий на концептуальном и практическом уровне. Функциональное программирование помогает создавать более короткие и удобочитаемые программы. Данная глава посвящена функциям РНР, их определению и применению на практике. Хотя основное внимание в ней уделяется определению и вызову пользовательских функций, необходимо помнить и о том, что в РНР существуют сотни стандартных функций. Стандартные функции работают точно так же, как пользовательские, и обеспечивают заметную экономию времени при создании новых приложений. Обновленный список стандартных функций РНР можно найти по адресу http://www.php.net. Что такое функция? Функцией называется фрагмент программного кода, обладающий уникальным именем и предназначенный для решения конкретной задачи. Функция вызывается по имени в разных точках программы, что позволяет многократно выполнять фрагмент с указанным именем. Преимущество такого решения заключается в том, что блок кода пишется всего один раз, а затем легко модифицируется по мере необходимости. Определение и вызов функций Определить новую функцию в РНР несложно. Функции могут создаваться в любой точке программ РНР, однако по соображениям структурной организации кода удобнее разместить все функции, используемые сценарием, в самом начале сценарного файла. Существует и другой способ, заметно повышающий эффективность программирования и способствующий многократному использованию кода, - выделение функций в отдельный файл (называемый библиотекой). Библиотеки удобны тем, что их функции можно использовать в разных приложениях, не создавая лишних копий и не рискуя допустить ошибки в процессе копирования. Эта тема подробно рассматривается в разделе «Построение библиотек функций» ближе к концу главы. Определение функции обычно состоит из трех частей: имени функции; круглых скобок, в которых перечисляются необязательные входные параметры, разделенные запятыми; тела функции, заключенного в фигурные скобки. Обобщенный синтаксис функций РНР выглядит так: function имя_функции ([$параметр1. $параметр2, .... $параметрn]) { тело функции } Имя функции должно подчиняться условиям, приведенным для идентификаторов в главе 2. После имени функции следуют обязательные круглые скобки, в которые заключается необязательный список входных параметров ($параметр1, $параметр2, .... $параметрn). Вследствие относительно либеральных принципов определения переменных в РНР указывать тип входных параметров не нужно. Хотя такой подход имеет свои преимущества, следует помнить, что механизм РНР не проверяет аргументы на соответствие тем типам, которые должны обрабатываться функцией. Случайные ошибки в использовании входных параметров могут привести к неожиданным последствиям (чтобы убедиться в том, что параметр относится к нужному типу, можно проверить его стандартной функцией gettype( )). После закрывающей круглой скобки следуют фигурные скобки, в которые заключается программный код, ассоциируемый с именем функции. Рассмотрим простой пример использования функции. Предположим, вы хотите создать функцию для вывода лицензионной информации на web-странице: function display_copyright() { print "Copyright © 2001 PHP-Powered Recipes. All Rights Reserved."; } Если ваш web-сайт состоит из нескольких страниц, достаточно вызвать эту функцию в конце каждой страницы - и вам не придется заново переписывать один и тот же текст. А когда наступит 2002 год, одно простое изменение текста, выводимого этой функцией, приведет к автоматическому обновлению всех страниц. Если бы не преимущества функционального программирования, вам пришлось бы вручную редактировать все страницы, на которых выводится лицензионная информация. Рассмотрим разновидность функции display_copyright(), которой при вызове передается параметр. Предположим, вы отвечаете за администрирование нескольких web-сайтов, каждому из которых присвоено отдельное имя. На каждом сайте имеется собственный административный сценарий с несколькими переменными, относящимися к этому сайту; к их числу принадлежит переменная $site_name с именем сайта. В этом случае функцию display_copyright() можно записать следующим образом: function display_copyright($site_name) { print "Copyright © 2001 $site_name. All Rights Reserved."; } Переменная $site_name, значение которой присваивается за пределами display_copy-right(), передается функции в качестве параметра. Переданное значение можно использовать и модифицировать в любом месте функции, однако любые изменения будут действовать лишь внутри этой функции. Впрочем, специальные ключевые слова позволяют сделать так, чтобы изменения параметров распространялись и за пределы display_copyright(). Эти ключевые слова были представлены в главе 2, в общем обзоре области видимости переменных и ее отношения к функциям. Вложенные функции Функции можно вызывать внутри других функций - по аналогии с тем, как одна управляющая конструкция (if, while, for и т. д.) может находиться внутри другой. Такая возможность удобна в любых программах, и в больших, и в малых, поскольку она увеличивает степень модульности приложения и упрощает сопровождение программы. В примере, описанном выше, можно полностью избавиться от необходимости модификации даты. Для этого достаточно включить в d1splay_copyright() вызов стандартной функции РНР date( ): function display_copyright($site_name) { print "Copyright ©". date("Y"). "$site_name. All Rights Reserved."; } Параметр Y функции date( ) указывает, что возвращаемое значение представляет собой текущий год, отформатированный в виде четырех цифр. Если системная дата установлена правильно, РНР при каждом выполнении сценария будет выводить год. Функция РНР date( ) отличается исключительной гибкостью и поддерживает 25 разных флагов форматирования даты и времени. Также допускается объявление функций внутри других функций. Тем не менее, вложенное объявление еще не делает функцию «защищенной», то есть не ограничивает возможность ее вызова той функцией, в которой она была объявлена. Более того, вложенная функция не наследует параметров родительской функции; параметры должны передаваться ей точно так же, как и любой другой функции. Впрочем, вложенные объявления функций все равно могут использоваться из соображений удобства сопровождения и наглядности. Пример вложенного объявления приведен в листинге 4.1. Листинг 4.1. Эффективное использование вложенных функций function display_footer($site_name) { function display_copyright($site_name) { print "Copyright &сору". date("Y"). $site_name. All Rights Reserved."; print "
home | recipes | events
tutorials | about I contact us
"; display_copyright($site_name); print "
"; } $site_name = "PHP Recipes": display_footer($site_name); display_copyhght($site_name); Сценарий выводит следующий результат: home | recipes | events tutorials | about | contact us Copyright © 2001 PHP Recipes. All Rights Reserved Обратите внимание: функцию display_copyright( ) можно вызвать и за пределами display_footer( ) по аналогии стем, как функция display_footer( ) использовалась в предыдущем примере. Концепция защищенных функций в РНР не поддерживается. Хотя вложенные функции не защищены от вызова из других точек сценария, они не могут вызываться до вызова своей родительской функции. При попытке вызвать вложенную функцию раньше вызова родительской функции выводится сообщение об ошибке. Возврат значений из функции По завершении работы функции часто бывает полезно вернуть некоторое значение, для чего результат вызова функции обычно присваивается некоторой переменной. Функции могут возвращать значения любых типов, в том числе массивы и списки. Пример приведен в листинге 4.2, где функция calculate_cost( ) вычисляет налог с заданной суммы и возвращает общую сумму вместе с налогом. Прежде чем переходить к рассмотрению листинга, просмотрите краткое описание алгоритма на псевдокоде: Перед вызовом функции задать значения переменных: $price (цена товара) и $tax (налоговая ставка). Объявить функцию calculate_cost( ). При вызове функция получает два параметра: налоговую ставку и цену товара. Вычислить цену с учетом налога и вернуть ее командой return. Вызвать calculate_cost() и присвоить значение, возвращенное функцией, переменной $total_cost. Вывести соответствующее сообщение. Листинг 4.2. Создание функции для вычисления налога $price = 24.99; $tax = .06; function calculate_cost($tax, $price) { $sales_tax = $tax; return $price + ($price * $sales_tax); } // Обратите внимание на возврат значения функцией calculate_cost(). $total_cost = calculate_cost ($tax. $price); // Округлить цену до двух десятичных цифр. $total_cost = round($total_cost. 2); print "Total cost: $".$total_cost; // $total cost = 26.49 Функции, не возвращающие значений, также называются процедурами. Существует и другой способ использования возвращаемых значений, при котором вызов функции включается прямо в условную/циклическую команду. В следующей программе (листинг 4.3) сумма счета пользователя сравнивается с предельным размером кредита. Алгоритм на псевдокоде выглядит так: Объявить функцию check_limit( ), которая при вызове получает два параметра. Первый параметр, $total_cost, определяет общую сумму счета, накопленную пользователем до настоящего момента. Второй параметр, $credit_limit, определяет максимальную сумму, которую может потратить пользователь. Если накопленная сумма счета превышает предельный размер кредита, функция возвращает ложное значение (0). Если условие команды i f оказывается ложным, работа функции еще не завершена. В этом случае общая сумма не превышает предельного размера кредита, поэтому функция должна вернуть логическую истину. Вызвать функцию check_limit( ) в условии команды if. Проверить, какое значение было возвращено при вызове - истинное или ложное. В зависимости от результата проверки выполняется то или иное действие. Если при вызове check_limit( ) было получено значение TRUE, мы предлагаем пользователю продолжить закупку. В противном случае пользователь информируется о превышении кредита. Листинг 4.3. Сравнение текущей суммы счета пользователя с предельным размером кредита $cost = 1456.22; $limit = 1000.00; function check_limit($total_cost. $credit_limit) if ($total_cost > $credit_limit) : return 0; endif; return 1; } if (check_limit($cost. $limit)) : // Продолжить закупки print "Keep shopping!"; else : print "Please lower your total bill to less than $".$limit."!"; endif; При выполнении листинга 4.3 будет выведено сообщение об ошибке, поскольку значение $cost превышает $limit. Функция также может возвращать сразу несколько значений при помощи списка. Продолжая кулинарную тему, давайте напишем функцию, которая бы возвращала три лучших года для указанного сорта вина. Функция приведена в листинге 4.4, но сначала прочитайте алгоритм на псевдокоде: Объявить функцию best_years( ), вызываемую с одним параметром. Параметр $label определяет сорт вина, для которого пользователь хотел бы узнать три рекомендуемых года. Объявить два массива, $merlot и $zinfandel. В каждом массиве хранится три рекомендуемых года для соответствующего сорта вина. Написать команду return, которая бы использовала особые возможности переменных. Выражение $$label сначала интерпретирует переменную $label, а затем интерпретирует полученное значение как имя другой переменной. В настоящем примере массив merlot возвращается в виде списка, и каждый возвращаемый год занимает свою позицию в списке, для которого вызывалась функция. Вывести сообщение с информацией о рекомендуемых годах. Листинг 4.4. Возвращение функцией нескольких величин // Сорт вина, для которого выводятся лучшие годы $label = "merlot"; // Функция использует массивы и "переменную в переменной" // для возвращения нескольких значений. function best_years($label) { $merlot = array("1987", "1983", "1977"); $zinfandel = array("1992", "1990", "1989"); return $$label; } // Функция list( ) используется получения возвращаемых значений. list ($yr_one, $yr_two. $yr_three) = best_years($label); print "$label had three particularly remarkable years: $yr_one. $yr_two, and $yr_three."; Программа выводит следующий результат: merlot has three particularly remarkable years: 1987, 1983 and 1977. Рекурсивные функции Ситуация, при которой функция многократно вызывает сама себя, пока не будет выполнено некоторое условие, открывает замечательные возможности. При правильном использовании рекурсивные функции уменьшают объем программы и делают ее более выразительной. Рекурсивные функции особенно часто используются при выполнении повторяющихся действий - например, при поиске в файлах/массивах и построении графических изображений (например, фракталов). Классическим примером рекурсивных функций, встречающимся во многих курсах программирования, является суммирование чисел от 1 до N. Программа, приведенная в листинге 4.5, суммирует все целые числа от 1 до 10. Листинг 4.5. Использование рекурсивной функции для суммирования последовательных целых чисел function summation ($count) { if ($count != 0) : return $count + summation($count-1); endif; } $sum = summation(10); print "Summation = $sum"; В результате выполнения листинга 4.5 будет выведен следующий результат: Summation = 55 Если функция вызывается достаточно часто, рекурсия делает программу более эффективной. Тем не менее, при использовании рекурсии необходима осторожность, поскольку ошибки могут привести к зацикливанию программы. Функции-переменные Одной из интересных возможностей РНР являются функции-переменные (variable functions), то есть динамические вызовы функций, имена которых определяются во время выполнения программы. Хотя в большинстве web-приложений можно обойтись и без функций-переменных, они значительно сокращают объем и сложность программного кода, а также часто снимают необходимость в условных командах if. Вызов функции-переменной представляет собой имя переменной, за которым следует пара круглых скобок. В круглых скобках могут перечисляться параметры (однако присутствие параметров не обязательно). Обобщенный синтаксис функции-переменной: $имя_функции( ); Следующая программа (листинг 4.6) демонстрирует эту непривычную, но полезную возможность. Допустим, программа выводит разную информацию в зависимости от языка, выбранного пользователем. В нашем примере для простоты используются приветственные сообщения для англо- и италоязычных пользователей. Алгоритм на псевдокоде: Создать сообщение для итальянского языка в функции с именем italian. Создать сообщение для английского языка в функции с именем english. Передать информацию о выбранном языке в сценарий, присвоив значение переменной $language. Переменная $language используется для выполнения функции-переменной (в приведенном примере - italian()). Листинг 4.6. Выбор функции в зависимости от пользовательского ввода // Приветствие на итальянском языке, function italian( ) { " print "Benvenuti al PHP Recipes."; } // Приветствие на английском языке function english( ) { print "Welcome to PHP Recipes."; } // Выбрать итальянский язык $language = "italian": // Выполнить функцию-переменную $language( ); Листинг 4.6 демонстрирует интересную концепцию функций-переменных и наглядно показывает, что функции-переменные способствуют уменьшению объема программного кода. Если бы не эта возможность, функцию пришлось бы выбирать командой if или switch; это привело бы к заметному увеличению объема программного кода и риску появления дополнительных ошибок при кодировании. Построение библиотек функций Библиотеки функций - одно из самых эффективных средств экономии времени при построении приложений. Предположим, вы написали серию функций для сортировки массива. Вероятно, эти функции будут неоднократно использоваться в разных приложениях. Вместо того чтобы постоянно переписывать эти функции в новый сценарий или копировать их через текстовый буфер, гораздо удобнее разместить все функции сортировки в отдельном файле и присвоить ему легко узнаваемое имя (например, array_sorting.inc). Пример такого файла приведен в листинге 4.7. Листинг 4.7. Пример библиотеки функций (array_sorting.inc) Библиотека array_sorting.inc служит накопителем для всех функций сортировки. Это удобно, поскольку функции фактически группируются по своему назначению и при необходимости можно легко найти нужную функцию. Как видно из листинга 4.7, в начало библиотеки обычно включается заголовок из нескольких строк комментария, чтобы при открытии файла библиотеки можно было сразу получить краткую сводку его содержимого. После собственной библиотеки функций можно включить ее в сценарий при помощи команд РНР include( ) и require( ), в результате чего все функции библиотеки становятся доступными. В общем виде синтаксис этих команд выглядит так: include(путь/имя_файла); require(путь/имя_файла); Также существует альтернативный вариант: include "путь/имя_файла"; require "путь/имя_файла"; где путь определяет относительный или абсолютный путь к файлу. Конструкции include( ) и requirе( ) подробно описаны в главе 9. А пока достаточно запомнить, что эти конструкции используются для включения файла непосредственно в сценарий. Предположим, вы хотите воспользоваться функциями библиотеки array_sorting.inc в сценарии. Пример включения библиотеки показан в листинге 4.8. Листинг 4.8. Включение библиотечного файла (array_sorting.inc) в сценарий // Предполагается, что библиотека array_sorting.inc // находится в одном каталоге со сценарием. include("array_sorting.inc"); // Теперь вы можете использовать любые функции из array_sorting.inc $some_array = array (50, 42. 35, 46); // Использовать функцию bubble_sort() $sorted_array = bubble_sort($some_array, 1); Итоги Эта глава посвящена функциям и их применению в РНР. В частности, мы рассмотрели следующие темы: определение и вызов функций; вложенные функции; возврат значений из функции; рекурсивные функции; функции-переменные; построение библиотек функций. Хорошее знание этого материала абсолютно необходимо для понимания концепций, рассматриваемых в следующих главах, поскольку функции используются везде, где это возможно. Как и в других главах, я рекомендую поэкспериментировать с примерами, чтобы лучше усвоить изложенный материал. Глава 5 посвящена массивам - несомненно, эта тема заметно обогатит ваши познания в РНР. В главе 5 мы познакомимся с основными принципами хранения данных, постепенно продвигаясь к более содержательным и, в конечном счете, более интересным приложениям. ГЛАВА 5 Массивы В главе 2 были представлены две разновидности массивов, используемых в программах РНР, - индексируемые и ассоциативные. Вероятно, вы помните, что в индексируемых массивах при обращении к элементу указывается его позиция, а в ассоциативных массивах для этой цели используется специальный ключ. Обе разновидности массивов обладают гибкими и мощными средствами для работы с большими объемами данных В этой главе рассматриваются различные средства РНР по работе с массивами. К концу этой главы вы познакомитесь с одномерными и многомерными массивами, сортировкой и перебором элементов, а также с другими возможностями, часто используемыми при работе с массивами. Книгу не стоит рассматривать как подробный справочник по всем существующим функциям, хотя так уж получилось, что в этой главе рассматриваются практически все функции массивов. Последнюю версию списка функций можно найти на домашней странице РНР по адресу http://www.php.net. Создание массивов Массив представляет собой совокупность объектов, имеющих одинаковые размер и тип. Каждый объект в массиве называется элементом массива. Создать новый массив в РНР несложно. При объявлении индексируемого массива после имени переменной ставится пара квадратных скобок ([ ]): $languages [ ] = "Spanish"; // $languages[0] = "Spanish" После этого в массив можно добавлять новые элементы, как показано ниже. Обратите внимание: новые элементы добавляются без явного указания индекса. В этом случае новый элемент добавляется в позицию, равную длине массива плюс 1: $languages[ ] = "English"; // $1anguages[l] = "English"; $languagest ] = "Gaelic"; // $languages[2] = "Gaelic"; Кроме того, новые элементы можно добавлять в конкретную позицию массива. Для этого указывается индекс нрвого элемента: $languages[15] = "Italian"; $languages[22] = "French"; Ассоциативные массивы создаются аналогичным образом: $languages["Spain"] = "Spanish"; $languages["France"] = "French"; При создании массивов используются три стандартные языковые конструкции: аrrау( ); list( ); range( ). Хотя все три случая приводят к одному результату - созданию массива, в некоторых случаях одна конструкция может оказаться предпочтительнее других. Далее приведены описания и примеры использования каждой конструкции. аггау( ) Функция array( ) получает ноль или более элементов и возвращает массив, состоящий из указанных элементов. Ее синтаксис: array array ( [элемент1, элемент2...] ) Вероятно, array( ) является всего лишь более наглядной записью для создания массива, используемой для удобства программиста. Ниже показан пример использования array( ) для создания индексируемого массива: $languages = array ("English". "Gaelic". "Spanish"); // $languages[0] = "English". $languages[1] = "Gaelic", // $languages[2] = "Spanish" А вот как array( ) используется при создании ассоциативных массивов: $languages = array("Spain" => "Spanish", "Ireland" => "Gaelic". "United States" => "English"); // $languages["Spain"] = "Spanish" // $languages["Ireland"] = "Gaelic" // $languages["United States"] = "English" Ассоциативные массивы особенно удобны в тех ситуациях, когда числовые индексы не имеют логического соответствия. Например, в предыдущем примере названия стран вполне естественно ассоциируются с языками. Попробуйте-ка воплотить эту логическую связь при помощи цифр! list( ) Конструкция list( ) похожа на аrrау( ), однако ее главная задача - одновременное присваивание значений, извлеченных из массива, сразу нескольким переменным. Синтаксис команды list( ): void list (переменная1 [. переменная2 , ...] ) Конструкция list() особенно удобна при чтении информации из базы данных или файла. Допустим, вы хотите отформатировать и вывести данные, прочитанные из текстового файла. Каждая строка файла содержит сведения о пользователе (имя, профессия и любимый цвет); компоненты записи разделяются вертикальной чертой (|). Типичная строка выглядит так: Nino Sanzi|Professional Golfer|green При помощи list ( ) можно написать простой цикл, который будет читать каждую строку, присваивать ее компоненты переменным, форматировать и отображать данные. Приведенный ниже листинг демонстрирует возможность одновременного присваивания нескольким переменным с использованием list ( ): // Читать строки, пока не будет достигнут конец файла while ($line = fgets ($user_file. 4096)) : // Разделить строку функцией split( ). // Компоненты присваиваются переменным Sname. $occupation и Scolor. list ($name, $occupation, $color) = split( "|", $line); // Отформатировать и вывести данные print "Name: Sname
"; print "Occupation: Soccupation
"; print "Favorite color: Scolor
"; endwhile; Каждая строка файла читается, форматируется и выводится в следующем виде: Name: Nino Sanzi Occupation: Professional Golfer Favorite color: green В приведенном примере применение list( ) зависит от разделения строки на элементы функцией split( ). Элементы, полученные в результате деления, присваиваются, соответственно, переменным $name, $occupation и $color. Дальше все сводится к форматированию данных для вывода в конкретном браузере. Удобные средства лексического анализа текстовых файлов являются одной из сильных сторон РНР. Эта тема подробно рассматривается в главах 7 и 8. range ( ) Конструкция range( ) позволяет легко и быстро создать массив целых чисел из интервала, определяемого верхней и нижней границами. Range( ) возвращает массив, состоящий из всех целых чисел указанного интервала. Синтаксис range( ): array range (int нижняя_граница, int верхняя граница) Следующий пример наглядно показывает, насколько удобна эта конструкция: $lottery = range(0,9); // $lottery = array(0,1,2,3,4,5,6,7,8,9) Как видно из приведенного фрагмента, в параметрах range( ) был указан интервал от 0 до 9 и массив $lottery был заполнен целыми числами из этого интервала. Многомерные массивы Со временем ваши программы станут более сложными, и возможностей простых одномерных массивов окажется недостаточно для хранения необходимой информации. Многомерный массив (массив массивов) предоставляет в распоряжение программиста более эффективные средства для хранения информации, требующей дополнительного структурирования. Создать многомерный массив несложно - просто добавьте дополнительную пару квадратных скобок, чтобы вывести массив в новое измерение: $chessboard [1] [4] = "King"; // Двухмерный массив $capitals["USA"] ["Ohio"] = "Columbus": // Двухмерный массив $streets["USA"]["Ohio"]["Columbus"] = "Harrison"; // Трехмерный массив В качестве примера рассмотрим массив, в котором хранится информация о десертах и особенностях их приготовления. Обойтись одномерным массивом было бы довольно трудно, но двухмерный массив подходит как нельзя лучше: $desserts = аrrау( "Fruit Cup" => array ( "calories" => "low", "served" -> "cold", "preparation" => "10 minutes" ), "Brownies" => array ( "calories" -> "high", "served" => "piping hot", "preparation" => "45 minutes" ) ); После создания массива к его элементам можно обращаться по соответствующим ключам: $desserts["Fruit Cup"]["preparation"] // возвращает "10 minutes" $desserts["Brownies"]["calories"] // возвращает "high" Присваивание значений элементам многомерных массивов выполняется так же, как и в одномерных массивах: $desserts["Cake"]["calories"] = "too many"; // Присваивает свойству "calories" объекта "Cake" значение "too many" Хотя в многомерных массивах появляются новые уровни логической организации данных, многомерные массивы создаются практически так же, как и одномерные. Впрочем, ссылки на многомерные массивы в строках требуют особого внимания; этой теме посвящен следующий раздел. Ссылки на многомерные массивы Ссылки на элементы многомерных массивов внутри строк несколько отличаются от ссылок на другие типы данных. Возможны два варианта. Во-первых, можно воспользоваться оператором конкатенации: print "Brownies are good, but the calories content is ". $desserts["Brownies"]["calories"]; Во-вторых, ссылку на элемент многомерного массива можно заключить в фигурные скобки ({ }): print "Brownies are good, but the calories content is {$desserts[Brownies][calories]}"; Обратите внимание на отсутствие кавычек вокруг ключей. Также следует помнить, что между фигурными скобками и ссылкой не должно быть лишних пробелов. Если хотя бы одно из этих условий не выполняется, произойдет ошибка. Впрочем, годятся оба способа. Я рекомендую выбрать один формат и придерживаться его, чтобы ваши программы выглядели более последовательно. Если не использовать какой-либо из этих способов форматирования, ссылки на многомерные массивы будут интерпретироваться буквально, что наверняка приведет к непредвиденным результатам. Поиск элементов массива Поиск элементов относится к числу важнейших операций с массивами. В РНР существует несколько стандартных функций, позволяющих легко находить в массиве нужные ключи и значения. in_array( ) Функция i n_array ( ) проверяет, присутствует ли в массиве заданный элемент. Если поиск окажется удачным, функция возвращает TRUE, в противном случае возвращается FALSE. Синтаксис функции in_array( ): bool in_array(mixed элемент, array массив) Эта функция особенно удобна тем, что вам не приходится в цикле перебирать весь массив в поисках нужного элемента. В следующем примере функция in_array( ) ищет элемент "Russian" в массиве $languages: $languages = array("English", "Gaelic", "Spanish"): $exists = in_array("Russian", $languages); // $exists присваивается FALSE $exists = in_array("English", $languages): // $exists присваивается TRUE Функция in_array( ) часто встречается в управляющих конструкциях, когда ее возвращаемое значение (TRUE/FALSE) используется для выбора одного из двух вариантов продолжения. В следующем примере функция in_array( ) используется для выбора одного из двух вариантов в условной команде if: // Ввод данных пользователем $language = "French"; $email = "wjgilmore@hotmail.com"; // Если язык присутствует в массиве if (in_array($language. $languages)) : // Подписать пользователя на бюллетень. // Обратите внимание: в РНР нет стандартной функции с именем // subscribe_user(). В данном примере эта функция просто имитирует // процесс подписки. subscribe_user($email, $language); print "You are now subscribed to the $language edition of the newsletter."; // Язык отсутствует в массиве else : print "We're sorry, but we don't yet offer a $language edition of the newsletter". endif; Что происходит в этом примере? Допустим, переменные $language и $email содержат данные, введенные пользователем. Вы хотите убедиться в том, что указанный язык поддерживается вашей системой, и используете для этой цели функцию in_array( ). Если название языка присутствует в массиве, пользователь подписывается на бюллетень и получает соответствующее сообщение. В противном случае программа сообщает, что на указанном языке бюллетень не распространяется. Конечно, в настоящей программе пользователь не должен гадать, какие языки поддерживаются вашей программой. Задача решается при помощи раскрывающегося списка - эта тема подробно рассматривается в главе 10. Здесь этот пример всего лишь демонстрирует возможности работы с массивами. array_keys( ) Функция array_keys( ) возвращает массив, содержащий все ключи исходного массива, переданного в качестве параметра. Если при вызове передается дополнительный параметр искомый_элемент, возвращаются только ключи, которым соответствует заданное значение; в противном случае возвращаются все ключи массива. Синтаксис функции array_keys( ): array array_keys (array массив [, mixed искомый_элемент]) Рассмотрим пример использования функции array_keys( ) для получения ключа заданного элемента: $great_wines = array ("Australia" => "Clarendon Hills 96", "France" => "Comte George de Vogue 97", "Austria" => "Feiler Artinger 97"); $great_labels = array_keys($great_wines); // $great_labels = array("Australia", "France", "Austria"); $great_labels = array_keys($great_wines, "Clarendon Hills 96"); // $great_labels = array("Australia"); Функция array_keys( ) позволяет очень легко получить все ключи ассоциативного массива - например, в предыдущем случае ими были названия стран, в которых производятся различные сорта вин. array_values( ) Функция array_values( ) возвращает массив, состоящий из всех значений исходного массива, переданного в качестве параметра. Синтаксис функции array_values( ): array array_values(array массив) Вернемся к предыдущему примеру, в котором функция array_keys( ) использовалась для получения всех значений ключей. На этот раз функция array_values( ) возвращает все значения, соответствующие ключам: // $great_wines = array ("Australia" => "Clarendon Hills 96", // "France" => "Comte George de Vogue 97", // "Austria" => "Feiler Artinger 97"); $great_labels = array_values($great_wines); // $great_labels = аrrау("Clarendon Hills 96", // "Comte George de Vogue 97", // "Feiler Artinger 97"); Функции array_keys( ) и array_values( ) дополняют друг друга, позволяя при необходимости получить все составляющие той или иной стороны ассоциативного массива. Добавление и удаление элементов К счастью, в РНР при создании массива не нужно указывать максимальное количество элементов. Это увеличивает свободу действий при операциях с массивами, поскольку вам не приходится беспокоиться о случайном выходе за границы массива, если количество элементов превысит ожидаемый порог. В РНР существует несколько функций для увеличения размеров массива. Некоторые из них были созданы для удобства программистов, привыкших работать с различными типами очередей и стеков (FIFO, FILO и т. д.), что отражается в названиях функций (push, pop, shift и unshift). Но даже если вы не знаете, что такое «очередь» или «стек», не огорчайтесь - в этих функциях нет ничего сложного. Очередью (queue) называется структура данных, из которой элементы извлекаются в порядке поступления. Стеком (stack) называется структура данных, из которой элементы извлекаются в порядке, обратном порядку их поступления. array_push( ) Функция array_push( ) присоединяет (то есть дописывает в конец массива) один или несколько новых элементов. Синтаксис функции array_push( ): int array_push(array массив, mixed элемент [, ...]) Длина массива возрастает прямо пропорционально количеству его элементов. Это продемонстрировано в следующем примере: $languages = array("Spanish", "English", "French"); array_push($languages, "Russian", "German", "Gaelic"); // $languages = array("Spanish", "English", "French", // "Russian", "German", "Gaelic") У функции array_push( ), как и у многих стандартных функций РНР, существует «двойник» - функция аrrау_рор( ), предназначенная для извлечения элементов из массива. Главное различие между этими функциями заключается в том, что array_push( ) может добавлять несколько элементов одновременно, а аrrау_рор( ) удаляет элементы только по одному. аrrау_рор( ) Результат работы функции аrrау_рор( ) прямо противоположен array_push( ) - эта функция извлекает (то есть удаляет) последний элемент из массива. Извлеченный элемент возвращается функцией. Синтаксис функции аrrау_рор( ): аrrау_рор(аrrау массив) При каждом выполнении аrrау_рор( ) размер массива уменьшается на 1. Рассмотрим пример: $languages = array("Spanish", "English", "French", "Russian", "German", "Gaelic"); $a_language = array_pop($languages): // $a_language = "Gaelic" $a_language = array_pop($languages): // $a_language = "German" // $languages = array ("Spanish", "English", "French", "Russian"); Функции array_push( ), и array_pop( ) удобны тем, что с их помощью можно выполнять операции с элементами и управлять размером массива, не беспокоясь о неинициализированных или пустых элементах. Такое решение работает намного эффективнее, чем любые попытки управления этими факторами со стороны программиста. array_shift( ) Функция array_shift( ) аналогична аrrау_рор( ) с одним отличием: элемент удаляется из начала (левого края) массива. Все остальные элементы массива сдвигаются на одну позицию к началу массива. У функции array_shift( ) такой же синтаксис, как и у аггау_рор( ): array_shift(array массив) При работе с функцией array_shift( ) необходимо помнить, что элементы удаляются из начала массива, как показывает следующий пример: $languages = array("Spanish", "English", "French", "Russian"); $a_language = array_shift($languages); // $a_language = "Spanish"; // $languages = array("English", "French", "Russian"); array_unshift( ) Функция array_unshift( ) дополняет array_shift( ) - новый элемент вставляется в начало массива, а остальные элементы сдвигаются на одну позицию вправо. Синтаксис команды array_unshift( ): 1nt array_unshift(array массив, mixed переменная1 [....переменная2]) При одном вызове функции можно добавить как один, так и несколько элементов, при этом размер массива возрастает пропорционально количеству добавленных элементов. Пример добавления нескольких элементов: $languages = array("French", "Italian", "Spanish"); array_unshift($languages, "Russian", "Swahili", "Chinese"); // $languages = array("Russian", "Swahili", "Chinese", // "French", "Italian", "Spanish"); array_pad( ) Функция array_pad( ) позволяет быстро увеличить массив до желаемого размера посредством его дополнения стандартными элементами. Синтаксис функции array_pad( ): array arrap_pad(array массив, int размер, mixed значение): Параметр размер определяет новую длину массива. Параметр значение задает стандартное значение, присваиваемое элементам во всех новых позициях массива. При использовании array_pad( ) необходимо учитывать некоторые обстоятельства: Если размер положителен, массив дополняется справа, а если отрицателен - слева. Если абсолютное значение параметра размер меньше либо равно длине массива, никаких действий не выполняется. Абсолютным значением (модулем) целого числа называется его значение без знака. Например, абсолютное значение чисел 5 и -5 равно 5. Пример дополнения массива с конца: $weights = array(1, 3, 5, 10, 15, 25, 50); $weights = array_pad($weights. 10, 100); // Результат: $weights = array(1, 3, 5, 10, 15, 25, 50, 100, 100, 100); Пример дополнения массива с начала: $weights = array(1, 3, 5, 10, 15, 25, 50); $weights = array_pad($weights, -10, 100); // Результат: $weights = array(100, 100, 100, 1, 3, 5, 10, 15, 25, 50); Неправильная попытка дополнения массива: $weights = array(1, 3, 5, 10, 15, 25, 50); $weights = array_pad($weigtits, 3, 100); // Массив $weights не изменяется: // $weights = array(1, 3, 5, 10, 15, 25, 50); Перебор элементов В РНР существует несколько стандартных функций, предназначенных для перебора элементов массива. В совокупности эти функции обеспечивают гибкие и удобные средства для быстрой обработки и вывода содержимого массивов. Вероятно, вы будете часто использовать эти функции, поскольку они лежат в основе практически всех алгоритмов работы с массивами. reset( ) Функция reset( ) переводит внутренний указатель текущей позиции в массиве к первому элементу. Кроме того, она возвращает значение первого элемента. Синтаксис функции reset( ): mixed reset (array массив) Рассмотрим следующий массив: $fruits = array("apple", "orange", "banana"); Допустим, указатель текущей позиции в этом массиве установлен на элемент "orange". Команда: $a_fruit = reset($fruits); вернет указатель в начало массива, то есть на элемент "apple", и вернет это значение, если результат вызова reset( ) используется в программе. Возможен и упрощенный вариант вызова: reset($fruits); В этом случае указатель переходит к первому элементу массива, а возвращаемое значение не используется. each ( ) Функция each( ) при каждом вызове выполняет две операции: она возвращает пару «ключ/значение», на которую ссылается указатель текущей позиции, и перемещает указатель к следующему элементу. Синтаксис функции each( ): array each (array массив) Для удобства each ( ) возвращает ключ и значение в виде массива из четырех элементов; ключами этого массива являются 0, 1, value и key. Возвращаемый ключ ассоциируется с ключами 0 и key, а возвращаемое значение - с ключами 1 и value. В следующем примере функция each ( ) возвращает элемент, находящийся в текущей позиции: // Объявить массив из пяти элементов $spices = array("parsley", "sage", "rosemary", "thyme", "pepper"); // Установить указатель на первый элемент массива reset($spices); // Создать массив $a_sp1ce. состоящий из четырех элементов $a_spice = each($spices); В результате выполнения приведенного фрагмента массив $a_spice будет содержать следующие пары «ключ/значение»: 0 => 0; 1 => "parsley"; key => 0; value => "parsley". После этого строку "parsley" можно вывести любой из следующих команд: print $a_spice[1]: print $a_spice["value"]; Функция each() обычно используется в сочетании с list( ) в циклических конструкциях для перебора всех или некоторых элементов массива. При каждой итерации each( ) возвращает либо следующую пару «ключ/значение», либо логическую ложь при достижении последнего элемента массива. Вернемся к массиву $spices; чтобы вывести все элементы на экран, можно воспользоваться следующим сценарием: // Сбросить указатель текущей позиции reset($spices); // Перебрать пары "ключ/значение", ограничиваясь выводом значения while (list ($key, $val) = each ($spices) ) : print "$val
" endwhile; Ниже приведен более интересный пример использования each( ) в сочетании с другими функциями, описанными в этой главе. Листинг 5.1 показывает, как при помощи этих функций вывести отформатированную таблицу стран и языков. Листинг 5.1. Построение таблицы HTML по содержимому массива // Объявить ассоциативный массив стран и языков $languages = array ("Country" => "Language", "Spain" => "Spanish", "USA" => "English", "France" => "French", "Russia" => "Russian"); // Начать новую таблицу print ""; // Переместить указатель к позиции первого элемента reset ($languages); // Прочитать первый ключ и элемент $hdl = key ($languages); Shd2 = $languages[$hd1]; // Вывести первый ключ и элемент в виде заголовков таблицы print ""; next($languages); // Выводить строки таблицы с ключами и элементами массива while (list ($ctry,$lang) = each ($languages)) : print ""; endwhile; // Завершить таблицу print "
$hd1$hd2
Sctry$lang
"; ?> В результате выполнения этого кода будет построена следующая таблица HTML. Country Language Spain Spanish USA English France French Russia Russian Этот пример дает представление о самой сильной стороне РНР - возможности объединения динамического кода с HTML для получения наглядного, отформатированного представления прочитанных данных. end( ) Функция end( ) перемещает указатель к позиции последнего элемента массива. Синтаксис функции end( ): end (array массив) next( ) Функция next ( ) смещает указатель на одну позицию вперед, после чего возвращает элемент, находящийся в новой позиции. Если в результате смещения указатель выйдет за пределы массива, next ( ) возвращает ложное значение. Синтаксис функции next ( ): mixed next (array массив) Недостаток функции next ( ) заключается в том, что ложное значение возвращается и для существующих, но пустых элементов массива. Если вы хотите провести обычный перебор, воспользуйтесь функцией each( ). prev( ) Функция prev( ) аналогична next ( ) за одним исключением: указатель смещается на одну позицию к началу массива, после чего возвращается элемент, находящийся в новой позиции. Если в результате смещения указатель окажется перед первым элементом массива, prev( ) вернет ложное значение. Синтаксис функции prev( ): mixed prev (array массив) Недостаток функции prev( ) заключается в том, что ложное значение возвращается и для существующих, но пустых элементов массива. Если вы хотите провести обычный перебор, воспользуйтесь функцией each( ). array_walk( ) Функция array_walk( ) позволяет применить функцию к нескольким (а возможно, всем) элементам массива. Синтаксис функции array_walk( ): int array_walk(array массив, string имя_функции [, mixed данные]) Функция, заданная параметром имя_функции, может использоваться для различных целей - например, для поиска элементов с определенными характеристиками или модификации содержимого массива. В ассоциативных массивах функция имя_функции должна получать минимум два параметра - элемент массива и ключ. Если указан необязательный третий параметр данные, он становится третьим параметром. В следующем примере функция array_walk( ) используется для удаления дубликатов из массива: function delete_dupes($element) { static $last=""; if ($element == $last) unset($element); else . '' $last=$element: } $emails = array("blah@blah.com", "chef@wjgilmore.com", "blah@blah.com"); sort($emails); reset($emails); array_walk($emails,"delete_dupes"); // $emails = array("chef@wjgilmore.com", "blah@blah.com"); array_reverse( ) Функция array_reverse( ) позволяет легко перейти к противоположному порядку элементов, составляющих массив. Синтаксис функции array_reverse( ): array array_reverse(array массив) Рассмотрим пример использования функции array_reverse( ): $us_wireproducers = array ("California", "Oregon", "New York". "Washington"); $us_wine_producers - array_reverse (Sus_wine_producers); // $us_wine_producers = array ("Washington". "New York", "Oregon". "California"); При вызове array_reverse( ) для ассоциативного массива пары «ключ/значение» сохраняются, изменяется только порядок элементов массива. array_flip( ) Функция array_flip( ) меняет местами ключи и значения элементов массива. Синтаксис функции array_flip( ): array array_flip(array массив) В следующем примере функция array_flip( ) меняет местами все ключи и значения элементов: $languages = array("Spain" => "Spanish", "France" => "French", "Italy" => "Italian"); $languages = array_flip($languages); // $languages = array("Spanish" => "Spain", // "French" => "France", // "Italian" => "Italy"); Помните: функция array_flip( ) не изменяет порядок элементов массива. Для этой цели используется функция array_reverse( ). Размер массива Наличие информации о текущем размере массива часто позволяет повысить эффективность сценария. Вероятно, размер массива чаще всего используется при циклическом переборе элементов: $us_wine_producers = array ("Washington". "New York", "Oregon", "California"); for (Si = 0; Si < sizeof ($us_wine_producers); $i++) : print "$us_wine_producers[$i]"; endfor; Поскольку массив $us_wine_producers индексируется целыми числами, мы можем воспользоваться циклом for для циклического увеличения переменной-счетчика ($i) и вывода каждого элемента в массиве. sizeof( ) Функция sizeof ( ) возвращает количество элементов в массиве. Синтаксис функции sizeof ( ): int sizeof (array массив) Вероятно, функция sizeof ( ) будет часто встречаться в ваших web-приложениях. Ниже приведен краткий пример ее использования (кстати, предыдущий пример тоже относится к числу стандартных применений sizeof ( )): $pasta = array("bowties", "angelhair", "rigatoni"); $pasta_size = sizeof($pasta); // $pasta_size = 3 У функции sizeof ( ) существует и другая, расширенная форма - count ( ) (см. ниже). count( ) Функция count( ) выполняет ту же операцию, что и sizeof ( ), - она возвращает количество значений, содержащихся в массиве. Синтаксис функции count ( ): int count (mixed переменная) Единственное различие между sizeof ( ) и count( ) заключается в том, что в некоторых ситуациях count ( ) возвращает дополнительную информацию: если переменная существует и является массивом, count ( ) возвращает количество элементов в массиве; если переменная существует, но не является массивом, функция возвращает значение 1; если переменная не существует, возвращается значение 0. array_count_values( ) Функция array_count_values( ) является разновидностью sizeof ( ) и count ( ). Вместо общего количества элементов она подсчитывает количество экземпляров каждого значения в массиве. Синтаксис функции array_count_values( ): array array_count_values(array массив): В возвращаемом массиве ключами будут значения исходного массива, а значениями - их частоты: $states = аrrау("ОН", "ОК", "СА", "РА", "ОН", "ОН", "РА", "АК"); $state_freq = array_count_values($states); Массив $state_freq заполняется следующими ассоциативными парами «ключ/значение»: $state_freq = аrrау("ОН" => 3, "ОК" => 1, "СА" => 1, "РА" => 2, "АК" => 1); Сортировка массивов Сортировка занимает важное место в программировании и часто встречается на практике в таких Интернет-приложениях, как коммерческие сайты (сортировка категорий товаров в алфавитном порядке, сортировка цен) или поисковые системы (сортировка программ по количеству загрузок). В РНР существует девять стандартных функций сортировки (табл. 5.1), причем каждая функция сортирует массив особым образом. Таблица 5.1. Функции сортировки Функция Сортировка Обратный порядок Сохранение пар «ключ/значение» sort Значение Нет Нет rsort Значение Да Нет asort Значение Нет Да arsort Значение Да Да ksort Ключ Нет Да krsort Ключ Да Да usort Значение ? Нет uasort Значение ? Да uksort Ключ ? Да ? относится к применению пользовательских функций сортировки, когда порядок сортировки массива зависит от результатов, возвращаемых пользовательской функцией. Сортировка элементов массива не ограничивается стандартными критериями, поскольку три функции (usort(), uasort( ) и uksort( )) позволяют задать пользовательский критерий и отсортировать информацию произвольным образом. sort( ) Простейшая функция sort( ) сортирует элементы массива по возрастанию (от меньших к большим). Синтаксис функции sort ( ): void sort (array массив) Нечисловые элементы сортируются в алфавитном порядке в соответствии с ASCII-кодами. Следующий пример демонстрирует применение функции sort( ) при сортировке: // Создать массив городов. $cities = array("Aprilia", "Nettuno", "Roma", "Venezia", "Anzio"); // Отсортировать города по возрастанию sort($cities); // Перебрать содержимое массива и вывести все пары "ключ/значение". for (reset($cities); $key = key ($cities); next ($cities)): print("cities[$key] = $cities[$key]
"; endfor; Этот фрагмент выводит следующий результат: cities[0] = Anzio cities[1] = Aprilia cities[2] = Nettuno cities[3] = Roma cities[4] = Venezia Как видите, массив $cities сортируется в алфавитном порядке. Одна из разновидностей этого способа сортировки реализована в функции asort( ), описанной ниже. rsort( ) Функция rsort ( ) работает точно так же, как функция sort ( ), за одним исключением: элементы массива сортируются в обратном порядке. Синтаксис функции rsort ( ): void rsort (array массив) Вернемся к массиву $cities из предыдущего примера: $cities array("Aprilia", "Nettuno", "Roma", "Venezia", "Anzio"); rsort($cities); В результате сортировки массива $cities функцией rsort( ) элементы будут расположены в следующем порядке: cities[0] = Venezia cities[1] = Roma cities[2] = Nettuno cities[3] = Aprilia cities[4] = Anzio Массив $cities также сортируется, но на этот раз в порядке, обратном алфавитному. Одна из разновидностей этого способа сортировки реализована в функции arsort( ), описанной ниже. asort( ) Функция asort( ) работает почти так же, как упоминавшаяся выше функция sort( ), однако она сохраняет исходную ассоциацию индексов с элементами независимо от нового порядка элементов. Синтаксис функции asort( ): void asort(array массив) Вернемся к массиву $cities: $cities = array("Aprilia", "Nettuno", "Roma", "Venezia", "Anzio"); asort($cities): В результате сортировки массива $cities функцией rsort() элементы будут расположены в следующем порядке: cities[4] = Anzio cities[0] = Aprilia cities[1] = Nettuno cities[2] = Roma cities[3] = Venezia Обратите внимание на индексы и сравните их с приведенными в описании функции sort ( ). Именно в этом и состоит различие между двумя функциями. arsort( ) Функция arsort ( ) представляет собой разновидность asort( ), которая сохраняет исходную ассоциацию индексов, но сортирует элементы в обратном порядке. Синтаксис функции arsort( ): void arsort (array массив) Воспользуемся функцией arsort( ) для сортировки массива $cities: $cities = array("Aprilia", "Nettuno", "Roma", "Venezia", "Anzio"); arsort($cities); В результате сортировки элементы будут расположены в следующем порядке: cities[3] = Venezia cities[2] = Roma cities[l] = Nettuno cities[0] = Aprilia cities[4] = Anzio Обратите внимание на индексы и сравните их с приведенными в описании функции rsort( ). Именно в этом и состоит различие между двумя функциями. ksort( ) Функция ksort( ) сортирует массив по ключам, сохраняя исходные ассоциации ключей со значениями. Синтаксис функции ksort( ): void ksort (array массив) Для примера рассмотрим массив, слегка отличающийся от исходного массива $cities: $wine_producers = array ("America" => "Napa Valley", "Italy" => "Tuscany", "Australia" => "Ruthgerlen", "France" => "Loire", "Chile" => "Rapel Valley"); В результате сортировки массива функцией ksort( ) элементы будут расположены в следующем порядке: "America" => "Napa Valley" "Australia" => "Ruthgerlen" "Chile" => "Rapel Valley" "France" => "Loire" "Italy" => "Tuscany" Сравните с результатами сортировки $wine_producers функцией sort ( ): "America" => "Napa Valley" "Australia" => "Tuscany" "Chile" => "Ruthgerlen" "France" => "Loire" "Italy" => "Rapel Valley" Более чем сомнительный результат! krsort( ) Функция krsort( ) почти аналогична ksort( ), однако ключи сортируются в обратном порядке. Синтаксис функции krsort( ): void krsort (array $массив) Рассмотрим сортировку массива $wi reproducers функцией krsort( ): $wine_producers = array ("America" => "Napa Valley", "Italy" => "Tuscany", "Australia" => "Ruthgerlen", "France" => "Loire". "Chile" => "Rapel Valley"); krsort($wine_producers); В результате сортировки элементы будут расположены в следующем порядке: "Italy" => "Tuscany" "France" => "Loire" "Chile" => "Rapel Valley" "Australia" => "Ruthgerlen" "America" => "Napa Valley" Вероятно, описанных выше функций сортировки будет вполне достаточно для большинства случаев. Тем не менее, в некоторых ситуациях может возникнуть необходимость в определении собственных критериев сортировки. В РНР такая возможность реализована в трех стандартных функциях: usort( ), uasort( ) и uksort( ). usort( ) Функция usort( ) дает возможность отсортировать массив на основании критерия, определяемого программистом. Для этого usort( ) в качестве параметра передается имя функции, определяющей порядок сортировки. Синтаксис функции usort( ): void usort (array массив, string имя_функции) В параметре массив передается имя сортируемого массива, а в параметре имя_функции - имя функции, на основании которой будет осуществляться сортировка. Допустим, у вас имеется длинный список греческих имен, которые необходимо выучить к предстоящему экзамену по истории. Вы хотите отсортировать слова по длине, чтобы начать с самых длинных, а затем учить короткие, когда вы уже устанете. Для сортировки массива по длине можно воспользоваться функцией usort( ). Листинг 5.2. Определение критерия сортировки для функции usort( ) $vocab = аrrау( "Socrates", "Aristophanes", "Plato", "Aeschylus", "Thesmophoriazusae"); function compare_length($str1, $str2) { // Получить длину двух следующих слов $length1 = strlen($str1); $length2 = strlen($str2); // Определить, какая строка имеет меньшую длину if ($length1 == $length2) : return 0; elseif ($length1 < $length2) : return -1; else : return 1; endif; } // Вызвать usort() с указанием функции compare_length() // в качестве критерия сортировки usort ($vocab, "compare_length") : // Вывести отсортированный список while (list ($key, $val) = each ($vocab)) { echo "$val
"; } В листинге 5.2 функция compare_length ( ) определяет критерий сортировки массива. В данном случае это делается посредством сравнения длины передаваемых элементов. Функция-критерий должна получать два параметра, представляющих сравниваемые элементы массива. Также обратите внимание на то, как эти элементы неявно передаются функции-критерию при вызове usort( ), и на то, что все элементы автоматически сравниваются этой функцией. Функции uasort( ) и uksort( ) представляют собой разновидности usort( ) с тем же синтаксисом. Функция uasort() сортирует массив по пользовательскому критерию с сохранением ассоциаций «ключ/значение». Функция uksort( ) также сортирует массив по пользовательскому критерию, однако сортируются не значения, а ключи. Другие полезные функции В этом разделе описаны некоторые функции, которые не принадлежат ни к какому конкретному разделу, но приносят несомненную пользу. array_merge( ) Функция arrayjnerge( ) сливает от 1 до N массивов, объединяя их в соответствии с порядком перечисления в параметрах. Синтаксис функции array_merge( ): array array_merge(array массив1, array массив2, ..., array массивN] Рассмотрим пример простого объединения массивов функцией arrayjnerge( ); $arr_1 = array("strawberry", "grape", "lemon"); $arr_2 = array("banana", "cocoa", "lime"); $arr_3 = array("peach", "orange"); $arr_4 = array_merge ($arr2, $arr_1, $arr_3): // $arr_4 = array("banana", "cocoa", "lime", "strawberry", "grape", "lemon", "peach", "orange"); array_slice( ) Функция array_slice( ) возвращает часть массива, начальная и конечная позиция которой определяется смещением от начала и необязательным параметром длины. Синтаксис функции array_slice( ): array array_slice(array массив, int смещение [, int длина]) Значения параметров задаются по определенным правилам: Если смещение положительно, начальная позиция возвращаемого фрагмента отсчитывается от начала массива. Если смещение отрицательно, начальная позиция возвращаемого фрагмента отсчитывается от конца массива. Если длина не указана, в возвращаемый массив включаются все элементы от начальной позиции до конца массива. Если указана положительная длина, возвращаемый фрагмент состоит из заданного количества элементов. Если указана отрицательная длина, возвращаемый фрагмент заканчивается в заданном количестве элементов от конца массива. array_splice( ) Функция array_spl ice( ) отдаленно напоминает array_slice( ) - она заменяет часть массива, определяемую начальной позицией и необязательной длиной, элементами необязательного параметра-массива. Синтаксис функции array_splice( ): array_splice(array входной_массив, int смещение, [int длина], [array заменяющий_массив]); Значения параметров задаются по определенным правилам: Если смещение положительно, начальная позиция первого удаляемого элемента отсчитывается от начала массива. Если смещение отрицательно, начальная позиция первого удаляемого элемента отсчитывается от конца массива. Если длина не указана, удаляются все элементы от начальной позиции до конца массива. Если указана положительная длина, удаляемый фрагмент состоит из заданного количества элементов. Если указана отрицательная длина, из массива удаляются элементы от начальной позиции до позиции, находящейся на заданном расстоянии от конца массива. Если заменяющий_массив не указан, то элементы, заданные смещением и необязательной длиной, удаляются из массива. Если заменяющий_массив указан, он должен быть заключен в конструкцию аггау() (если он содержит более одного элемента). Рассмотрим несколько примеров, наглядно демонстрирующих возможности этой функции. В этих примерах будет использоваться массив $pasta (см. выше), с которым будут выполняться различные операции. Удаление всех элементов с пятой позиции до конца массива: $pasta = array_splice($pasta, 5); Удаление пятого и шестого элементов: $pasta = array_splice($pasta. 5, 2); Замена пятого и шестого элементов новыми значениями: $pasta = array_splice($pasta, 5, 2, array("element1", "element2")); Удаление всех элементов, начиная с пятого, до третьего элемента с конца массива: $pasta = array_splice($pasta, 5, -3); Как видно из приведенных примеров, функция array_splice( ) обеспечивает гибкие возможности удаления элементов из массива при минимальном объеме кода. shuffle( ) Функция shuffle( ) сортирует элементы массива в случайном порядке. Синтаксис функции shuffle( ): void shuffle(array массив); Итоги В этой главе рассматривались массивы и стандартные функции РНР для работы с массивами. В частности, рассматривались следующие темы: создание индексируемых и ассоциативных массивов; многомерные массивы; отображение содержимого многомерных массивов; поиск элементов; добавление и удаление элементов; размер массива; сортировка; другие полезные функции для работы с массивами. Массивы являются очень удобными и универсальными средствами для работы с данными в web-приложениях. В примерах дальнейших глав массивы будут неоднократно использоваться для повышения эффективности и наглядности кода. В главе 6 мы продолжим знакомиться с базовыми средствами РНР. На этот раз речь пойдет об объектно-ориентированных возможностях языка. ГЛАВА 6 Объектно-ориентированные возможности РНР Если вы ориентируетесь в современных технологиях программирования, объектно-ориентированное программирование (ООП) наверняка является частью вашей повседневной работы. Если же вы принадлежите к числу новичков в области ООП, после чтения этой главы и рассмотрения нескольких примеров программирование предстанет перед вами совсем в новом свете. Эта глава посвящена технологии ООП и ее реализации в РНР. В ней описан весь необходимый синтаксис и приводятся примеры, которые позволят вам заняться созданием объектно-ориентированных приложений. Стратегию ООП лучше всего описать как смещение приоритетов в процессе программирования от функциональности приложения к структурам данных. Это позволяет программисту моделировать в создаваемых приложениях реальные объекты и ситуации. Технология ООП обладает тремя главными преимуществами: она проста для понимания - ООП позволяет мыслить категориями повседневных объектов; повышенно надежна и проста для сопровождения - правильное проектирование обеспечивает простоту расширения и модификации объектно-ориентированных программ. Модульная структура позволяет вносить независимые изменения в разные части программы, сводя к минимуму риск ошибок программирования; ускоряет цикл разработки - модульность и здесь играет важную роль, поскольку различные компоненты ОО-программ можно легко использовать в других программах, что уменьшает избыточность,кода и снижает риск внесения ошибок при копировании. Специфика ООП заметно повышает эффективность труда программистов и позволяет им создавать более мощные, масштабируемые и эффективные приложения. Многие преимущества ООП обусловлены одним из его фундаментальных принципов - инкапсуляцией. Инкапсуляцией называется включение различных мелких элементов в более крупный объект, в результате чего программист работает непосредственно с этим объектом. Это приводит к упрощению программы, поскольку из нее исключаются второстепенные детали. Инкапсуляцию можно сравнить с работой автомобиля с точки зрения типичного водителя. Многие водители не разбираются в подробностях внутреннего устройства машины, но при этом управляют ею именно так, как было задумано. Пусть они не знают, как устроен двигатель, тормоз или рулевое управление, - существует специальный интерфейс, который автоматизирует и упрощает эти сложные операции. Сказанное также относится к инкапсуляции и ООП - многие подробности «внутреннего устройства» скрываются от пользователя, что позволяет ему сосредоточиться на решении конкретных задач. В ООП эта возможность обеспечивается классами, объектами и различными средствами выражения иерархических связей между ними (классы и объекты рассматриваются ниже). РНР и ООП Хотя РНР обладает общими объектно-ориентированными возможностями, он не является полноценным ОО-языком (например, таким, как C++ или Java). В частности, в РНР не поддерживаются следующие объектно-ориентированные возможности: множественное наследование; автоматический вызов конструкторов (если вы хотите, чтобы при конструировании объекта производного класса вызывался конструктор базового класса, вам придется вызвать его явно); абстрактные классы; перегрузка методов; перегрузка операторов (это связано с тем, что РНР является языком со свободной типизацией, - за дополнительной информацией обращайтесь к главе 2); закрытый и открытый доступ, виртуальные функции; деструкторы; полиморфизм. Но и без всего перечисленного вы все равно сможете извлечь пользу из объектно-ориентированных возможностей, поддерживаемых РНР. Реализация ООП в РНР оказывает колоссальную помощь в модульном оформлении функциональности вашей программы. Классы, объекты и объявления методов Классы образуют синтаксическую базу объектно-ориентированного программирования. Их можно рассматривать как своего рода «контейнеры» для логически связанных данных и функций (обычно называемых методами - см. ниже). Класс представляет собой шаблон, по которому создаются конкретные экземпляры, используемые в программе. Экземпляры классов называются объектами. Чтобы лучше понять связь между классами и объектами, можно представить класс как «чертеж» для создания объектов. По чертежу «изготавливаются» разные объекты, обладающие одними и теми же базовыми характеристиками (например, при строительстве дома - одна дверь, два окна и определенная толщина стены). Тем не менее, каждый объект существует независимо от других - изменение его характеристик никак не влияет на характеристики других объектов; например, в уже построенном доме можно прорубить дополнительное окно. Важно помнить, что у объектов все равно остается общая характеристика - количество окон. Класс также можно рассматривать как тип данных (см. главу 2), а объект - как переменную (по аналогии с тем, как переменная $counter относится к целому, а переменная $last_name - к строковому типу). Программа может одновременно работать с несколькими объектами одного класса как с несколькими переменными целого типа. Общий формат классов РНР приведен в листинге 6.1. Листинг 6.1. Объявление классов в РНР class Class_name { var $attribute_1; ... var $attribute_N; function function1() { ... } ... function functionN() { ... } // end Class_name Подведем итоги: объявление класса должно начинаться с ключевого слова class (подобно тому, как объявление функции начинается с ключевого слова function). Каждому объявлению атрибута, содержащегося в классе, должно предшествовать ключевое слово van. Атрибуты могут относиться к любому типу данных, поддерживаемых в РНР; их можно рассматривать как переменные с небольшими различиями, о которых вы узнаете в этой главе. После объявлений атрибутов следуют объявления методов, очень похожие на типичные объявления функций. По общепринятым правилам имена классов ООП начинаются с прописной буквы, а все слова в именах методов, кроме первого, начинаются с прописных букв (первое слово начинается со строчной буквы). Разумеется, вы можете использовать любые обозначения, которые сочтете удобными; главное - выберите стандарт и придерживайтесь его. Методы часто используются для работы с атрибутами классов. При ссылках на атрибуты внутри методов используется специальная переменная $this. Синтаксис методов продемонстрирован в следующем примере: bgcolor = $color; } function getBgColor() { return $this->bgcolor; } } ?> Переменная $this ссылается на экземпляр объекта, для которого вызывается метод. Поскольку в любом классе может существовать несколько экземпляров объектов, уточнение $this необходимо для ссылок на атрибуты, принадлежащие текущему объекту. При использовании этого синтаксиса обратите внимание на два обстоятельства: атрибут, на который вы ссылаетесь в методе, не нужно передавать в виде параметра функции; знак доллара ($) ставится перед переменной $this, но не перед именем атрибута (как у обычной переменной). Создание объектов и работа с ними Объекты создаются оператором new. Например, объект класса Webpage создается следующей командой: $home_page = new Webpage; Новый объект с именем $some_page обладает собственным набором атрибутов и методов, перечисленных в классе Webpage. Для изменения значения атрибута $bgcolor, принадлежащего этому конкретному объекту, можно воспользоваться определенным в классе методом setBgColor( ): $some_page->setBgColor("black"); Следует помнить, что РНР также позволяет явно получить значение атрибута указанием имен объекта и атрибута: $some_page->bgcolor; Однако второй способ противоречит принципу инкапсуляции, и при работе с ООП поступать так не следует. Чтобы понять, почему это так, прочитайте следующий раздел. Нарушение инкапсуляции Допустим, вы создали класс, один из атрибутов которого представляет собой массив. Но вместо того чтобы работать с массивом через промежуточные методы (например, предназначенные для создания, удаления, модификации элементов и т. д.), вы в случае необходимости напрямую обращаетесь к массиву. В течение месяца вы уверенно программируете большое «объектно-ориентированное» приложение и благосклонно принимаете хвалу коллег-программистов. Будущее сулит много радостей - премии, оплачиваемый отпуск и даже отдельный кабинет. Но вот через месяц после успешного запуска вашего web-приложения ваш начальник вдруг решает, что массивы в данном случае не годятся и работать с информацией нужно только через базу данных. Какая неприятность! Поскольку вы решили работать с атрибутами напрямую, вам теперь придется просматривать всю программу и везде, где происходят обращения к данным, вносить исправления в соответствии с новым интерфейсом. Задача весьма хлопотная, к тому же чревата риском внесения новых ошибок. А теперь давайте посмотрим, что произошло бы при работе с данными с использованием методов. Все, что вам пришлось бы сделать при переходе от массива к базе данных - перепрограммировать методы. Модификация автоматически распространяется на все точки программы, в которых присутствуют вызовы методов. Конструкторы Довольно часто при создании объекта требуется задать значения некоторых атрибутов. К счастью, разработчики технологии ООП учли это обстоятельство и реализовали его в концепции конструкторов. Конструктор представляет собой метод, который задает значения некоторых атрибутов (а также может вызывать другие методы). Конструкторы вызываются автоматически при создании новых объектов. Чтобы это стало возможным, имя метода-конструктора должно совпадать с именем класса, в котором он содержится. Пример конструктора приведен в листинге 6.2. Листинг 6.2. Использование конструктора bgcolor = $color; } } // Вызвать конструктор класса Webpage $page = new Webpage("brown"); ?> Раньше создание объекта и инициализация атрибутов выполнялись раздельно. Конструкторы позволяют выполнить эти действия за один этап. Интересная подробность: в зависимости от количества передаваемых параметров могут вызываться разные конструкторы. Например, в листинге 6.2 объекты класса Webpage могут создаваться двумя способами. Во-первых, вы можете вызвать конструктор, который просто создает объект, но не инициализирует его атрибуты: $page = new Webpage; Во-вторых, объект можно создать при помощи конструктора, определенного в классе, - в этом случае вы создаете объект класса Webpage и присваиваете значение его атрибуту bgcolor: $page = new Webpage("brown"); Деструкторы Как упоминалось ранее, в РНР отсутствует непосредственная поддержка деструкторов. Тем не менее, вы можете легко имитировать работу деструктора, вызывая функцию РНР unset( ). Эта функция уничтожает содержимое переменной и возвращает занимаемые ею ресурсы системе. С объектами unset( ) работает так же, как и с переменными. Допустим, вы работаете с объектом $Webpage. После завершения работы с этим конкретным объектом вызывается функция unset($Webpage); Эта команда удаляет из памяти все содержимое $Webpage. Действуя в духе инкапсуляции, можно поместить вызов unset( ) в метод с именем destroy( ) и затем вызвать его: $Website->destroy( ); Помните: необходимость в вызове деструкторов возникает лишь при работе с объектами, использующими большой объем ресурсов, поскольку все переменные и объекты автоматически уничтожаются по завершении сценария. Простое и иерархическое наследование Как говорилось выше, класс является шаблоном, по которому создаются реальные объекты с определенными характеристиками и функциями. Нетрудно представить себе ситуацию, при которой такой объект является частью другого объекта. Например, автомобиль можно считать частным случаем категории «транспортное средство», к которой относятся и самолеты. Хотя разные типы транспортных средств сильно отличаются друг от друга, все они характеризуются атрибутами из общего набора (количество колес, мощность, максимальная скорость, модель и т. д.). Пусть конкретные значения этих атрибутов сильно различаются - атрибуты все равно присущи всем транспортным средствам. Таким образом, субклассы «автомобиль» и «самолет» наследуют общий набор базовых характеристик от суперкласса «транспортное средство». Концепция получения классом характеристик от другого, более общего класса называется наследованием. Наследование является исключительно полезным средством программирования, поскольку его применение предотвращает копирование кода, совместно используемого структурами данных, - например, общих характеристик различных типов транспортных средств, упоминавшихся в предыдущем абзаце. В общем случае синтаксис наследования характеристик другого класса в РНР выглядит так: class Class_name2 extends Class_name1 { объявления атрибутов; объявления методов; } Ключевое слово extends говорит о том, что класс Class_name2 наследует все характеристики класса Class_name1. Помимо возможности многократного использования кода, наследование обладает еще одним важным преимуществом - снижается вероятность ошибок при модификации программы. Например, в иерархии, изображенной на рис. 6.1, изменения в классе «автомобиль» никак не отразятся на коде (и данных) класса «самолет», и наоборот. Вызов конструктора производного класса не приводит к автоматическому вызову конструктора базового класса. Рис. 6.1. Иерархия транспортных средств В листинге 6.3 приведены классы, моделирующие иерархию, изображенную на рис. 6.1. Листинг 6.3. Представление различных типов транспортных средств при помощи наследования current_speed = $mph; } function getSpeed() { return $this->current_speed; } } // Автомобиль class Auto extends Vehicle { var $fue1_type; function setFuelType($fuel) { $this->fuel_type = $fuel; } function getFuelType() { return $this->fuel_type; } } // Самолет class Airplane extends Vehicle { var $wingspan; function setWingSpan($wingspan) { $this->wingspan = $wingspan; } function getWingSpan() { return $this->wingspan; } } ?> Объекты этих классов создаются следующим образом: $tractor = new Vehicle; $gulfstream = new Airplane; Приведенные команды создают два объекта. Первый объект, $tractor, относится к классу Vehicle. Второй объект, $gulfstream, относится к классу Airplane и потому обладает как общими характеристиками класса Vehicle, так и уточненными характеристиками класса Airplаne. Ситуация, при которой класс наследует свойства нескольких родительских классов, называется множественным наследованием. К сожалению, в РНР множественное наследование не поддерживается. Например, следующая конструкция невозможна в РНР: class Airplane extends Vehicle extends Building... Многоуровневое наследование С увеличением размеров и сложности программ может возникнуть необходимость в многоуровневом наследовании. Иначе говоря, класс будет наследовать свои свойства от других классов, которые, в свою очередь, будут наследовать от третьих классов и т. д. Многоуровневое наследование развивает модульную структуру программы, обеспечивая простоту сопровождения и более четкую логическую структуру. Скажем, при использовании примера с транспортными средствами в большой программе может появиться необходимость в дополнительном разбиении на субклассы суперкласса Vehicle, продолжающем логическое развитие иерархии. Например, транспортные средства можно дополнительно разделить на наземные, морские и воздушные, чтобы суперкласс специализированных субклассов выбирался в зависимости от среды, в которой перемещается данное транспортное средство. Новый вариант иерархии показан на рис. 6.2. Краткий пример, приведенный в листинге 6.4, подчеркивает некоторые важные аспекты многоуровневого наследования в РНР. Листинг 6.4. Многоуровневое наследование Объект $nissan содержит все атрибуты и методы классов Саr, Land и Vehicle. Как видите, программа получается исключительно модульной. Допустим, когда-то в будущем вы захотите добавить в класс Land новый атрибут. Нет проблем: внесите соответствующие изменения в класс Land, и этот атрибут немедленно становится доступным для классов Land и Саr, не влияя на функциональность других классов. Таким образом, модульность кода и гибкость относятся к числу основных преимуществ ООП. Рис. 6.2. Многоуровневое наследование в иерархии Vehicle Хотя масс наследует свои характеристики от цепочки родителей, конструкторы родительских классов не вызываются автоматически при создании объектов класса-наследника. Эти конструкторы могут вызываться классом-наследником в виде методов. Абстрактные классы В некоторых ситуациях бывает удобно создать класс, объекты которого никогда не создаются (данный класс нужен всего лишь как базовый для создания производных классов). Такие классы называются абстрактными. Абстрактные классы обычно применяются в тех случаях, когда разработчик программы хочет обеспечить обязательную поддержку некоторых функциональных возможностей всеми классами, производными от абстрактного базового класса. В РНР отсутствует прямая поддержка абстрактных классов, однако существует простое обходное решение - достаточно определить в «абстрактном» классе конструктор и включить в него вызов die( ). Вернемся к классам из листинга 6.4. Скорее всего, вам никогда не придется создавать экземпляры классов Land и Vehicle, поскольку они не могут представлять физические объекты. Для представления реальных объектов (например, автомобилей) следует создать класс, производный от этих классов. Следовательно, чтобы предотвратить возможное создание объектов классов Land и Vehicle, необходимо включить в их конструкторы вызовы die( ), как показано в листинге 6.5. Листинг 6.5. Создание абстрактных классов Попытка создания экземпляра этих абстрактных классов приведет к выдаче сообщения об ошибке и завершению программы. Перегрузка методов Перегрузкой методов называется определение нескольких методов с одинаковыми именами, но разным количеством или типом параметров. Как и в случае с абстрактными классами, в РНР эта возможность не поддерживается, но существует простое обходное решение, приведенное в листинге 6.6. Листинг 6.6. Перегрузка методов $name(); else : $this->$name(func_get_arg(0)); endif; } function Page0() { $this->bgcolor = "white"; $this->textcolor = "black"; print "Created default page"; } function Page1($bgcolor) { $this->bgcolor = $bgcolor; $this->textcolor = "black"; print "Created custom page"; } } $html_page - new Page("red"); ?> В этом примере при создании нового объекта с именем $html_page передается один аргумент. Поскольку в классе был определен конструктор по умолчанию (Раgе( )), вызывается именно он. Однако конструктор по умолчанию всего лишь выбирает, какому из конструкторов (Page0( ) или Page1( )) следует передать управление. При выборе конструктора используются функции func_num_args( ) и func_get_arg( ), которые, соответственно, определяют количество аргументов и читают эти аргументы. Конечно, такое решение вряд ли можно назвать полноценной перегрузкой, но оно подойдет для тех, кто не может жить без этого важного аспекта ООП. Функции для работы с классами и объектами В РНР существует несколько стандартных функций для работы с классами и объектами; эти функции рассматриваются в следующих разделах. Все они часто используются на практике, особенно в процессе разработки интерфейса, администрирования кода и диагностики ошибок. get_class_methods( ) Функция get_class_methods( ) возвращает массив имен методов класса с заданным именем. Синтаксис функции get_class_methods( ): array get_class_methods (string имя_класса) Простой пример использования get_class_methods( ) приведен в листинге 6.7. Листинг 6.7. Получение списка методов класса wingspan = $wingspan; } function getWingSpan() { return $this->wingspan; } } $cls_methods = get_class_methods(Airplane); // Массив $cls_methods содержит имена всех методов, // объявленных в классах "Airplane" и "Vehicle" ?> Как видно из листинга 6.7, функция get_class_methods( ) позволяет легко получить информацию обо всех методах, поддерживаемых классом. get_class_vars( ) Функция get_class_vars( ) возвращает массив имен атрибутов класса с заданным именем. Синтаксис функции get_class_vars( ): array get_class_vars (string имя_класса) Пример использования get_class_vars( ) приведен в листинге 6.8. Листинг 6.8. Получение списка атрибутов класса функцией get_class_vars( ) Массив $attribs заполняется именами всех атрибутов класса Airplane. get_object_vars( ) Функция get_object_vars( ) возвращает ассоциативный массив с информацией обо всех атрибутах объекта с заданным именем. Синтаксис функции get_object_vars( ): array get_object_vars (object имя_обьекта) Пример использования функции get_object_vars( ) приведен в листинге 6.9. Листинг 6.9. Получение информации о переменных объекта doors = $doors; $this->engine = $eng; $this->wheels = $wheels; } function get_wheels() { return $this->wheels; } } $toyota = new car(2,400,4); $vars = get_object_vars($toyota); while (list($key, $value) = each($vars)) : print "$key ==> $value
"; endwhile; // Выходные данные: // doors ==> 2 // engine ==> 400 // wheels ==> 2 ?> Функция get_object_vars( ) позволяет быстро получить всю информацию об атрибутах конкретного объекта и их значениях в виде ассоциативного массива. method_exists( ) Функция method_exists( ) проверяет, поддерживается ли объектом метод с заданным именем. Если метод поддерживается, функция возвращает TRUE, в противном случае возвращается FALSE. Синтаксис функции method_exists( ): bool method_exi sts (object имя_обьекта. string имя_метода) Пример использования метода method_exists( ) приведён в листинге 6.10. Листинг 6.10. Проверка поддержки метода объектом при помощи функции method_exists() fourWeel = 1; } } // Создать объект с именем $саr $car = new Land; // Если метод "fourWheelDrive" поддерживается классом "Land" // или "Vehicle", вызов method_exists возвращает TRUE; // в противном случае возвращается FALSE. // В данном примере method_exists() возвращает TRUE. if (method_exists($car, "setfourWheelDrive")) : print "This car is equipped with 4-wheel drive"; else : print "This car is not equipped with 4-wheel drive"; endif; ?> В листинге 6.10 функция method_exists ( ) проверяет, поддерживается ли объектом $car метод с именем setFourWheelDrive( ). Если метод поддерживается, функция возвращает логическую истину и фрагмент выводит соответствующее сообщение. В противном случае возвращается FALSE и выводится другое сообщение. get_class( ) Функция get_class( ) возвращает имя класса, к которому относится объект с заданным именем. Синтаксис функции get_class( ): string get_class(object имя_объекта); Пример использования get_class( ) приведен в листинге 6.11. Листинг 6.11. Получение имени класса функцией get_class( ) В результате переменной $class_a присваивается имя класса, на основе которого был создан объект $саr. get_parent_class( ) Функция get_parent_class( ) возвращает имя родительского класса (если он есть) для объекта с заданным именем. Синтаксис функции get_parent_dass( ): string get_parent_class (object имя_обьекта); Листинг 6.12 демонстрирует использование get_parent_class( ). Листинг 6.12. Получение имени родительского класса функцией get_parent_class( ) Как и следовало ожидать, при вызове get_parent_class( ) переменной $parent будет присвоена строка "Vehicle". is_subclass_of( ) Функция is_subclass_of( ) проверяет, был ли объект создан на базе класса, имеющего родительский класс с заданным именем. Функция возвращает TRUE, если проверка дает положительный результат, и FALSE в противном случае. Синтаксис функции is_subclass_of( ): bool is_subclass_of (object объект, string имя_класса) Использование is_subclass_of( ) продемонстрировано в листинге 6.13. Листинг 6.13. Использование функции is_subdass_of( ) В листинге 6.13 переменной $is_subclass( ) присваивается признак того, принадлежит ли объект $auto к субклассу родительского класса Vehicle. В приведенном фрагменте $auto относится к классу Vehicle; следовательно, переменной $is_subclass( ) будет присвоено значение TRUE. get_declared_classes( ) Функция get_declared_classes( ) возвращает массив с именами всех определенных классов (листинг 6.14). Синтаксис функции get_declared_classes( ): array get_declared_classes( ) Листинг 6.14. Получение списка классов функцией get_declared_classes( ) Итоги В этой главе были представлены некоторые концепции объектно-ориентированного программирования, при этом особое внимание уделялось их реализации в языке РНР. В частности, были рассмотрены следующие темы: общие принципы объектно-ориентированного программирования; классы, объекты и методы; простое и иерархическое наследование; абстрактные классы; перегрузка методов; функции для работы с классами и объектами в РНР. Технология объектно-ориентированного программирования не очень сложна, но полное усвоение всех концепций обычно требует некоторого времени. Однако я гарантирую, что затраченное время полностью окупится - ООП поднимет эффективность вашей работы на принципиально новый уровень. ГЛАВА 7 Файловый ввод/вывод и файловая система Данная глава посвящена одному из важнейших аспектов РНР - средствам файлового ввода/вывода. Как нетрудно предположить, входные и выходные потоки данных интенсивно используются при разработке web-приложений. Не ограничиваясь простым чтением/записью файлов, РНР предоставляет в распоряжение программиста средства просмотра и модификации серверной информации, а также запуска внешних программ. Этим средствам и посвящена настоящая глава. Проверка существования и размера файла Прежде чем пытаться работать с файлом, желательно убедиться в том, что он существует. Для решения этой задачи обычно используются две функции: file_exists( ) и is_file( ). file_exists( ) Функция f ilе_ехists ( ) проверяет, существует ли заданный файл. Если файл существует, функция возвращает TRUE, в противном случае возвращается FALSE. Синтаксис функции file_exists( ): bool file_exists(string файл) Пример проверки существования файла: if (! file_exists ($filename)) : print "File $filename does not exist!"; endif: is_file( ) Функция is_file( ) проверяет существование заданного файла и возможность выполнения с ним операций чтения/записи. В сущности, is_file( ) представляет собой более надежную версию file_exists( ), которая проверяет не только факт существования файла, но и то, поддерживает ли он чтение и запись данных: bool is_file(string файл) Следующий пример показывает, как убедиться в существовании файла и возможности выполнения операций с ним: $file = "somefile.txt"; if (is_file($file)) : print "The file $file is valid and exists!"; else : print "The file $file does not exist or it is not a valid file!"; endif: Убедившись в том, что нужный файл существует и с ним можно выполнять различные операции чтения/записи, можно переходить к следующему шагу - открытию файла. filesize( ) Функция filesize( ) возвращает размер (в байтах) файла с заданным именем или FALSE в случае ошибки. Синтаксис функции filesize( ): int filesize(string имя_файла) Предположим, вы хотите определить размер файла pastry.txt. Для получения нужной информации можно воспользоваться функцией filesize( ): $fs = filesize("pastry.txt"); print "Pastry.txt is $fs bytes."; Выводится следующий результат: Pastry.txt is 179 bytes. Прежде чем выполнять операции с файлом, необходимо открыть его и связать с файловым манипулятором, а после завершения работы с файлом его следует закрыть. Эти темы рассматриваются в следующем разделе. Открытие и закрытие файлов Прежде чем выполнять операции ввода/вывода с файлом, необходимо открыть его функцией fopen( ). fopen( ) Функция fopen( ) открывает файл (если он существует) и возвращает целое число - так называемый файловый манипулятор (file handle). Синтаксис функции fopen( ): int fopen (string файл, string режим [, int включение_пути]) Открываемый файл может находиться в локальной файловой системе, существовать в виде стандартного потока ввода/вывода или представлять файл в удаленной системе, принимаемой средствами HTTP или FTP. Параметр файл может задаваться в нескольких формах, перечисленных ниже: Если параметр содержит имя локального файла, функция fopen( ) открывает этот файл и возвращает манипулятор. Если параметр задан в виде php://stdin, php://stdout или php://stderr, открывается соответствующий стандартный поток ввода/вывода. Если параметр начинается с префикса http://, функция открывает подключение HTTP к серверу и возвращает манипулятор для указанного файла. Если параметр начинается с префикса ftp://, функция открывает подключение FTP к серверу и возвращает манипулятор для указанного файла. В этом случае следует обратить особое внимание на два обстоятельства: если сервер не поддерживает пассивный режим FTP, вызов fopen( ) завершается неудачей. Более того, FTP-файлы открываются либо для чтения, либо для записи. При работе в пассивном режиме сервер ЯР ожидает подключения со стороны клиентов. При работе в активном режиме сервер сам устанавливает соединение с клиентом. По умолчанию обычно используется активный режим. Параметр режим определяет возможность выполнения чтения и записи в файл. В табл. 7.1 перечислены некоторые значения, определяющие режим открытия файла. Таблица 7.1. Режимы открытия файла Режим Описание r Только чтение. Указатель текущей позиции устанавливается в начало файла r+ Чтение и запись. Указатель текущей позиции устанавливается в начало файла w Только запись. Указатель текущей позиции устанавливается в начало файла, а все содержимое файла уничтожается. Если файл не существует, функция пытается создать его w+ Чтение и запись. Указатель текущей позиции устанавливается в начало файла, а все содержимое файла уничтожается. Если файл не существует, функция пытается создать его a Только запись. Указатель текущей позиции устанавливается в конец файла. Если файл не существует, функция пытается создать его a+ Чтение и запись. Указатель текущей позиции устанавливается в конец файла. Если файл не существует, функция пытается создать его Если необязательный третий параметр включение_пути равен 1, то путь к файлу определяется по отношению к каталогу включаемых файлов, указанному в файле php.ini (см. главу 1). Ниже приведен пример открытия файла функцией fopen( ). Вызов die( ), используемый в сочетании с fopen( ), обеспечивает вывод сообщения об ошибке в том случае, если открыть файл не удастся: $file = "userdata.txt"; // Некоторый файл $fh = fopen($file, "a+") or die("File ($file) does not exist!"); Следующий фрагмент открывает подключение к сайту РНР (http://www.php.net): $site = "http://www.php.net": // Сервер, доступный через HTTP $sh = fopen($site., "r"); //Связать манипулятор с индексной страницей Php.net После завершения работы файл всегда следует закрывать функцией fclose( ). fclose ( ) Функция fclose( ) закрывает файл с заданным манипулятором. При успешном закрытии возвращается TRUE, при неудаче - FALSE. Синтаксис функции fclose( ): int fclose(int манипулятор) Функция fclose( ) успешно закрывает только те файлы, которые были ранее открыты функциями fopen( ) или fsockopen( ). Пример закрытия файла: $file = "userdata.txt"; if (file_exists($file)) : $fh = fopen($file, "r"); // Выполнить операции с файлом fclose($fh); else : print "File Sfile does not exist!"; endif; Запись в файл С открытыми файлами выполняются две основные операции - чтение и запись. is_writeable( ) Функция is_writeable( ) позволяет убедиться в том, что файл существует и для него разрешена операция записи. Возможность записи проверяется как для файла, так и для каталога. Синтаксис функции is_writeable( ): bool is_writeable (string файл) Одно важное обстоятельство: скорее всего, РНР будет работать под идентификатором пользователя, используемым web-сервером (как правило, «nobody»). Пример использования is_writeable( ) приведен в описании функции fwrite( ). fwrite ( ) Функция fwrite( ) записывает содержимое строковой переменной в файл, заданный файловым манипулятором. Синтаксис функции fwrite( ): int fwrite(int манипулятор, string переменная [, int длина]) Если при вызове функции передается необязательный параметр длина, запись останавливается либо после записи указанного количества символов, либо при достижении конца строки. Проверка возможности записи в файл продемонстрирована в следующем примере: Функция fputs( ) является псевдонимом fwrite( ) и может использоваться всюду, где используется fwrite( ). fputs( ) Функция fputs( ) является псевдонимом fwrite( ) и имеет точно такой же синтаксис. Синтаксис функции fputs( ): int fputs(int манипулятор, string переменная [, int длина]) Лично я предпочитаю использовать fputs( ). Следует помнить, что это всего лишь вопрос стиля, никак не связанный с какими-либо различиями между двумя функциями. Чтение из файла Несомненно, чтение является самой главной операцией, выполняемой с файлами. Ниже описаны некоторые функции, повышающие эффективность чтения из файла. Синтаксис этих функций практически точно копирует синтаксис аналогичных функций записи. is_readable( ) Функция i s_readable( ) позволяет убедиться в том, что файл существует и для него разрешена операция чтения. Возможность чтения проверяется как для файла, так и для каталога. Синтаксис функции is_readable( ): boo! is_readable (string файл] Скорее всего, РНР будет работать под идентификатором пользователя, используемым web-сервером (как правило, «nobody»), поэтому для того чтобы функция is_readable( ) возвращала TRUE, чтение из файла должно быть разрешено всем желающим. Следующий пример показывает, как убедиться в том, что файл существует и доступен для чтения: if ( is_readable($filename) ) : // Открыть файл и установить указатель текущей позиции в конец файла $fh = fopen($filename, "r"); else : print "$filename is not readable!"; endif; fread( ) Функция fread( ) читает из файла, заданного файловым манипулятором, заданное количество байт. Синтаксис функции fwrite( ): int fread(int манипулятор, int длина) Манипулятор должен ссылаться на открытый файл, доступный для чтения (см. описание функции is_readable( )). Чтение прекращается после прочтения заданного количества байт или при достижении конца файла. Рассмотрим текстовый файл pastry.txt, приведенный в листинге 7.1. Чтение и вывод этого файла в браузере осуществляется следующим фрагментом: $fh = fopen('pastry.txt', "r") or die("Can't open file!"); $file = fread($fh, filesize($fh)); print $file; fclose($fh); Используя функцию fllesize( ) для определения размера pastry.txt в байтах, вы гарантируете, что функция fread( ) прочитает все содержимое файла. Листинг 7.1. Текстовый файл pastry.txt Recipe: Pastry Dough 1 1/4 cups all-purpose flour 3/4 stick (6 tablespoons) unsalted butter, chopped 2 tablespoons vegetable shortening 1/4 teaspoon salt 3 tablespoons water fgetc( ) Функция fgetc( ) возвращает строку, содержащую один символ из файла в текущей позиции указателя, или FALSE при достижении конца файла. Синтаксис функции fgetc( ): string fgetc (int манипулятор) Манипулятор должен ссылаться на открытый файл, доступный для чтения (см. описание функции is_readable( ) ранее в этой главе). В следующем примере продемонстрированы посимвольное чтение и вывод файла с использованием функции fgetc( ): $fh = fopen("pastry.txt", "r"); while (! feof($fh)) : $char = fgetc($fh): print $char; endwhile; fclose($fh); fgets( ) Функция fgets( ) возвращает строку, прочитанную от текущей позиции указателя в файле, определяемом файловым манипулятором. Файловый указатель должен ссылаться на открытый файл, доступный для чтения (см. описание функции is_readable( ) ранее в этой главе). Синтаксис функции fgets( ): string fgets (int манипулятор, int длина) Чтение прекращается при выполнении одного из следующих условий: из файла прочитано длина - 1 байт; из файла прочитан символ новой строки (включается в возвращаемую строку); из файла прочитан признак конца файла (EOF). Если вы хотите организовать построчное чтение файла, передайте во втором параметре значение, заведомо превышающее количество байт в строке. Пример построчного чтения и вывода файла: $fh = fopen("pastry.txt", "r"); while (! feof($fh)); $line = fgets($fh, 4096); print $line. "
"; endwhile; fclose($fh): fgetss( ) Функция fgetss( ) полностью аналогична fgets( ) за одним исключением - она пытается удалять из прочитанного текста все теги HTML и РНР: string fgetss (Int манипулятор, int длина [, string разрешенные_теги]) Прежде чем переходить к примерам, ознакомьтесь с содержимым листинга 7.2 - этот файл используется в листингах 7.3 и 7.4. Листинг 7.2. Файл science.html Breaking News - Science

Alien lifeform discovered


August 20. 2000
Early this morning, a strange new form of fungus was found growing in the closet of W. J. Gilmore's old apartment refrigerator. It is not known if powerful radiation emanating from the tenant's computer monitor aided in this evolution. Листинг 7.З. Удаление тегов из файла HTML перед отображением в браузере Результат приведен ниже. Как видите, из файла science.html были удалены все теги HTML, что привело к потере форматирования: Breaking News - Science Alien lifeform discovered August 20. 2000 Early this morning, a strange new form of fungus was found growing in the closet of W. J. Gilmore's old apartment refrigerator. It is not known if powerful radiation emanating from the tenant's computer monitor aided in this evolution. В некоторых ситуациях из файла удаляются все теги, кроме некоторых - например, тегов разрыва строк
. Листинг 7.4 показывает, как это делается. Листинг 7.4. Выборочное удаление тегов из файла HTML "; while (! feof($fh)) : print fgetss($fh. 2048, $allowable); endwhile; fclose($fh); ?> Результат: Breaking News - Science Alien lifeform discovered August 20. 2000 Early this morning, a strange new form of fungus was found growing in the closet of W. J. Gilmore's old apartment refrigerator. It is not known if powerful radiation emanating from the tenant's computer monitor aided in this evolution. Как видите, функция fgetss( ) упрощает преобразование файлов, особенно при наличии большого количества файлов HTML, отформатированных сходным образом. Чтение файла в массив Функция file( ) загружает все содержимое файла в индексируемый массив. Каждый элемент массива соответствует одной строке файла. Синтаксис функции filе ( ): array file (string файл [, int включение_пути]) Если необязательный третий параметр включение_пути равен 1, то путь к файлу определяется по отношению к каталогу включаемых файлов, указанному в файле php.ini (см. главу 1). В листинге 7.5 функция file( ) используется для загрузки файла pastry.txt (см. листинг 7.1). Листинг 7.5. Загрузка файла pastry.txt функцией file( ) Line $line_num: ", htmlspecialchars($line ), "
\n" endwhile; ?> Каждая строка массива выводится вместе с номером: Line 0: Recipe: Pastry Dough Line 1: 1 1/4 cups all-purpose flour Line 2: 3/4 stick (6 tablespoons) unsalted butter, chopped Line 3: 2 tablespoons vegetable shortening Line 4: 1/4 teaspoon salt Line 5: 3 tablespoons water Перенаправление файла в стандартный выходной поток Функция readfile( ) читает содержимое файла и направляет его в стандартный вывод (в большинстве случаев - в браузер). Синтаксис функции readfile( ): int readfile (string файл [, int включение_пути]) Функция возвращает количество прочитанных байтов. Файл может находиться в локальной файловой системе, существовать в виде стандартного потока ввода/вывода или представлять файл в удаленной системе, принимаемой средствами HTTP или FTP. Параметр файл задается по тем же правилам, что и в функции fopen( ). Предположим, у вас имеется файл latorre.txt, содержимое которого вы хотите вывести в браузере: Restaurant "La Тоrrе." located in Nettuno, Italy, offers an eclectic blend of style. history, and fine seafood cuisine. Within the walls of the medieval borgo surrounding the city, one can dine while watching the passersby shop in the village boutiques. Comfort coupled with only the freshest seafare make La Torre one of Italy's finest restaurants. При выполнении следующего фрагмента все содержимое latorre.txt направляется в стандартный выходной поток: Открытие файлового манипулятора процесса popen( ) Наряду с обычными файлами можно открывать файловые манипуляторы для взаимодействия с процессами на сервере. Задача решается функцией рореn( ), которая имеет следующий синтаксис: int popen (string команда, string режим) Параметр команда определяет выполняемую системную команду, а параметр режим описывает режим доступа: Результат выглядит так: Basil, sage, ginger Функция fpassthru( ) является аналогом функции passthru( ), рассматриваемой в разделе «Запуск внешних программ» этой главы. pclose( ) После выполнения всех операций файл или процесс необходимо закрыть. Функция pclose( ) закрывает соединение с процессом, заданным манипулятором, по аналогии с тем, как функция fclose( ) закрывает файл, открытый функцией fopen( ). Синтаксис функции pclose( ): int pclose (int манипулятор} В параметре манипулятор передается манипулятор, полученный ранее при успешном вызове рореn( ). Открытие соединения через сокет РНР не ограничивается взаимодействием с файлами и процессами - вы также можете устанавливать соединения через сокеты. Сокет (socket) представляет собой программную абстракцию, позволяющую устанавливать связь с различными службами другого компьютера. fsockopen( ) Функция fsockopen( ) устанавливает сокетное соединение с сервером в Интернете через протокол TCP или UDP. Синтаксис функции fsockopen( ): int fsockopen (string узел, int порт [, int код_ошибки [, string текст_ошибки [, int тайм-аут]]]) Необязательные параметры код_ошибки и текст_ошибки содержат информацию, которая будет выводиться в случае неудачи при подключении к серверу. Оба параметра должны передаваться по ссылке. Третий необязательный параметр, тайм-аут, задает продолжительность ожидания ответа от сервера (в секундах). В листинге 7.6 продемонстрировано применение функции fsockopen( ) для получения информации о сервере. Однако перед рассмотрением листинга 7.6 необходимо познакомиться еще с одной функцией - socket_set_blocking( ). UDP (User Datagram Protocol) - коммуникационный протокол, не ориентированный на соединение. socket_set_blocking( ) Функция socket_set_b1ocki ng( ) позволяет установить контроль над тайм-аутом для операций с сервером: socket_set_blocking(int манипулятор, boolean режим) Параметр манипулятор задает открытый ранее сокет, а параметр режим выбирает режим, в который переключается сокет (TRUE для блокирующего режима, FALSE для неблокирующего режима). Пример использования функций fsockopen( ) и socket_set_blocking( ) приведен в листинге 7.6. Листинг 7.6. Использование функции fsockopen() для получения информации о сервере В результате выполнения листинга 7.6 выводится следующий результат: НТТР/1.1 200 OK Server: Microsoft-IIS/4.0 Content-location: http://www.apress.com/0efault.htm Date: Sat. 19 Aug 2000 23:03:25 GMT Content-Type: text/html Accept-Ranges: bytes Last-Modified: Wed. 19 Jul 2000 20:25:06 GMT ETag: "f0a61666dbff1bf1:34a5" Content-Length: 1311 pfsockopen( ) Функция pfsockopen( ) представляет собой устойчивую (persistent) версию fsockopen( ). Это означает, что соединение не будет автоматически разорвано по завершении сценария, в котором была вызвана функция. Синтаксис функции pfsockopen( ): int pfsockopen (string узел, int порт [, int код_ошибки [, string текст _ошибки [, int тайм-аут]]]) В зависимости от конкретных целей вашего приложения может оказаться удобнее использовать pfsockopen( ) вместо fsockopen( ). Запуск внешних программ Сценарии РНР также могут выполнять программы, находящиеся на сервере. Такая возможность особенно часто используется при администрировании системы через web-браузер, а также для более удобного получения сводной информации о системе. ехес( ) Функция ехес( ) запускает заданную программу и возвращает последнюю строку ее выходных данных. Синтаксис функции ехес( ): string exec (string команда [, string массив [, int возврат]]) Обратите внимание: функция ехес( ) только выполняет команду, не выводя результатов ее работы. Все выходные данные команды можно сохранить в необязательном параметре массив. Кроме того, если при заданном параметре массив также задается переменная возврат, последней присваивается код возврата выполненной команды. Листинг 7.7 показывает, как использовать функцию ехес( ) для выполнения системной функции UNIX ping. Листинг 7.7. Проверка связи с сервером с применением функции ехес( ) $ping[$i]"; endfor; ?> Результат: PING www.php.net (208.247.106.187): 56 data bytes 64 bytes from 208.247.106.187: icmp_seq=0 ttl=243 time=66.602 ms 64 bytes from 208.247.106.187: icmp_seq=1 ttl=243 time=55.723 ms 64 bytes from 208.247.106.187: icmp_seq=2 ttl=243 time=70.779 ms 64 bytes from 208.247.106.187: icmp_seq=3 ttl=243 time=55.339 ms 64 bytes from 208.247.106.187: icmp_seq=4 ttl=243 time=69.865 ms -- www.php.net ping statistics -- 5 packets transmitted. 5 packets received. 0% packet loss round-trip min/avg/max/stddev - 55.339/63.662/70.779/6.783 ms Обратные апострофы Существует и другой способ выполнения системных команд, не требующий вызова функций, - выполняемая команда заключается в обратные апострофы (` `), а результаты ее работы отображаются в браузере. Пример: $output = `ls`; print "
$output
"; Этот фрагмент выводит в браузер содержимое каталога, в котором находится сценарий. Внутренний параметр ping -с 5 (-п 5 в системе Windows) задает количество опросов сервера. Если вы хотите просто вернуть неформатированные результаты выполнения команды, воспользуйтесь функцией passthru( ), описанной ниже. passthru( ) Функция passthru( ) работает почти так же, как ехес( ), за одним исключением - она автоматически выводит результаты выполнения команды. Синтаксис функции passthru( ): void passthru(string команда [, int возврат]) Если при вызове passthru( ) передается необязательный параметр возврат, этой переменной присваивается код возврата выполненной команды. escapeshellcmd( ) Функция escapeshellcmd( ) экранирует все потенциально опасные символы, которые могут быть введены пользователем (например, на форме HTML), для выполнения команд exec( ), passthru( ), system( ) или рореn( ). Синтаксис: string escapeshellcmd (string команда) К пользовательскому вводу всегда следует относиться с определенной долей осторожности, но даже в этом случае пользователи могут вводить команды, которые будут исполняться функциями запуска системных команд. Рассмотрим следующий фрагмент: $user_input = `rm -rf *`; // Удалить родительский каталог и все его подкаталоги ехес($user_input); // Выполнить $user_input !!! Если не предпринять никаких мер предосторожности, такая команда приведет к катастрофе. Впрочем, можно воспользоваться функций escapeshellcmd( ) для экранирования пользовательского ввода: $user_input = `rm - rf *`; // Удалить родительский каталог и все его подкаталоги ехес( escapeshellcmd($user_input)); // Экранировать опасные символы Функция escapeshellcmd( ) экранирует символ *, предотвращая катастрофические последствия выполнения команды. Безопасность является одним из важнейших аспектов программирования в среде Web, поэтому я посвятил целую главу этой теме и ее отношению к программированию РНР. За дополнительной информацией обращайтесь к главе 16. Работа с файловой системой В РНР существуют функции для просмотра и выполнения различных операций с файлами на сервере. Информация об атрибутах серверных файлов (местонахождение, владелец и привилегии) часто бывает полезной. basename( ) Функция basename( ) выделяет имя файла из переданного полного имени. Синтаксис функции basename( ): string basename(string полное_имя) Выделение базового имени файла из полного имени происходит следующим образом: $path = "/usr/local/phppower/htdocs/index.php"; $file = basename($path); // $file = "index.php" Фактически эта функция удаляет из полного имени путь и оставляет только имя файла. getlastmod( ) Функция getlastmod( ) возвращает дату и время последней модификации страницы, из которой вызывается функция. Синтаксис функции getlastmod( ): int getlastmod(void) Возвращаемое значение соответствует формату даты/времени UNIX, и для его форматирования можно воспользоваться функцией date( ). Следующий фрагмент выводит дату последней модификации страницы: echo "Last modified: ".date( "H:i:s a". getlastmod( ) ); stat( ) Функция stat( ) возвращает индексируемый массив с подробной информацией о файле с заданным именем: array stat(string имя_файла) В элементах массива возвращается следующая информация: 0 Устройство 1 Индексный узел (inode) 2 Режим защиты индексного узла 3 Количество ссылок 4 Идентификатор пользователя владельца 5 Идентификатор группы владельца 6 Тип устройства индексного узла 7 Размер в байтах 8 Время последнего обращения 9 Время последней модификации 10 Время последнего изменения 11 Размер блока при вводе/выводе в файловой системе 12 Количество выделенных блоков Таким образом, если вы хотите узнать время последнего обращения к файлу, обратитесь к элементу 8 возвращаемого массива. Рассмотрим пример: $file - "datafile.txt"; list($dev, $inode, $inodep, $nlink, $uid, $gid, $inodev, $size, $atime, $mtime, $ctime, $bsize) = stat($file); print "$file is $size bytes.
"; print "Last access time: $atime
"; print "Last modification time: $mtime
"; Результат: popen.php is 289 bytes. Last access time: August 15 2000 12:00:00 Last modification time: August 15 2000 10:07:18 В этом примере я воспользовался конструкцией list () для присваивания имен каждому возвращаемому значению. Конечно, с таким же успехом можно вернуть массив, в цикле перебрать элементы и вывести всю необходимую информацию. Как видите, функция stat ( ) позволяет получить различные полезные сведения о файле. Отображение и изменение характеристик файлов У каждого файла в системах семейства UNIX есть три важные характеристики: принадлежность группе; владелец; разрешения (permissions). Все эти характеристики можно изменить при помощи соответствующих функций РНР. Функции, описанные в этом разделе, не работают в системах семейства Windows. Если у вас нет опыта работы в операционных системах UNIX, информацию о характеристиках файловой системы UNIX можно получить по адресу http://sunsite.auc.dk/linux-newbie/FAQ2.htm. Темы принадлежности группе, владения и разрешений рассматриваются в разделе 3.2.6. chgrp( ) Функция chgrp( ) пытается сменить группу, которой принадлежит заданный файл. Синтаксис функции chgrp( ): int chgrp (string имя_файла, mixed группа) filegroup( ) Функция filegroup( ) возвращает идентификатор группы владельца файла с заданным именем или FALSE в случае ошибки. Синтаксис функции filegroup( ): int filegroup (string имя_файла) chmod( ) Функция chmod( ) изменяет разрешения файла с заданным именем. Синтаксис функции chmod( ): int chmod (string имя_файла, int разрешения) Разрешения задаются в восьмеричной системе. Специфика задания параметра функции chmod ( ) продемонстрирована в следующем примере: chmod("data_file.txt", g+r); // He работает chmod("data_file.txt", 766); // Не работает chmod("data_file.txt", 0766); // Работает fileperms( ) Функция fileperms( ) возвращает разрешения файла с заданным именем или FALSE в случае ошибки. Синтаксис функции fileperms( ): int fileperms (string имя_файла) chown( ) Функция chown( ) пытается сменить владельца файла. Право изменения владельца файла предоставляется только привилегированному пользователю. Синтаксис функции chown( ): int chown (string имя_файла, mixed пользователь) fileowner( ) Функция fileowner( ) возвращает идентификатор пользователя для владельца файла с заданным именем. Синтаксис функции fileowner( ): int fileowner (string имя_файла) Копирование и переименование файлов К числу других полезных системных функций, которые могут выполняться в сценариях РНР, относятся копирование и переименование файлов на сервере. Эти операции выполняются двумя функциями: сору( ) и rename( ). сору( ) Скопировать файл в сценарии РНР ничуть не сложнее, чем при помощи команды UNIX ср. Задача решается функцией РНР сору( ). Синтаксис функции сору( ): int copy (string источник, string приемник) Функция сору( ) пытается скопировать файл источник в файл приемник; в случае успеха возвращается TRUE, а при неудаче - FALSE. Если файл приемник не существует, функция сору( ) создает его. Следующий пример показывает, как создать резервную копию файла при помощи функции сору( ): $data_file = "datal.txt"; copy($data_file. $data_file'.bak') or die("Could not copy $data_file"); rename ( ) Функция rename( ) переименовывает файл. В случае успеха возвращается TRUE, a при неудаче - FALSE. Синтаксис функции rename( ): bool rename (string старое_имя, string новое_имя) Пример переименования файла функцией rename( ): $data_file = "datal.txt"; rename($data file, $datafile'.old') or die ("Could not rename $data file"); Удаление файлов unlink( ) Функция unlink( ) удаляет файл с заданным именем. Синтаксис: int unlink (string файл) Если вы работаете с РНР в системе Windows, при использовании этой функции иногда возникают проблемы. В этом случае можно воспользоваться описанной выше функцией system( ) и удалить файл командой DOS del: system ("del filename.txt"); Работа с каталогами Функции РНР позволяют просматривать содержимое каталогов и перемещаться по ним. В листинге 7.8 изображена типичная структура каталогов в системе UNIX. Листинг 7.8. Типичная структура каталогов drwxr-xr-x 4 root wheel 512 Aug 13 13:51 book/ drwxr-xr-x 4 root wheel 512 Aug 13 13:51 code/ -rw-r--r-- 1 root wheel 115 Aug 4 09:53 index.html drwxr-xr-x 7 root wheel 1024 Jun 29 13:03 manual/ -rw-r--r-- 1 root wheel 19 Aug 12 12:15 test.php dirname( ) Функция dirname( ) дополняет basename( ) - она извлекает путь из полного имени файла. Синтаксис функции dirname( ): string dirname (string путь) Пример использования dirname( ) для извлечения пути из полного имени: $path = "/usr/locla/phppower/htdocs/index.php"; $file = dirname($path); // $file = "usr/local/phppower/htdocs" Функция dirname( ) иногда используется в сочетании с переменной $SCRIPT_FILENAME для получения полного пути к сценарию, из которого выполняется команда: $dir - dirname($SCRIPT_FILENAME); is_dir( ) Функция is_dir( ) проверяет, является ли файл с заданным именем каталогом: bool is_dir (string имя_файла) В следующем примере используется структура каталогов из листинга 7.8: $isdir = is_dir("index.html"); // Возвращает FALSE $isdir = is_dir("book"); // Возвращает TRUE mkdir() Функция mkdir( ) делает то же, что и одноименная команда UNIX, - она создает новый каталог. Синтаксис функции mkdir( ): int mkdir (string путь, int режим) Параметр путь определяет путь для создания нового каталога. Не забудьте завершить параметр именем нового каталога! Параметр режим определяет разрешения, назначаемые созданному каталогу. opendir( ) Подобно тому как функция fopen( ) открывает манипулятор для работы с заданным файлом, функция opendir( ) открывает манипулятор для работы с каталогом. Синтаксис функции opendir( ): int opendir (string путь) closedir( ) Функция closedir( ) закрывает манипулятор каталога, переданный в качестве параметра. Синтаксис функции closedir( ): void closedir(int манипулятор_каталога) readdir( ) Функция readdir( ) возвращает очередной элемент заданного каталога. Синтаксис: string readdir(int манипулятор_каталога) С помощью этой функции можно легко вывести список всех файлов и подкаталогов, находящихся в текущем каталоге: $dh = opendir(' . ); while ($file = readdir($dh)) : print "$file
"; endwhile; closedir($dh); chdir( ) Функция chdir( ) работает так же, как команда UNIX cd, - она осуществляет переход в каталог, заданный параметром. Синтаксис функции chdir( ): int chdir (string каталог) В следующем примере мы переходим в подкаталог book/ и выводим его содержимое: $newdir = "book"; chdir($newdir) or die("Could not change to directory ($newdir)"); $dh = opendir(' . '); print "Files:"; while ($file = readdir($dh)) ; print "$file
"; endwhile; closedir($dh); rewinddir( ) Функция rewlnddir( ) переводит указатель текущей позиции в начало каталога, открытого функцией opendir( ). Синтаксис функции rewinddir( ): void rewinddir (int нанипулятор_каталога) Проект 1: простой счетчик обращений Сценарий, представленный в этом разделе, подсчитывает количество обращений к странице, в которой он находится. Прежде чем переходить к программному коду в листинге 7.9, просмотрите алгоритм, написанный на псевдокоде: Присвоить переменной $access имя файла, в котором будет храниться значение счетчика. Использовать функцию filе( ) для чтения содержимого $access в массив $visits. Префикс @ перед именем функции подавляет возможные ошибки (например, отсутствие файла с заданным именем). Присвоить переменной $current_visitors значение первого (и единственного) элемента массива $visits. Увеличить значение $current_visitors на 1. Открыть файл $access для записи и установить указатель текущей позиции в начало файла. Записать значение $current_visitors в файл $access. Закрыть манипулятор, ссылающийся на файл $access. Листинг 7.9. Простой счетчик обращений Проект 2: построение карты сайта Сценарий, приведенный в листинге 7.10, строит карту сайта - иерархическое изображение всех папок и файлов на сервере, начиная с заданного каталога. При вычислении отступов элементов, из которых состоит карта сайта, используются функции, определенные в этой и предыдущих главах. Прежде чем переходить к программе, просмотрите алгоритм, написанный на псевдокоде: Объявить служебные переменные для хранения родительского каталога, имени графического файла с изображением папки, названия страницы и флага серверной ОС (Windows или другая система). Объявить функцию display_directory( ), которая читает содержимое каталога и форматирует его для вывода в браузере. Построить путь к каталогу объединением имени, передаваемого в переменной $dir1, с $dir. Открыть каталог и прочитать его содержимое. Отформатировать имена каталога и файлов и вывести их в браузере. Если текущий файл является каталогом, рекурсивно вызвать функцию display_di rectory( ) и передать ей имя нового каталога для вывода. Вычислить отступ, используемый при форматировании вывода. Если файл не является каталогом, он форматируется для отображения в виде гиперссылки (а также вычисляется отступ, используемый при форматировании). Листинг 7.10. Программа sitemap.php // Файл: sitemap.php // Назначение: построение карты сайта // Каталог, с которого начинается построение карты $beg_path = "C:\Program FilesVApache Group\Apache\htdocs\phprecipes"; // Файл с графическим изображением папки. // Путь должен задаваться Относительно* корневого каталога сервера Apache $folder_location = "C:\My Documents\PHP for Programmers\FINAL CHPS\graphics\folder.gif"; // Текст в заголовке окна $page_name = "PHPRecipes SiteMap"; // В какой системе будет использоваться сценарий - Linux или Windows? // (0 - Windows; 1 - Linux) $usingjinux = 0; // Функция: display_directory // Назначение: чтение содержимого каталога, определяемого параметром // $dir1, с последующим форматированием иерархии каталогов и файлов. // Функция может вызываться рекурсивно. function display_directory ($dir1, $folder_location, $using_linux, $init_depth) { // Обновить путь $dir.= $dir1; Sdh = opendir($dir); while($file = readdir($dh)) : // Элементы каталогов "." и ".." не выводятся. if ( ($file != ".") && ($file != "..") ) : if ($using_linux == 0 ) : $depth = explode("\\", $dir): else : $depth = explode("/", $dir); endif ; $curtent_depth = sizeof( $depth); // Построить путь по правилам используемой операционной системы. if ($using_linux == 0) : $tab_depth = $current_deptn - $init_depth; $file = $dir. "\\", $file; else : $file = $dir. "/",$file; endif; // Переменная $file содержит каталог? if ( is dir($file) ) : $х = 0; // Вычислить отступ while ( $х < ($tab_depth * 2) ) : print " "; $х++; endwhile; print "\"[dir]\" ".basename($file)."
"; // Увеличить счетчик   // Рекурсивный вызов функции display_directory() display_directory($file, $folder_location, $using_linux, $init_depth); // He каталог else : // Построить путь по правилам используемой // операционной системы. if ($using_linux == 0) : $tab_depth = ($current_depth - $init_depth) - 2; $x = 0; // Вычислить отступ while ( $x < (($tab_depth * 2) + 5) ) : print " "; $x++; endwhile: print "".basename($file)."
"; else : print "".basename($file)."
"; endif: endif; // Is_dir(file) endif: // If ! "." or ".." endwhile; // Закрыть каталог closedir($dh); <? print "$page_name"; ?> На рис. 7.1 изображен результат выполнения сценария для каталога с несколькими главами этой книги. Рис. 7.1. Вывод структуры каталога на сервере с использованием сценария sitemap.php Итоги В этой главе были представлены многие средства РНР, предназначенные для работы с файлами. В частности, мы рассмотрели следующие вопросы: проверку существования файлов; открытие и закрытие файлов и потоков ввода/вывода; запись в файл и чтение из него; перенаправление файла в выходной поток; запуск внешних программ; операции с файловой системой. Материал этой главы подготовил почву для следующей главы, «Строки и регулярные выражения», поскольку при разработке web-приложений обработка строк и операции ввода/вывода очень тесно связаны.