Для метода GET
При отправке данных формы с помощью метода GET содержимое формы добавляется к URL после знака вопроса в виде пар имя=значения, объединенных с помощью амперсанта &:
action?name1=value1&name2=value2&name3=value3
Здесь action – это URL-адрес программы, которая должна обрабатывать форму (это либо программа, заданная в атрибуте action тега form, либо сама текущая программа, если этот атрибут опущен). Имена name1, name2, name3 соответствуют именам элементов формы, а value1, value2, value3 – значениям этих элементов. Все специальные символы, включая = и &, в именах или значениях этих параметров будут опущены. Поэтому не стоит использовать в названиях или значениях элементов формы эти символы и символы кириллицы в идентификаторах.
Если в поле для ввода ввести какой-нибудь служебный символ, то он будет передан в его шестнадцатеричном коде, например, символ $ заменится на %24. Так же передаются и русские буквы.
Для полей ввода текста и пароля (это элементы input с атрибутом type=text и type=password), значением будет то, что введет пользователь. Если пользователь ничего не вводит в такое поле, то в строке запроса будет присутствовать элемент name=, где name соответствует имени этого элемента формы.
Для кнопок типа checkbox и radio button значение value определяется атрибутом VALUE в том случае, когда кнопка отмечена. Не отмеченные кнопки при составлении строки запроса игнорируются целиком. Несколько кнопок типа checkbox могут иметь один атрибут NAME (и различные VALUE), если это необходимо. Кнопки типа radio button предназначены для одного из всех предложенных вариантов и поэтому должны иметь одинаковый атрибут NAME и различные атрибуты VALUE.
В принципе создавать HTML-форму для передачи данных методом GET не обязательно. Можно просто добавить в строку URL нужные переменные и их значения.
http://phpbook.info/test.php?id=10&user=pit
Пример 4.1. Передача данных в URL (html, txt)
В связи с этим у передачи данных методом GET есть один существенный недостаток – любой может подделать значения параметров. Поэтому не советуем использовать этот метод для доступа к защищенным паролем страницам, для передачи информации, влияющей на безопасность работы программы или сервера. Кроме того, не стоит применять метод GET для передачи информации, которую не разрешено изменять пользователю.
Несмотря на все эти недостатки, использовать метод GET достаточно удобно при отладке скриптов (тогда можно видеть значения и имена передаваемых переменных) и для передачи параметров, не влияющих на безопасность.
Для метода POST
Содержимое формы кодируется точно так же, как для метода GET (см. выше), но вместо добавления строки к URL содержимое запроса посылается блоком данных как часть операции POST. Если присутствует атрибут ACTION, то значение URL, которое там находится, определяет, куда посылать этот блок данных. Этот метод, как уже отмечалось, рекомендуется для передачи больших по объему блоков данных.
Информация, введенная пользователем и отправленная серверу с помощью метода POST, подается на стандартный ввод программе, указанной в атрибуте action, или текущему скрипту, если этот атрибут опущен. Длина посылаемого файла передается в переменной окружения CONTENT_LENGTH, а тип данных – в переменной CONTENT_TYPE.
Передать данные методом POST можно только с помощью HTML-формы, поскольку данные передаются в теле запроса, а не в заголовке, как в GET. Соответственно и изменить значение параметров можно, только изменив значение, введенное в форму. При использовании POST пользователь не видит передаваемые серверу данные.
Основное преимущество POST запросов – это их большая безопасность и функциональность по сравнению с GET-запросами. Поэтому метод POST чаще используют для передачи важной информации, а также информации большого объема. Тем не менее не стоит целиком полагаться на безопасность этого механизма, поскольку данные POST запроса также можно подделать, например создав html-файл на своей машине и заполнив его нужными данными. Кроме того, не все клиенты могут применять метод POST, что ограничивает варианты его использования.
При отправке данных на сервер любым методом передаются не только сами данные, введенные пользователем, но и ряд переменных, называемых переменными окружения, характеризующих клиента, историю его работы, пути к файлам и т.п. Вот некоторые из переменных окружения:
REMOTE_ADDR – IP-адрес хоста (компьютера), отправляющего запрос;REMOTE_HOST – имя хоста, с которого отправлен запрос;HTTP_REFERER – адрес страницы, ссылающейся на текущий скрипт; REQUEST_METHOD – метод, который был использован при отправке запроса;QUERY_STRING – информация, находящаяся в URL после знака вопроса; SCRIPT_NAME – виртуальный путь к программе, которая должна выполняться;HTTP_USER_AGENT – информация о браузере, который использует клиент
Форма запроса клиента
Клиент отсылает серверу запрос в одной из двух форм: в полной или сокращенной. Запрос в первой форме называется соответственно полным запросом, а во второй форме – простым запросом.
Простой запрос содержит метод доступа и адрес ресурса. Формально это можно записать так:
<Простой-Запрос> := <Метод> <символ пробел> <Запрашиваемый-URI> <символ новой строки>
В качестве метода могут быть указаны GET, POST, HEAD, PUT, DELETE и другие. О наиболее распространенных из них мы поговорим немного позже. В качестве запрашиваемого URI чаще всего используется URL-адрес ресурса.
Пример простого запроса:
GET http://phpbook.info/
Здесь GET – это метод доступа, т.е. метод, который должен быть применен к запрашиваемому ресурсу, а http://phpbook.info/ – это URL-адрес запрашиваемого ресурса.
Полный запрос содержит строку состояния, несколько заголовков (заголовок запроса, общий заголовок или заголовок содержания) и, возможно, тело запроса. Формально общий вид полного запроса можно записать так:
<Полный запрос> := <Строка Состояния> (<Общий заголовок>|<Заголовок запроса>| <Заголовок содержания>) <символ новой строки> [<содержание запроса>]
Квадратные скобки здесь обозначают необязательные элементы заголовка, через вертикальную черту перечислены альтернативные варианты. Элемент <Строка состояния> содержит метод запроса и URI ресурса (как и простой запрос) и, кроме того, используемую версию протокола HTTP. Например, для вызова внешней программы можно задействовать следующую строку состояния:
POST http://phpbook.info/cgi-bin/test HTTP/1.0
В данном случае используется метод POST и протокол HTTP версии 1.0.
В обеих формах запроса важное место занимает URI запрашиваемого ресурса. Чаще всего URI используется в виде URL-адреса ресурса. При обращении к серверу можно применять как полную форму URL, так и упрощенную.
Полная форма содержит тип протокола доступа, адрес сервера ресурса и адрес ресурса на сервере (рисунок 4.2).
В сокращенной форме опускают протокол и адрес сервера, указывая только местоположение ресурса от корня сервера. Полную форму используют, если возможна пересылка запроса другому серверу. Если же работа происходит только с одним сервером, то чаще применяют сокращенную форму.
Рис. 4.2. Полная форма URL
Далее мы рассмотрим наиболее распространенные методы отправки запросов.
Использование HTML-форм для передачи данных на сервер
Как передавать данные серверу? Для этого в языке HTML есть специальная конструкция – формы. Формы предназначены для того чтобы получать от пользователя информацию. Например, вам нужно знать логин и пароль пользователя для того, чтобы определить, на какие страницы сайта его можно допускать. Или вам необходимы личные данные пользователя, чтобы была возможность с ним связаться. Формы как раз и применяются для ввода такой информации. В них можно вводить текст или выбирать подходящие варианты из списка. Данные, записанные в форму, отправляются для обработки специальной программе (например, скрипту на PHP) на сервере. В зависимости от введенных пользователем данных эта программа может формировать различные web-страницы, отправлять запросы к базе данных, запускать различные приложения и т.п.
Разберемся с синтаксисом HTML-форм. Возможно, многие с ним знакомы, но мы все же повторим основные моменты, поскольку это важно.
Итак, для создания формы в языке HTML используется тег FORM. Внутри него находится одна или несколько команд INPUT. С помощью атрибутов action и method тега FORM задаются имя программы, которая будет обрабатывать данные формы, и метод запроса, соответственно. Команда INPUT определяет тип и различные характеристики запрашиваемой информации. Отправка данных формы происходит после нажатия кнопки input типа submit. Создадим форму для регистрации участников заочной школы программирования.
Листинг 4.0. form.html (html, txt)
После обработки браузером этот файл будет выглядеть примерно так:
Рис. 4.3. Пример html-формы
Вот так создаются и выглядят HTML-формы. Будем считать, что мы научились или вспомнили, как их создавать. Как мы видим, в форме можно указывать метод передачи данных. Посмотрим, что будет происходить, если указать метод GET или POST, и в чем будет разница.
Методы
Как уже говорилось, любой запрос клиента к серверу должен начинаться с указания метода. Метод сообщает о цели запроса клиента. Протокол HTTP поддерживает достаточно много методов, но реально используются только три: POST, GET и HEAD. Метод GET позволяет получить любые данные, идентифицированные с помощью URI в запросе ресурса. Если URI указывает на программу, то возвращается результат работы программы, а не ее текст (если, конечно, текст не есть результат ее работы). Дополнительная информация, необходимая для обработки запроса, встраивается в сам запрос (в строку статуса). При использовании метода GET в поле тела ресурса возвращается собственно затребованная информация (текст HTML-документа, например).
Существует разновидность метода GET – условный GET. Этот метод сообщает серверу о том, что на запрос нужно ответить, только если выполнено условие, содержащееся в поле if-Modified-Since заголовка запроса. Если говорить более точно, то тело ресурса передается в ответ на запрос, если этот ресурс изменялся после даты, указанной в if-Modified-Since.
Метод HEAD аналогичен методу GET, только не возвращает тело ресурса и не имеет условного аналога. Метод HEAD используют для получения информации о ресурсе. Это может пригодиться, например, при решении задачи тестирования гипертекстовых ссылок.
Метод POST разработан для передачи на сервер такой информации, как аннотации ресурсов, новостные и почтовые сообщения, данные для добавления в базу данных, т.е. для передачи информации большого объема и достаточно важной. В отличие от методов GET и HEAD, в POST передается тело ресурса, которое и является информацией, получаемой из полей форм или других источников ввода.
До сих пор мы только теоретизировали, знакомились с основными понятиями. Теперь пора научиться использовать все это на практике. Далее в лекции мы рассмотрим, как посылать запросы серверу и как обрабатывать его ответы.
Обработка запросов с помощью PHP
До сих пор мы упоминали только, что запросы клиента обрабатываются на сервере с помощью специальной программы. На самом деле эту программу мы можем написать сами, в том числе и на языке PHP, и она будет делать с полученными данными все, что мы захотим. Для того чтобы написать эту программу, необходимо познакомиться с некоторыми правилами и инструментами, предлагаемыми для этих целей PHP.
Внутри PHP-скрипта имеется несколько способов получения доступа к данным, переданным клиентом по протоколу HTTP. До версии PHP 4.1.0 доступ к таким данным осуществлялся по именам переданных переменных (напомним, что данные передаются в виде пар «имя переменной, символ «=», значение переменной»). Таким образом, если, например, было передано first_name=Nina, то внутри скрипта появлялась переменная $first_name со значением Nina. Если требовалось различать, каким методом были переданы данные, то использовались ассоциативные массивы $HTTP_POST_VARS и $HTTP_GET_VARS, ключами которых являлись имена переданных переменных, а значениями – соответственно значения этих переменных. Таким образом, если пара first_name=Nina передана методом GET, то $HTTP_GET_VARS["first_name"]="Nina".
Использовать в программе имена переданных переменных напрямую небезопасно. Поэтому было решено начиная с PHP 4.1.0 задействовать для обращения к переменным, переданным с помощью HTTP-запросов, специальный массив – $_REQUEST. Этот массив содержит данные, переданные методами POST и GET, а также с помощью HTTP cookies. Это суперглобальный ассоциативный массив, т.е. его значения можно получить в любом месте программы, используя в качестве ключа имя соответствующей переменной (элемента формы).
Пример 4.2. Допустим, мы создали форму для регистрации участников заочной школы программирования, как в приведенном выше примере. Тогда в файле 1.php, обрабатывающем эту форму, можно написать следующее:
<?php $str = "Здравствуйте, ".$_REQUEST["first_name"]. " ".$_REQUEST["last_name"]."! <br>"; $str .="Вы выбрали для изучения курс по ".$_REQUEST["kurs"]; echo $str; ?>
Пример 4.2. Файл 1.php, обрабатывающий форму form.html (html, txt)
Тогда, если в форму мы ввели имя «Вася», фамилию «Петров» и выбрали среди всех курсов курс по PHP, на экране браузера получим такое сообщение:
Здравствуйте, Вася Петров! Вы выбрали для изучения курс по PHP
После введения массива $_REQUEST массивы $HTTP_POST_VARS и $HTTP_GET_VARS для однородности были переименованы в $_POST и $_GET соответственно, но сами они из обихода не исчезли из соображений совместимости с предыдущими версиями PHP. В отличие от своих предшественников, массивы $_POST и $_GET стали суперглобальными, т.е. доступными напрямую и внутри функций и методов.
Приведем пример использования этих массивов. Допустим, нам нужно обработать форму, содержащую элементы ввода с именами first_name, last_name, kurs (например, форму form.html, приведенную выше). Данные были переданы методом POST, и данные, переданные другими методами, мы обрабатывать не хотим. Это можно сделать следующим образом:
<?php $str = "Здравствуйте, ".$_POST ["first_name"]." ".$_POST ["last_name"] ."! <br>"; $str .= "Вы выбрали для изучения курс по ". $_POST["kurs"]; echo $str; ?>
Тогда на экране браузера, если мы ввели имя «Вася», фамилию «Петров» и выбрали среди всех курсов курс по PHP, увидим сообщение, как в предыдущем примере:
Здравствуйте, Вася Петров! Вы выбрали для изучения курс по PHP
Для того чтобы сохранить возможность обработки скриптов более ранних версий, чем PHP 4.1.0, была введена директива register_globals, разрешающая или запрещающая доступ к переменным непосредственно по их именам. Если в файле настроек PHP параметр register_globals=On, то к переменным, переданным серверу методами GET и POST, можно обращаться просто по их именам (т.е. можно писать $first_name). Если же register_globals=Off, то нужно писать $_REQUEST["first_name"] или $_POST["first_name"], $_GET["first_name"], $HTTP_POST_VARS["first_name"], $HTTP_GET_VARS["first_name"]. С точки зрения безопасности эту директиву лучше отключать (т.е. register_globals=Off). При включенной директиве register_globals перечисленные выше массивы также будут содержать данные, переданные клиентом.
Иногда возникает необходимость узнать значение какой-либо переменной окружения, например метод, использовавшийся при передаче запроса или IP-адрес компьютера, отправившего запрос. Получить такую информацию можно с помощью функции getenv(). Она возвращает значение переменной окружения, имя которой передано ей в качестве параметра.
<? getenv("REQUEST_METHOD"); // возвратит использованный метод echo getenv ("REMOTE_ADDR"); // выведет IP-адрес пользователя, // пославшего запрос ?>
Пример 4.3. Использование функции getenv() (html, txt)
Как мы уже говорили, если используется метод GET, то данные передаются добавлением строки запроса в виде пар «имя_переменной=значение к URL-адресу ресурса». Все, что записано в URL после знака вопроса, можно получить с помощью команды
getenv("QUERY_STRING");
Благодаря этому можно по методу GET передавать данные в каком-нибудь другом виде. Например, указывать только значения нескольких параметров через знак плюс, а в скрипте разбирать строку запроса на части или можно передавать значение всего одного параметра. В этом случае в массиве $_GET появится пустой элемент с ключом, равным этому значению (всей строке запроса), причем символ «+», встретившийся в строке запроса, будет заменен на подчеркивание «_».
Методом POST данные передаются только с помощью форм, и пользователь (клиент) не видит, какие именно данные отправляются серверу. Чтобы их увидеть, хакер должен подменить нашу форму своей. Тогда сервер отправит результаты обработки неправильной формы не туда, куда нужно. Чтобы этого избежать, можно проверять адрес страницы, с которой были посланы данные. Это можно сделать опять же с помощью функции getenv():
getenv("HTTP_REFERER");
Теперь самое время решить задачу, сформулированную в начале лекции.
Основы клиент-серверных технологий
В самом начале курса мы уже говорили о том, что PHP – это скриптовый язык, обрабатываемый сервером. Сейчас мы хотим уточнить, что же такое сервер, какие функции он выполняет и какие вообще бывают серверы. Если речь идет о сервере, невольно всплывает в памяти понятие клиента. Все потому, что эти два понятия неразрывно связаны. Объединяет их компьютерная архитектура клиент-сервер. Обычно, когда говорят «сервер», имеют в виду сервер в архитектуре клиент-сервер, а когда говорят «клиент» – имеют в виду клиент в этой же архитектуре. Так что же это за архитектура? Суть ее в том, чтобы разделить функции между двумя подсистемами: клиентом, который отправляет запрос на выполнение каких-либо действий, и сервером, который выполняет этот запрос. Взаимодействие между клиентом и сервером происходит посредством стандартных специальных протоколов, таких как TCP/IP и z39.50. На самом деле протоколов очень много, они различаются по уровням. Мы рассмотрим только протокол прикладного уровня HTTP (чуть позднее), поскольку для решения наших программистских задач нужен только он. А пока вернемся к клиент-серверной архитектуре и разберемся, что же такое клиент и что такое сервер.
Сервер представляет собой набор программ, которые контролируют выполнение различных процессов. Соответственно, этот набор программ установлен на каком-то компьютере. Часто компьютер, на котором установлен сервер, и называют сервером. Основная функция компьютера-сервера – по запросу клиента запустить какой-либо определенный процесс и отправить клиенту результаты его работы.
Клиентом называют любой процесс, который пользуется услугами сервера. Клиентом может быть как пользователь, так и программа. Основная задача клиента – выполнение приложения и осуществление связи с сервером, когда этого требует приложение. То есть клиент должен предоставлять пользователю интерфейс для работы с приложением, реализовывать логику его работы и при необходимости отправлять задания серверу.
Взаимодействие между клиентом и сервером начинается по инициативе клиента. Клиент запрашивает вид обслуживания, устанавливает сеанс, получает нужные ему результаты и сообщает об окончании работы.
Услугами одного сервера чаще всего пользуется несколько клиентов одновременно. Поэтому каждый сервер должен иметь достаточно большую производительность и обеспечивать безопасность данных.
Логичнее всего устанавливать сервер на компьютере, входящем в какую-либо сеть, локальную или глобальную. Однако можно устанавливать сервер и на отдельно стоящий компьютер (тогда он будет являться одновременно и клиентом и сервером).
Существует множество типов серверов. Вот лишь некоторые из них.
Видеосервер
Такой сервер специально приспособлен к обработке изображений, хранению видеоматериалов, видеоигр и т.п. В связи с этим компьютер, на котором установлен видеосервер, должен иметь высокую производительность и большую память.
Поисковый сервер предназначен для поиска информации в Internet.Почтовый сервер предоставляет услуги в ответ на запросы, присланные по электронной почте.Сервер WWW предназначен для работы в Internet.Сервер баз данных выполняет обработку запросов к базам данных.Сервер защиты данных предназначен для обеспечения безопасности данных (содержит, например, средства для идентификации паролей).Сервер приложений предназначен для выполнения прикладных процессов. С одной стороны взаимодействует с клиентами, получая задания, а с другой – работает с базами данных, подбирая необходимые для обработки данные.Сервер удаленного доступа обеспечивает коллективный удаленный доступ к данным.Файловый сервер обеспечивает функционирование распределенных ресурсов, предоставляет услуги поиска, хранения, архивирования данных и возможность одновременного доступа к ним нескольких пользователей.
Обычно на компьютере-сервере работает сразу несколько программ-серверов. Одна занимается электронной почтой, другая распределением файлов, третья предоставляет web-страницы.
Из всех типов серверов нас в основном интересует сервер WWW. Часто его называют web-сервером, http-сервером или даже просто сервером. Что представляет собой web-сервер? Во-первых, это хранилище информационных ресурсов. Во-вторых, эти ресурсы хранятся и предоставляются пользователям в соответствии со стандартами Internet (такими, как протокол передачи данных HTTP). Как предоставляются данные в соответствии с этим протоколом, мы рассмотрим чуть позже. Работа с документами web-сервера осуществляется при помощи браузера (например, IE, Opera или Mozilla), который отсылает серверу запросы, созданные в соответствии с протоколом HTTP. В процессе выполнения задания сервер может связываться с другими серверами.
Далее в ходе лекции, говоря «сервер», мы будем подразумевать web-сервер.
В качестве примеров web-серверов можно привести сервер Apache группы Apache, Internet Information Server (IIS) компании Microsoft, SunOne фирмы Sun Microsystems,WebLogic фирмы BEA Systems, IAS (Inprise Application Server) фирмы Borland, WebSphere фирмы IBM, OAS (Oracle Application Server).
На рис. 4.1 и в таблице 4.1 приведена статистика использования различных серверов среди всех доменов Internet от NetCraft http://news.netcraft.com/.
Рис. 4.1. Статистика использования ведущих web-серверов
Apache | 31703884 | 67.21 | 32280582 | 67.20 | -0.01 |
Microsoft | 9849971 | 20.88 | 10099760 | 21.02 | 0.14 |
SunONE | 1657295 | 3.51 | 1651575 | 3.44 | -0.07 |
Zeus | 755227 | 1.60 | 762716 | 1.59 | -0.01 |
данные формы будет обрабатывать файл
<h2>Форма для регистрации участников</h2> <form action="1.php" method=POST> <!--создаем форму--> <!-- данные формы будет обрабатывать файл 1.php, при отправке запроса будет использован метод POST--> Имя <br><input type=text name="first_name" value="Введите Ваше имя"><br> Фамилия <br><input type=text name="last_name"><br> E-mail <br><input type=text name="email"><br> <p> Выберите курс, который вы бы хотели посещать:<br> <input type=radio name="kurs" value="PHP">PHP<br> <input type=radio name="kurs" value="Lisp">Lisp<br> <input type=radio name="kurs" value="Perl">Perl<br> <input type=radio name="kurs" value="Unix">Unix<br> <P>Что вы хотите, чтобы мы знали о вас? <BR> <textarea name="comment" cols=32 rows=5></textarea> <P><input name="confirm" type=checkbox checked>Подтвердить получение <br> <input type=submit value="Отправить"> <input type=reset value="Отменить"> </form> |
Листинг 4.0. form.html |
Закрыть окно |
http://phpbook.info/test.php?id=10&user=pit |
Пример 4.1. Передача данных в URL |
Закрыть окно |
<?php $str = "Здравствуйте, ".$_REQUEST["first_name"]. " ".$_REQUEST["last_name"]."! <br>"; $str .=" Вы выбрали для изучения курс по ".$_REQUEST["kurs"]; echo $str; ?> |
Пример 4.2. Файл 1.php, обрабатывающий форму form.html |
Закрыть окно |
<? getenv("REQUEST_METHOD"); // возвратит использованный метод echo getenv ("REMOTE_ADDR"); // выведет IP-адрес пользователя, // пославшего запрос ?> |
Пример 4.3. Использование функции getenv() |
Закрыть окно |
<h2>Форма для регистрации студентов</h2> <form action="1.php" method=POST> Имя <br><input type=text name="first_name" value="Введите Ваше имя"><br> Фамилия <br><input type=text name="last_name"><br> E-mail <br><input type=text name="email"><br> <p> Выберите курс, который вы бы хотели посещать:<br> <input type=checkbox name='kurs[]' value='PHP'>PHP<br> <input type=checkbox name='kurs[]' value='Lisp'>Lisp<br> <input type=checkbox name='kurs[]' value='Perl'>Perl<br> <input type=checkbox name='kurs[]' value='Unix'>Unix<br> <P>Что вы хотите, чтобы мы знали о вас? <BR> <textarea name="comment" cols=32 rows=5></textarea> <input type=submit value="Отправить"> <input type=reset value="Отменить"> </form> |
Листинг 4.4. form_final.html |
Закрыть окно |
<? // создадим массивы соответствий курс-время его // проведения и курс-его лектор $times = array("PHP"=>"14.30","Lisp"=>"12.00", "Perl"=>"15.00","Unix"=>"14.00"); $lectors = array("PHP"=>"Василий Васильевич", "Lisp"=>"Иван Иванович", "Perl"=>"Петр Петрович", "Unix"=>"Семен Семенович"); define("SIGN","С уважением, администрация"); // определяем подпись письма как константу define("MEETING_TIME","18.00"); // задаем время собрания студентов $date = "12 мая"; // задаем дату проведения лекций //начинаем составлять текст сообщения $str = "Здравствуйте, уважаемый " . $_POST["first_name"] . " " . $_POST["last_name"]."!<br>"; $str .= "<br>Сообщаем Вам, что "; $kurses = $_POST["kurs"]; // сохраним в этой переменной // список выбранных курсов if (!isset($kurses)) { // если не выбран ни один курс $event = "следующее собрание студентов"; $str .= "$event состоится $date ". MEETING_TIME . "<br>"; } else { // если хотя бы один курс выбран $event = "выбранные Вами лекции состоятся $date <ul>"; //функция count вычисляет число элементов в массиве for ($i=0;$i<count($kurses);$i++){ // для каждого выбранного курса $k = $kurses[$i]; // запоминаем название курса $lect = $lect . "<li>лекция по $k в $times[$k]"; // составляем сообщение $lect .= " (Ваш лектор, $lectors[$k])"; } $event = $event . $lect . "</ul>"; $str .= "$event"; } $str .= "<br>". SIGN; // добавляем подпись echo $str; // выводим сообщение на экран ?> |
Листинг 4.5. Скрипт 1.php, обрабатывающий форму form_final.html |
Закрыть окно |
Пример обработки запроса с помощью PHP
Напомним, в чем состояла задача, и уточним ее формулировку. Нужно написать форму для регистрации участников заочной школы программирования и после регистрации отправить участнику сообщение. Мы назвали это сообщение универсальным письмом, но оно будет немного отличаться от того письма, которое мы составили на предыдущей лекции. Здесь мы также не будем отправлять что-либо по электронной почте, дабы не уподобляться спамерам, а просто сгенерируем это сообщение и выведем его на экран браузера. Начальный вариант формы регистрации мы уже приводили выше. Изменим его таким образом, чтобы каждый регистрирующийся мог выбрать сколько угодно курсов для посещения, и не будем подтверждать получение регистрационной формы.
Листинг 4.4. form_final.html (html, txt)
Здесь все достаточно просто и понятно. Единственное, что можно отметить, – это способ передачи значений элемента checkbox. Когда мы пишем в имени элемента kurs[], это значит, что первый отмеченный элемент checkbox будет записан в первый элемент массива kurs, второй отмеченный checkbox – во второй элемент массива и т.д. Можно, конечно, просто дать разные имена элементам checkbox, но это усложнит обработку данных, если курсов будет много.
Скрипт, который все это будет разбирать и обрабатывать, называется 1.php (форма ссылается именно на этот файл, что записано в ее атрибуте action). По умолчанию используется для передачи метод GET, но мы указали POST. По полученным сведениям от зарегистрировавшегося человека, скрипт генерирует соответствующее сообщение. Если человек выбрал какие-то курсы, то ему выводится сообщение о времени их проведения и о лекторах, которые их читают. Если человек ничего не выбрал, то выводится сообщение о следующем собрании заочной школы программистов (ЗШП).
Листинг 4.5. Скрипт 1.php, обрабатывающий форму form_final.html (html, txt)
<? // создадим массивы соответствий курс-время его // проведения и курс-его лектор $times = array("PHP"=>"14.30","Lisp"=>"12.00", "Perl"=>"15.00","Unix"=>"14.00"); $lectors = array("PHP"=>"Василий Васильевич", "Lisp"=>"Иван Иванович", "Perl"=>"Петр Петрович", "Unix"=>"Семен Семенович"); define("SIGN","С уважением, администрация"); // определяем подпись письма как константу define("MEETING_TIME","18.00"); // задаем время собрания студентов $date = "12 мая"; // задаем дату проведения лекций //начинаем составлять текст сообщения $str = "Здравствуйте, уважаемый " . $_POST["first_name"] . " " . $_POST["last_name"]."!<br>"; $str .= "<br>Сообщаем Вам, что "; $kurses = $_POST["kurs"]; // сохраним в этой переменной // список выбранных курсов if (!isset($kurses)) { // если не выбран ни один курс $event = "следующее собрание студентов"; $str .= "$event состоится $date ". MEETING_TIME . "<br>"; } else { // если хотя бы один курс выбран $event = "выбранные Вами лекции состоятся $date <ul>"; //функция count вычисляет число элементов в массиве for ($i=0;$i<count($kurses);$i++){ // для каждого выбранного курса $k = $kurses[$i]; // запоминаем название курса $lect = $lect . "<li>лекция по $k в $times[$k]"; // составляем сообщение $lect .= " (Ваш лектор, $lectors[$k])"; } $event = $event . $lect . "</ul>"; $str .= "$event"; } $str .= "<br>". SIGN; // добавляем подпись echo $str; // выводим сообщение на экран ?>
Листинг 4.5. Скрипт 1.php, обрабатывающий форму form_final.html
Протокол HTTP и способы передачи данных на сервер
Internet построен по многоуровневому принципу, от физического уровня, связанного с физическими аспектами передачи двоичной информации, и до прикладного уровня, обеспечивающего интерфейс между пользователем и сетью.
HTTP (HyperText Transfer Protocol, протокол передачи гипертекста) – это протокол прикладного уровня, разработанный для обмена гипертекстовой информацией в Internet.
HTTP предоставляет набор методов для указания целей запроса, отправляемого серверу. Эти методы основаны на дисциплине ссылок, где для указания ресурса, к которому должен быть применен данный метод, используется универсальный идентификатор ресурсов (Universal Resource Identifier) в виде местонахождения ресурса (Universal Resource Locator, URL) или в виде его универсального имени (Universal Resource Name, URN).
Сообщения по сети при использовании протокола HTTP передаются в формате, схожем с форматом почтового сообщения Internet (RFC-822) или с форматом сообщений MIME (Multipurpose Internet Mail Exchange).
HTTP используется для коммуникаций между различными пользовательскими программами и программами-шлюзами, предоставляющими доступ к существующим Internet-протоколам, таким как SMTP (протокол электронной почты), NNTP (протокол передачи новостей), FTP (протокол передачи файлов), Gopher и WAIS. HTTP разработан для того, чтобы позволять таким шлюзам через промежуточные программы-серверы (proxy) передавать данные без потерь.
Протокол реализует принцип запрос/ответ. Запрашивающая программа – клиент инициирует взаимодействие с отвечающей программой – сервером и посылает запрос, содержащий:
метод доступа;адрес URI;версию протокола;сообщение (похожее по форме на MIME) с информацией о типе передаваемых данных, информацией о клиенте, пославшем запрос, и, возможно, с содержательной частью (телом) сообщения.
Ответ сервера содержит:
строку состояния, в которую входит версия протокола и код возврата (успех или ошибка);сообщение (в форме, похожей на MIME), в которое входит информация сервера, метаинформация (т.е. информация о содержании сообщения) и тело сообщения.
В протоколе не указывается, кто должен открывать и закрывать соединение между клиентом и сервером. На практике соединение, как правило, открывает клиент, а сервер после отправки ответа инициирует его разрыв.
Давайте рассмотрим более подробно, в какой форме отправляются запросы на сервер.
Подведем итоги. Мы научились отличать
Подведем итоги. Мы научились отличать клиента от сервера и компьютер-сервер от программы-сервера, познакомились с основными методами, используемыми для передачи данных на сервер, изучили средства, предлагаемые языком PHP для обработки клиентских запросов, и испробовали их на практике. В принципе этого достаточно для того, чтобы создавать клиент-серверные приложения.
Аргументы функций
У каждой функции может быть, как мы уже говорили, список аргументов. С помощью этих аргументов в функцию передается различная информация (например, значение числа, факториал которого надо подсчитать). Каждый аргумент представляет собой переменную или константу.
С помощью аргументов данные в функцию можно передавать тремя различными способами. Это передача аргументов по значению (используется по умолчанию), по ссылке и задание значения аргументов по умолчанию. Рассмотрим эти способы подробнее.
Когда аргумент передается в функцию по значению, изменение значения аргумента внутри функции не влияет на его значение вне функции. Чтобы позволить функции изменять ее аргументы, их нужно передавать по ссылке. Для этого в определении функции перед именем аргумента следует написать знак амперсанд «&».
<?php // напишем функцию, которая бы добавляла // к строке слово checked function add_label(&$data_str){ $data_str .= "checked"; } $str = "<input type=radio name=article "; // пусть имеется такая строка echo $str ."><br>"; // выведет элемент формы – // не отмеченную радио кнопку add_label($str); // вызовем функцию echo $str ."><br>"; // это выведет уже отмеченную // радио кнопку ?>
Пример 5.3. Передача аргументов по ссылке (html, txt)
В функции можно определять значения аргументов, используемые по умолчанию. Само значение по умолчанию должно быть константным выражением, а не переменной и не представителем класса или вызовом другой функции.
У нас есть функция, создающая информационное сообщение, подпись к которому меняется в зависимости от значения переданного ей параметра. Если значение параметра не задано, то используется подпись "Оргкомитет".
<?php function Message($sign="Оргкомитет."){ // здесь параметр sign имеет по умолчанию значение "Оргкомитет" echo "Следующее собрание состоится завтра.<br>"; echo "$sign<br>"; } Message(); // вызываем функцию без параметра. // В этом случае подпись – это Оргкомитет Message("С уважением, Вася"); // В этом случае подпись // будет "С уважением, Вася." ?>
Пример 5.4. Значения аргументов по умолчанию (html, txt)
Результатом работы этого скрипта будет:
Следующее собрание состоится завтра. Оргкомитет. Следующее собрание состоится завтра. С уважением, Вася.
Если у функции несколько параметров, то те аргументы, для которых задаются значения по умолчанию, должны быть записаны после всех остальных аргументов в определении функции. В противном случае появится ошибка, если эти аргументы будут опущены при вызове функции.
Например, мы хотим внести описание статьи в каталог. Пользователь должен ввести такие характеристики статьи, как ее название, автор и краткое описание. Если пользователь не вводит имя автора статьи, считаем, что это Иванов Иван.
<?php function Add_article($title, $description, $author="Иванов Иван"){ echo "Заносим в каталог статью: $title,"; echo "автор $author"; echo "<br>Краткое описание: "; echo "$description <hr>"; } Add_article("Информатика и мы", "Это статья про информатику ...", "Петров Петр"); Add_article("Кто такие хакеры", "Это статья про хакеров ..."); ?>
В результате работы скрипта получим следующее
Заносим в каталог статью: Информатика и мы, автор Петров Петр. Краткое описание: Это статья про информатику...
Заносим в каталог статью: Кто такие хакеры, автор Иванов Иван. Краткое описание: Это статья про хакеров...
Если же мы напишем вот так:
<?php function Add_article($author="Иванов Иван", $title, $description){ // ...действия как в предыдущем примере } Add_article("Кто такие хакеры", "Это статья про хакеров..."); ?>
То в результате получим:
Warning: Missing argument 3 for add_article() in c:\users\nina\tasks\func\def_bad.php on line 2
Функции, определяемые пользователем
Для чего нужны функции? Чтобы ответить на этот вопрос, нужно понять, что вообще представляют собой функции. В программировании, как и в математике, функция есть отображение множества ее аргументов на множество ее значений. То есть функция для каждого набора значений аргумента возвращает какие-то значения, являющиеся результатом ее работы. Зачем нужны функции, попытаемся объяснить на примере. Классический пример функции в программировании – это функция, вычисляющая значение факториала числа. То есть мы задаем ей число, а она возвращает нам его факториал. При этом не нужно для каждого числа, факториал которого мы хотим получить, повторять один и тот же код – достаточно просто вызвать функцию с аргументом, равным этому числу.
Функция вычисления факториала натурального числа
<?php function fact($n){ if ($n==0) return 1; else return $fact = $n * fact($n-1); } echo fact(3); // можно было бы написать echo (3*2); // но если число большое, echo fact(50); // то удобнее пользоваться функцией, // чем писать echo (50*49*48*...*3*2); ?>
Таким образом, когда мы осуществляем действия, в которых прослеживается зависимость от каких-то данных, и при этом, возможно, нам понадобится выполнять такие же действия, но с другими исходными данными, удобно использовать механизм функций – оформить блок действий в виде тела функции, а меняющиеся данные – в качестве ее параметров.
Посмотрим, как в общем виде выглядит задание (объявление) функции. Функция может быть определена с помощью следующего синтаксиса:
function Имя_функции (параметр1, параметр2, ... параметрN){ Блок_действий return "значение возвращаемое функцией"; }
Если прямо так написать в php-программе, то работать ничего не будет. Во-первых, Имя_функции и имена параметров функции (параметр1, параметр2 и т.д.) должны соответствовать правилам наименования в PHP (и русских символов в них лучше не использовать). Имена функций нечувствительны к регистру. Во-вторых, параметры функции – это переменные языка, поэтому перед названием каждого из них должен стоять знак $. Никаких троеточий ставить в списке параметров нельзя. В-третьих, вместо слов блок_действий в теле функции должен находиться любой правильный PHP-код (не обязательно зависящий от параметров). И наконец, после ключевого слова return должно идти корректное php-выражение (что-либо, что имеет значение). Кроме того, у функции может и не быть параметров, как и возвращаемого значения. Пример правильного объявления функции – функция вычисления факториала, приведенная выше.
Как происходит вызов функции? Указывается имя функции и в круглых скобках список значений ее параметров, если таковые имеются:
<?php Имя_функции ("значение_для_параметра1", "значение_для_параметра2",...); // пример вызова функции – вызов функции // вычисления факториала приведен выше, // там для вычисления факториала числа 3 // мы писали: fact(3); // где fact – имя вызываемой функции, // а 3 – значение ее параметра с именем $n ?>
Когда можно вызывать функцию? Казалось бы, странный вопрос. Функцию можно вызвать после ее определения, т.е. в любой строке программы ниже блока function f_name(){...}. В PHP3 это было действительно так. Но уже в PHP4 такого требования нет. Все дело в том, как интерпретатор обрабатывает получаемый код. Единственное исключение составляют функции, определяемые условно (внутри условных операторов или других функций). Когда функция определяется таким образом, ее определение должно предшествовать ее вызову.
<? $make = true; /* здесь нельзя вызвать Make_event(); потому что она еще не существует, но можно вызвать Save_info() */
Save_info("Вася","Иванов", "Я выбрал курс по PHP");
if ($make){ // определение функции Make_event() function Make_event(){ echo "<p>Хочу изучать Python<br>"; } } // теперь можно вызывать Make_event() Make_event(); // определение функции Save_info function Save_info($first, $last, $message){ echo "<br>$message<br>"; echo "Имя: ". $first . " ". $last . "<br>"; } Save_info("Федя","Федоров", "А я выбрал Lisp"); // Save_info можно вызывать и здесь ?>
Пример 5.1. Определение функции внутри условного оператора (html, txt)
Если функция однажды определена в программе, то переопределить или удалить ее позже нельзя. Несмотря на то, что имена функций нечувствительны к регистру, лучше вызывать функцию по тому же имени, каким она была задана в определении.
<?php /* нельзя сохранить данные, т.е. вызвать функцию DataSave() до того, как выполнена проверка их правильности, т.е. вызвана функция DataCheck() */
DataCheck(); DataSave();
function DataCheck(){ // проверка правильности данных function DataSave(){ // сохраняем данные } } ?>
Пример 5.2. Определение функции внутри функции (html, txt)
Рассмотрим подробнее аргументы функций, их назначение и использование.
Глобальные переменные
Чтобы использовать внутри функции переменные, заданные вне ее, эти переменные нужно объявить как глобальные. Для этого в теле функции следует перечислить их имена после ключевого слова global:
global $var1, $var2;
<? $a=1; function Test_g(){ global $a; $a = $a*2; echo 'в результате работы функции $a=',$a; } echo 'вне функции $a=',$a,', '; Test_g(); echo "<br>"; echo 'вне функции $a=',$a,', '; Test_g(); ?>
Пример 5.7. Глобальные переменные (html, txt)
В результате работы этого скрипта получим:
вне функции $a=1, в результате работы функции $a=2 вне функции $a=2, в результате работы функции $a=4
Когда переменная объявляется как глобальная, фактически создается ссылка на глобальную переменную. Поэтому такая запись эквивалентна следующей (массив GLOBALS содержит все переменные, глобальные относительно текущей области видимости):
$var1 = & $GLOBALS["var1"]; $var2 = & $GLOBALS["var2"];
Это значит, например, что удаление переменной $var1 не удаляет глобальной переменной $_GLOBALS["var1"].
Переменные функции
PHP поддерживает концепцию переменных функций. Это значит, что если имя переменной заканчивается круглыми скобками, то PHP ищет функцию с таким же именем и пытается ее выполнить.
<? /* создадим две простые функции: Add_sign – добавляет подпись к строке и Show_text – выводит строку текста */
function Add_sign($string, $sign="С уважением, Петр"){ echo $string ." ".$sign; } function Show_text(){ echo "Отправить сообщение по почте<br>"; } $func = "Show_text"; // создаем переменную со значением, // равным имени функции Show_text $func(); // это вызовет функцию Show_text $func = "Add_sign"; // создаем переменную со значением, // равным имени функции Add_sign $func("Привет всем <br>"); // это вызовет функцию // Add_sign с параметром "Привет всем" ?>
Пример 5.11. Использование переменных функций (html, txt)
В этом примере функция Show_text просто выводит строку текста. Казалось бы, зачем для этого создавать отдельную функцию, если существует специальная функция echo(). Дело в том, что такие функции, как echo(), print(), unset(), include() и т.п. нельзя использовать в качестве переменных функций. То есть если мы напишем:
<? $func = "echo "; $func("TEXT"); ?>
то интерпретатор выведет ошибку:
Fatal error: Call to undefined function: echo() in c:\users\nina\tasks\func\var_f.php on line 2
Поэтому для того, чтобы использовать любую из перечисленных выше функций как переменную функцию, нужно создать собственную функцию, что мы и сделали в предыдущем примере.
потому что она еще не
<? $make = true; /* здесь нельзя вызвать Make_event(); потому что она еще не существует, но можно вызвать Save_info() */ Save_info("Вася","Иванов", "Я выбрал курс по PHP"); if ($make){ // определение функции Make_event() function Make_event(){ echo "<p>Хочу изучать Python<br>"; } } // теперь можно вызывать Make_event() Make_event(); // определение функции Save_info function Save_info($first, $last, $message){ echo "<br>$message<br>"; echo "Имя: ". $first . " ". $last . "<br>"; } Save_info("Федя","Федоров", "А я выбрал Lisp"); // Save_info можно вызывать и здесь ?> |
Пример 5.1. Определение функции внутри условного оператора |
Закрыть окно |
<?php /* нельзя сохранить данные, т.е. вызвать функцию DataSave() до того, как выполнена проверка их правильности, т.е. вызвана функция DataCheck() */ DataCheck(); DataSave(); function DataCheck(){ // проверка правильности данных function DataSave(){ // сохраняем данные } } ?> |
Пример 5.2. Определение функции внутри функции |
Закрыть окно |
<?php // напишем функцию, которая бы добавляла // к строке слово checked function add_label(&$data_str){ $data_str .= "checked"; } $str = "<input type=radio name=article "; // пусть имеется такая строка echo $str ."><br>"; // выведет элемент формы – // не отмеченную радио кнопку add_label($str); // вызовем функцию echo $str ."><br>"; // это выведет уже отмеченную // радио кнопку ?> |
Пример 5.3. Передача аргументов по ссылке |
Закрыть окно |
<?php function Message($sign="Оргкомитет."){ // здесь параметр sign имеет по умолчанию значение "Оргкомитет" echo "Следующее собрание состоится завтра.<br>"; echo "$sign<br>"; } Message(); // вызываем функцию без параметра. // В этом случае подпись – это Оргкомитет Message("С уважением, Вася"); // В этом случае подпись // будет "С уважением, Вася." ?> |
Пример 5.4. Значения аргументов по умолчанию |
Закрыть окно |
<?php function DataCheck(){ $n = func_num_args(); echo "Число аргументов функции $n"; } DataCheck(); // выведет строку // " Число аргументов функции 0" DataCheck(1,2,3); // выведет строку // "Число аргументов функции 3" ?> |
Пример 5.5. Использование функции func_num_args() |
Закрыть окно |
<? function DataCheck(){ $check =true; $n = func_num_args(); // число аргументов, // переданных в функцию /* проверяем, является ли первый переданный аргумент целым числом */ if ($n>=1) if (!is_int(func_get_arg(0))) $check = false; /* проверяем, является ли второй переданный аргумент строкой */ if ($n>=2) if (!is_string(func_get_arg(1))) $check = false; return $check; } if (DataCheck(a123,"text")) echo "Проверка прошла успешно<br>"; else echo "Данные не удовлетворяют условиям<br>"; if (DataCheck(324)) echo "Проверка прошла успешно<br>"; else echo "Данные не удовлетворяют условиям<br>"; ?> |
Пример 5.6. Функция для проверки типа данных, ее аргументов |
Закрыть окно |
<? $a=1; function Test_g(){ global $a; $a = $a*2; echo ' в результате работы функции $a=',$a; } echo 'вне функции $a=',$a,', '; Test_g(); echo "<br>"; echo 'вне функции $a=',$a,', '; Test_g(); ?> |
Пример 5.7. Глобальные переменные |
Закрыть окно |
<? function Test_s(){ static $a = 1; // нельзя присваивать выражение или ссылку $a = $a*2; echo $a; } Test_s(); // выведет 2 echo $a; // ничего не выведет, так как // $a доступна только // внутри функции Test_s(); // внутри функции $a=2, поэтому // результатом работы функции // будет число 4 ?> |
Пример 5.8. Использование статической переменной |
Закрыть окно |
<? $arr = array("first","second"); list($a,$b) = $arr; // переменной $ a присваивается первое // значение массива, $b – второе echo $a," ",$b; // выведет строку «first second» ?> |
Пример 5.9. Использование list() |
Закрыть окно |
<? $a = 3; $b = 2; function & ref($par){ global $a, $b; if ($par % 2 == 0) return $b; else return $a; } $var =& ref(4); echo $var, " и ", $b, "<br>"; //выведет 2 и 2 $b = 10; echo $var, " и ", $b, "<br>"; // выведет 10 и 10 ?> |
Пример 5.10. Возвращение ссылки |
Закрыть окно |
<? /* создадим две простые функции: Add_sign – добавляет подпись к строке и Show_text – выводит строку текста */ function Add_sign($string, $sign="С уважением, Петр"){ echo $string ." ".$sign; } function Show_text(){ echo "Отправить сообщение по почте<br>"; } $func = "Show_text"; // создаем переменную со значением, // равным имени функции Show_text $func(); // это вызовет функцию Show_text $func = "Add_sign"; // создаем переменную со значением, // равным имени функции Add_sign $func("Привет всем <br>"); // это вызовет функцию // Add_sign с параметром "Привет всем" ?> |
Пример 5.11. Использование переменных функций |
Закрыть окно |
<form action="ask_names.php"> Создать элемент "строка ввода текста": <input type=checkbox name=types[] value=string><br> Количество элементов: <input type=text name=numbers[string] size=3><br> <br> Создать элемент "текстовая область": <input type=checkbox name=types[] value=text><br> Количество элементов: <input type=text name=numbers[text] size=3><br> <input type=submit value="Создать"> </form> |
Листинг 5.12. task_form.html |
Закрыть окно |
<? $file = "task.php"; /* файл, который будет обрабатывать сгенерированную этим скриптом форму */ function Ask_names(){ // функция генерирует форму для // ввода названий элементов ввода global $file; //объявляем, что хотим использовать эту // переменную, заданную вне функции if (isset($_GET["types"])){ $st = '<form action="'.$file.'">'; foreach ($_GET["types"] as $k => $type){ /* перебираем все типы элементов, которые нужно создать */ $num = $_GET["numbers"][$type]; // сколько элементов каждого типа нужно for ($i=1;$i<=$num;$i++){ // создаем $num строк для ввода $st.= "Введите имя $i-го элемента типа $type: "; $st.= "<input type=text name=names[$type][]><br>"; } // сохраняем тип и число необходимых // элементов ввода этого типа $st.= "<input type=hidden name=types[] value=$type>"; $st.= "<input type=hidden name=numbers[] value=$num><br>"; } $st .= "<input type=submit name=send value=send></form>"; return $st; // в переменной $st содержится код формы // для запроса имен } else echo "Select type"; } echo Ask_names(); // вызываем функцию и выводим // результаты ее работы ?> |
Листинг 5.13. ask_names.php |
Закрыть окно |
<? $show_file = "task_show.php"; /* файл, который будет обрабатывать данные созданной этим файлом формы */ function Create_element($type,$name){ // функция создает элемент ввода // по типу и названию switch($type){ case"string": $str .= "$name: <input type=text name=string[]><br>"; break; case "text": $str .= "$name: <textarea name=text[]></textarea><br>"; break; } return $str; } function Create_form(){ // функция создает форму // с нужными элементами global $show_file; $str = '<form action="'.$show_file.'">'; foreach ($_GET["types"] as $k => $type){ // перебираем типы элементов $num = $_GET["numbers"][$k]; // число элементов этого типа for ($i=1;$i<=$num;$i++){ $arr = $_GET["names"][$type][$i-1]; // имя создаваемого элемента $str .= Create_element($type,$arr); // вызываем функцию для // создания элемента } } $str .= "<input type=submit value=send></form>"; echo $str; } $crt = "Create_form"; $crt(); // вызываем функцию создания // формы Create_form ?> |
Листинг 5.14. task.php |
Закрыть окно |
в чем состоит задача. Мы
Напомним, в чем состоит задача. Мы хотим написать интерфейс, который позволял бы создавать html-формы. Пользователь выбирает, какие элементы и в каком количестве нужно создать, придумывает им названия, а наша программа сама генерирует требуемую форму.
Разобьем задачу на несколько подзадач: выбор типов элементов ввода и их количества, создание названий элементов ввода и обработка полученных данных, т.е. непосредственно генерация формы. Первая задача достаточно проста: нужно написать соответствующую форму, например подобную приведенной ниже (task_form.html):
<form action="ask_names.php"> Создать элемент "строка ввода текста": <input type=checkbox name=types[] value=string><br> Количество элементов: <input type=text name=numbers[string] size=3><br> <br> Создать элемент "текстовая область": <input type=checkbox name=types[] value=text><br> Количество элементов: <input type=text name=numbers[text] size=3><br> <input type=submit value="Создать"> </form>
Листинг 5.12. task_form.html (html, txt)
Когда мы пишем в имени элемента формы, например types[], это значит, что его имя – следующий элемент массива types. То есть у нас первый элемент формы ("строка ввода текста") будет иметь имя types[0], а второй (текстовая область) – types[1]. В браузере task_form.html будет выглядеть примерно так:
Рис. 5.1. Форма для выбора создаваемых элементов и их количества
После отправки данных этой формы мы получим информацию о том, какие элементы и сколько элементов каждого типа нужно создать. Следующий скрипт запрашивает названия для этих элементов:
Листинг 5.13. ask_names.php (html, txt)
Допустим, нужно создать два элемента типа «текстовая строка» и один элемент типа «текстовая область», как и отмечено в форме выше. Тогда скрипт ask_names.php обработает ее таким образом, что мы получим такую форму:
Рис. 5.2. Форма для ввода названий создаваемых элементов
Введем в эту форму, например, строки «Название», «Автор» и «Краткое содержание». Эти данные будет обрабатывать скрипт task.php.
Листинг 5.14. task.php (html, txt)
Результатом работы этого скрипта с входными данными, приведенными выше, будет следующая форма:
Рис. 5.3. Пример формы, сгенерированной нашей программой
$num = $_GET["numbers"][$type]; // сколько элементов каждого типа нужно for ($i=1;$i<=$num;$i++){ // создаем $num строк для ввода $st.= "Введите имя $i-го элемента типа $type: "; $st.= "<input type=text name=names[$type][]><br>"; } // сохраняем тип и число необходимых // элементов ввода этого типа $st.= "<input type=hidden name=types[] value=$type>"; $st.= "<input type=hidden name=numbers[] value=$num><br>"; } $st .= "<input type=submit name=send value=send></form>"; return $st; // в переменной $st содержится код формы // для запроса имен } else echo "Select type"; } echo Ask_names(); // вызываем функцию и выводим // результаты ее работы ?>
Листинг 5.13. ask_names.php
Допустим, нужно создать два элемента типа «текстовая строка» и один элемент типа «текстовая область», как и отмечено в форме выше. Тогда скрипт ask_names.php обработает ее таким образом, что мы получим такую форму:
Рис. 5.2. Форма для ввода названий создаваемых элементов
Введем в эту форму, например, строки «Название», «Автор» и «Краткое содержание». Эти данные будет обрабатывать скрипт task.php.
<? $show_file = "task_show.php"; /* файл, который будет обрабатывать данные созданной этим файлом формы */
function Create_element($type,$name){ // функция создает элемент ввода // по типу и названию switch($type){ case"string": $str .= "$name: <input type=text name=string[]><br>"; break; case "text": $str .= "$name: <textarea name=text[]></textarea><br>"; break; } return $str; } function Create_form(){ // функция создает форму // с нужными элементами global $show_file; $str = '<form action="'.$show_file.'">'; foreach ($_GET["types"] as $k => $type){ // перебираем типы элементов $num = $_GET["numbers"][$k]; // число элементов этого типа
for ($i=1;$i<=$num;$i++){ $arr = $_GET["names"][$type][$i-1]; // имя создаваемого элемента
$str .= Create_element($type,$arr); // вызываем функцию для // создания элемента } } $str .= "<input type=submit value=send></form>"; echo $str; } $crt = "Create_form"; $crt(); // вызываем функцию создания // формы Create_form ?>
Листинг 5.14. task.php
Результатом работы этого скрипта с входными данными, приведенными выше, будет следующая форма:
Рис. 5.3. Пример формы, сгенерированной нашей программой
Списки аргументов переменной длины
В PHP4 можно создавать функции с переменным числом аргументов. То есть мы создаем функцию, не зная заранее, со сколькими аргументами ее вызовут. Для написания такой функции никакого специального синтаксиса не требуется. Все делается с помощью встроенных функций func_num_args(), func_get_arg(), func_get_args().
Функция func_num_args() возвращает число аргументов, переданных в текущую функцию. Эта функция может использоваться только внутри определения пользовательской функции. Если она появится вне функции, то интерпретатор выдаст предупреждение.
<?php function DataCheck(){ $n = func_num_args(); echo "Число аргументов функции $n"; } DataCheck(); // выведет строку // "Число аргументов функции 0" DataCheck(1,2,3); // выведет строку // "Число аргументов функции 3" ?>
Пример 5.5. Использование функции func_num_args() (html, txt)
Функция func_get_arg (целое номер_аргумента ) возвращает аргумент из списка переданных в функцию аргументов, порядковый номер которого задан параметром номер_аргумента. Аргументы функции считаются начиная с нуля. Как и func_num_args(), эта функция может использоваться только внутри определения какой-либо функции.
Номер_аргумента не может превышать число аргументов, переданных в функцию. Иначе будет сгенерировано предупреждение, и функция func_get_arg() возвратит False.
Создадим функцию для проверки типа данных ее аргументов. Считаем, что проверка прошла успешно, если первый аргумент функции – целое число, второй – строка.
<? function DataCheck(){ $check =true; $n = func_num_args(); // число аргументов, // переданных в функцию /* проверяем, является ли первый переданный аргумент целым числом */ if ($n>=1) if (!is_int(func_get_arg(0))) $check = false; /* проверяем, является ли второй переданный аргумент строкой */ if ($n>=2) if (!is_string(func_get_arg(1))) $check = false; return $check; }
if (DataCheck(a123,"text")) echo "Проверка прошла успешно<br>"; else echo "Данные не удовлетворяют условиям<br>"; if (DataCheck(324)) echo "Проверка прошла успешно<br>"; else echo "Данные не удовлетворяют условиям<br>"; ?>
Пример 5.6. Функция для проверки типа данных, ее аргументов (html, txt)
Результатом работы будет следующее.
Данные не удовлетворяют условиям Проверка прошла успешно
Функция func_get_args() возвращает массив, состоящий из списка аргументов, переданных функции. Каждый элемент массива соответствует аргументу, переданному функции. Если функция используется вне определения пользовательской функции, то генерируется предупреждение.
Перепишем предыдущий пример, используя эту функцию. Будем проверять, является ли целым числом каждый четный аргумент, передаваемый функции:
<? function DataCheck(){ $check =true; $n = func_num_args(); // число аргументов, // переданных в функцию
$args = func_get_args(); // массив аргументов функции for ($i=0;$i<$n;$i++){ $v = $args[$i]; if ($i % 2 == 0){ if (!is_int($v)) $check = false; // проверяем, // является ли четный аргумент целым } } return $check; } if (DataCheck("text", 324)) echo "Проверка прошла успешно<br>"; else echo "Данные не удовлетворяют условиям<br>"; ?>
Как видим, комбинации функций func_num_args(), func_get_arg() и func_get_args() используется для того, чтобы функции могли иметь переменный список аргументов. Эти функции были добавлены только в PHP 4. В PHP3 для того, чтобы добиться подобного эффекта, можно использовать в качестве аргумента функции массив. Например, вот так можно написать скрипт, проверяющий, является ли каждый нечетный параметр функции целым числом:
<? function DataCheck($params){ $check =true; $n = count($params); // число аргументов, // переданных в функцию
for ($i=0;$i<$n;$i++){ $v = $params[$i]; if ($i % 2 !== 0){ // проверяем, является ли нечетный // аргумент целым if (!is_int($v)) $check = false; } } return $check; } if (DataCheck("text", 324)) echo "Проверка прошла успешно<br>"; else echo "Данные не удовлетворяют условиям<br>"; ?>
Статические переменные
Чтобы использовать переменные только внутри функции, при этом сохраняя их значения и после выхода из функции, нужно объявить эти переменные как статические. Статические переменные видны только внутри функции и не теряют своего значения, если выполнение программы выходит за пределы функции. Объявление таких переменных производится с помощью ключевого слова static:
static $var1, $var2;
Статической переменной может быть присвоено любое значение, но не ссылка.
<? function Test_s(){ static $a = 1; // нельзя присваивать выражение или ссылку $a = $a*2; echo $a; } Test_s(); // выведет 2 echo $a; // ничего не выведет, так как // $a доступна только // внутри функции Test_s(); // внутри функции $a=2, поэтому // результатом работы функции // будет число 4 ?>
Пример 5.8. Использование статической переменной (html, txt)
Task_form
Создать элемент "строка ввода текста":
Количество элементов:
Создать элемент "текстовая область":
Количество элементов:
Внутренние (встроенные) функции
Говоря о функциях, определяемых пользователем, все же нельзя не сказать пару слов о встроенных функциях. С некоторыми из встроенных функций, такими как echo(), print(), date(), include(), мы уже познакомились. На самом деле все перечисленные функции, кроме date(), являются языковыми конструкциями. Они входят в ядро PHP и не требуют никаких дополнительных настроек и модулей. Функция date() тоже входит в состав ядра PHP и не требует настроек. Но есть и функции, для работы с которыми нужно установить различные библиотеки и подключить соответствующий модуль. Например, для использования функций работы с базой данных MySql следует скомпилировать PHP с поддержкой этого расширения. В последнее время наиболее распространенные расширения и соответственно их функции изначально включают в состав PHP так, чтобы с ними можно работать без каких бы то ни было дополнительных настроек интерпретатора.
Возвращаемые значения
Все функции, приведенные выше в качестве примеров, выполняли какие-либо действия. Кроме подобных действий, любая функция может возвращать как результат своей работы какое-нибудь значение. Это делается с помощью утверждения return. Возвращаемое значение может быть любого типа, включая списки и объекты. Когда интерпретатор встречает команду return в теле функции, он немедленно прекращает ее исполнение и переходит на ту строку, из которой была вызвана функция.
Например, составим функцию, которая возвращает возраст человека. Если человек не умер, то возраст считается относительно текущего года.
<?php /* если второй параметр вычисляется как true, то он рассматривается как дата смерти, */
function Age($birth, $is_dead){ if ($is_dead) return $is_dead-$birth; else return date("Y")-$birth; } echo Age(1971, false); // выведет 33 echo Age(1971, 2001); // выведет 30 ?>
В этом примере можно было и не использовать функцию return, а просто заменить ее функцией вывода echo. Однако если мы все же делаем так, что функция возвращает какое-то значение (в данном случае возраст человека), то в программе мы можем присвоить любой переменной значение этой функции:
$my_age = Age(1981, 2004);
В результате работы функции может быть возвращено только одно значение. Несколько значений можно получить, если возвращать список значений (одномерный массив). Допустим, мы хотим получить полный возраст человека с точностью до дня.
<?php function Full_age($b_day, $b_month, $b_year) { $y = date("y"); $m = intval(date("m")); $d = intval(date("d")); $b_month = intval($b_month); $b_day = intval($b_day); $b_year = intval($b_year);
$day = ($b_day > $d ? $d : $d - $b_day); $tmpMonth = ($b_day > $d ? -1 : 0); $month = ($b_month > $m + $tmpMonth ? $m : $m + $tmpMonth - $b_month); $tmpYear = ($b_month > $m + $tmpMonth ? -1 : 0); if ($b_year > $y + $tmpYear) { $year = 0; $month = 0; $day = 0; } else { $year = $y + $tmpYear - $b_year; } return array ($day,$month,$year); } $age = Full_age("29","06","1986"); echo "Вам $age[2] лет, $age[1] месяцев и $age[0] дней"; ?>
Когда функция возвращает несколько значений для их обработки в программе, удобно использовать языковую конструкцию list(), которая позволяет одним действием присвоить значения сразу нескольким переменным. Например, в предыдущем примере, оставив без изменения функцию, обработать возвращаемые ей значения можно было так:
<? // задание функции Full_age() list($day,$month,$year) = Full_age("07", "08","1974"); echo "Вам $year лет, $month месяцев и $day дней"; ?>
Вообще конструкцию list() можно использовать для присвоения переменным значений элементов любого массива.
<? $arr = array("first","second"); list($a,$b) = $arr; // переменной $a присваивается первое // значение массива, $b – второе echo $a," ",$b; // выведет строку «first second» ?>
Пример 5.9. Использование list() (html, txt)
Возвращение ссылки
В результате своей работы функция также может возвращать ссылку на какую-либо переменную. Это может пригодиться, если требуется использовать функцию для того, чтобы определить, какой переменной должна быть присвоена ссылка. Чтобы получить из функции ссылку, нужно при объявлении перед ее именем написать знак амперсанд (&) и каждый раз при вызове функции перед ее именем тоже писать амперсанд (&). Обычно функция возвращает ссылку на какую-либо глобальную переменную (или ее часть – ссылку на элемент глобального массива), ссылку на статическую переменную (или ее часть) или ссылку на один из аргументов, если он был также передан по ссылке.
<? $a = 3; $b = 2; function & ref($par){ global $a, $b; if ($par % 2 == 0) return $b; else return $a; } $var =& ref(4); echo $var, " и ", $b, "<br>"; //выведет 2 и 2 $b = 10; echo $var, " и ", $b, "<br>"; // выведет 10 и 10 ?>
Пример 5.10. Возвращение ссылки (html, txt)
При использовании синтаксиса ссылок в переменную $var нашего примера не копируется значение переменной $b возвращенной функцией $ref, а создается ссылка на эту переменную. То есть теперь переменные $var и $b идентичны и будут изменяться одновременно.
В этой лекции мы изучили
Подведем итоги. В этой лекции мы изучили функции, определяемые пользователем, их синтаксис и семантику, способы передачи их аргументов и возвращаемых значений. Кроме того, обсуждались способы задания и работы с функциями, имеющими переменное число аргументов и альтернативный способ вызова функции (с помощью переменной, значение которой есть имя функции). В следующей лекции будет рассмотрена объектная модель языка PHP.
Extends
Механизм наследования – очень важная часть всего объектно-ориентированного подхода. Попытаемся объяснить его суть на примере. Допустим, мы создаем описание человека. Очевидно, что сделать это мы можем по-разному, в зависимости от того, для чего нужно это описание. Можно описать человека как программиста: он знает такие-то языки программирования, операционные системы, участвовал в стольких-то проектах. Однако если человек программист, то он не перестает быть человеком вообще, т.е. он имеет имя, фамилию, место жительства и т.п. Если перевести наши рассуждения в термины объектно-ориентированного программирования, то можно сказать, что мы описали два класса – класс людей и класс программистов, каждый со своими свойствами и методами. Причем класс программистов, очевидно, обладает всеми свойствами класса людей и при этом имеет свои специфические характеристики, т.е. класс программистов является подклассом класса людей. Так, если у человека вообще есть имя, то у программиста оно тоже должно быть, но не наоборот. Кроме программистов можно выделить еще множество классов по профессиональной принадлежности людей. И все они будут подклассами класса людей. Часто на практике удобно определять общий класс, который может использоваться сразу в нескольких проектах (например, класс людей или личностей), и адаптировать его для специфических нужд каждого проекта (например, как класс программистов). Как это можно реализовать? С помощью механизма расширений. Любой класс может быть расширением другого класса. Расширяющий (или производный) класс, кроме тех свойств и методов, которые описаны в его определении, имеет все функции и свойства основного (базового класса). В нашем примере класс программистов – расширяющий, а класс всех людей – базовый. Из класса нельзя удалить никакие существующие свойства и функции, класс можно только расширить. Расширяющий класс в PHP4 всегда зависит только от одного базового класса, поскольку множественное наследование в PHP не поддерживается. Расширяются классы в PHP с помощью ключевого слова extends.
<?php class Person { // определяем класс Личности var $first_name; // имя личности var $last_name; // фамилия личности function make_person($t,$a){ // метод устанавливает // значения имени и фамилии объекта $this->first_name = $t; $this->last_name = $a; } function show_person(){ // метод отображает информацию о личности echo ("<h2>" . $this->first_name . " " . $this->last_name . "</h2>"); } } class Programmer extends Person{ // определяем класс // Programmer, расширяющий Person var $langs = array ("Lisp"); // константным массивом // задать переменную в var можно function set_lang($new_lang){ // метод добавляет еще // один язык к списку известных $this->langs[] = $new_lang; } } ?>
Пример 6.3. Использование механизма наследования (html, txt)
Класс Programmer имеет те же переменные и функции, что и класс Person, плюс переменную $langs, в которой содержится список изученных программистом языков, и функцию set_lang для добавления еще одного языка к списку изученных. Создать представителя класса программистов можно обычным способом с помощью конструкции new. После этого можно устанавливать и получать список языков, которые знает программист, и в то же время можно использовать функции, заданные для класса Person, т.е. устанавливать и получать имя и фамилию программиста и отображать сведения о нем в браузере:
<?php $progr = new Programmer; $progr -> set_lang("PHP"); // методы, определенные для // класса Programmer print_r ($progr->langs); // методы, определенные для класса Person $progr->make_person("Bill","Gates"); $progr->show_person(); ?>
Отношения, в которых состоят созданные нами классы Person и Programmer, называют также отношениями родитель–потомок. Класс Person – родитель, а его потомки, такие как класс Programmer, создаются, основываясь на нем, с помощью расширений. Любой класс может стать родительским и соответственно породить потомков.
Порядок определения классов имеет значение. Нельзя сначала определить класс Programmer, расширяющий класс Person, а уже потом сам класс Person. Класс должен быть определен перед тем, как он будет использоваться (расширяться).
Инициализация переменных
Часто некоторым атрибутам класса бывает необходимо присваивать значения сразу после создания представителя класса. Когда мы создавали класс статей, для присваивания значений атрибутам (свойствам) класса мы использовали специальную функцию make_article(). Вообще говоря, мы поступили не совсем верно, потому что занялись изобретением велосипеда. Специально для задания начальных значений атрибутам класса существует два стандартных метода. В PHP4 можно инициализировать значения с помощью оператора var или с помощью функции конструктора. С помощью var можно инициализировать только константные значения. Для задания не константных значений используют функцию конструктор, которая вызывается автоматически, когда объект конструируется из класса. Функция-конструктор должна иметь имя, совпадающее с именем всего класса, в котором она определена.
Приведем пример. Допустим, при создании объекта «статья» мы хотим установить его свойства следующим образом: автора – равным строке «Иванов», название и краткое содержание – соответствующим элементам глобального массива $_POST, а дату публикации статьи – текущей дате. Тогда следующее описание класса не является корректным в PHP4:
<? class Articles { // Создаем класс Статей var $title= $_POST["title"]; var $author = "Иванов"; var $description = $_POST["description"]; var $published = date("Y-m-d"); // метод, который присваивает значения // атрибутам класса } ?>
А вот такое описание класса в PHP4 будет работать так, как нужно:
<? class Articles { // Создаем класс Статей var $title; var $author = "Иванов"; var $description; var $published; // метод, который присваивает значения // атрибутам класса function Articles(){ $this->title = $_POST["title"]; $this->description = $_POST["description"]; $this ->published = date("Y-m-d"); } } ?>
Отметим, что в PHP3 и PHP4 конструкторы работают по-разному. В PHP3 функция становилась конструктором, если она имела то же имя, что и класс, а в PHP4 – если она имеет то же имя, что и класс, в котором она определена. Разница в подходах видна, когда один класс расширяет другой и происходит наследование свойств и методов базового класса. Но об этом мы поговорим чуть позже. В PHP5 конструктор класса именуется _construct. Кроме того, в PHP5 появились и деструкторы – функции, которые вызываются автоматически перед уничтожением объекта. В PHP5 функция-деструктор должна быть названа _destruct.
Классы и объекты
Начнем с основных понятий объектно-ориентированного программирования – класса и объекта. Существует множество определений этих понятий. Мы дадим следующее: объект – это структурированная переменная, содержащая всю информацию о некотором физическом предмете или реализуемом в программе понятии, класс – это описание таких объектов и действий, которые можно с ними выполнять.
В PHP класс определяется с помощью следующего синтаксиса:
class Имя_класса{ var $имя_свойства; /*список свойств*/ function имя_метода( ){ /* определение метода */ } /*список методов*/ }
Имена свойств объектов класса объявляются с помощью ключевого слова var, методы, применимые к объектам данного класса, описываются функциями. Внутри определения класса можно использовать ключевое слово this для обращения к текущему представителю класса.
Например, нам нужно создать класс, описывающий категорию статей. У каждой статьи имеются такие свойства, как название, автор и краткое содержание. Какие действия мы хотим совершать со статьями? Возможно, нам понадобится задавать значения перечисленным свойствами статьи, отображать статью в браузере. Тогда определение этого класса может выглядеть следующим образом:
<? class Articles { // Создаем класс Статей var $title; var $author; var $description; // метод, который присваивает значения // атрибутам класса function make_article($t, $a, $d){ $this->title = $t; $this->author = $a; $this->description = $d; } //метод для отображения экземпляров класса function show_article(){ $art = $this->title . "<br>" . $this->description . "<br>Автор: " . $this->author; echo $art; } } ?>
Итак, для описания физических объектов типа «статья» мы создали класс с именем Articles, состоящий из трех переменных, содержащих характеристики статьи, и двух функций для создания конкретной статьи и для ее отображения.
Как известно, работая с PHP, можно периодически переключаться в режим HTML. В этом случае программа состоит из нескольких кусков (блоков) кода. Определение класса нельзя разносить по разным блокам php-кода и тем более по разным файлам. То есть если написать:
<?php class Articles { // Начало описания класса var $title; ?> <?php // продолжение описания класса function show_article(){ // содержание метода } } // конец описания класса ?>
то программа не будет работать корректно.
Несколько замечаний по поводу имен классов. Имя класса должно удовлетворять правилам именования объектов в языке PHP, но есть ряд имен, которые зарезервированы разработчиками для своих целей. В первую очередь это имена, начинающиеся с символа подчеркивания «_». Для создания классов и функций нельзя использовать такие имена. Кроме того, зарезервировано имя stdClass, поскольку оно используется внутри движка PHP.
Конструкторы
Теперь, после знакомства с механизмом наследования в PHP, мы можем прокомментировать различие между конструкторами PHP4 и PHP3 и более подробно рассказать о конструкторах вообще. Напомним, что в PHP3 конструктор – это функция, имя которой совпадает с именем класса. А в PHP4 – функция, имя которой совпадает с именем класса, в котором она определена.
<?php class Programmer extends Person{ // определяем класс // Programmer, расширяющий Person var $langs = array ("Lisp"); function Programmer(){ // этот конструктор будет // работать и в PHP3, и в PHP4 $this->make_person("Иван","Петров"); } } ?>
Пример 6.4. Использование конструктора (html, txt)
Здесь функция Programmer() является конструктором, т.е. выполняется сразу после создания любого представителя класса Programmer, задавая ему имя «Иван» и фамилию «Петров». Конструкторы, как и любые другие функции, могут иметь аргументы. В этом случае, создавая представителя класса, нужно указать значения этих параметров. Аргументы конструктора могут иметь и значения по умолчанию. Если все аргументы имеют значения по умолчанию, тогда можно создавать экземпляр класса без параметров.
<?php class Programmer extends Person{ // определяем класс // Programmer, расширяющий Person var $langs = array ("Lisp"); function Programmer($n = "Иван", $f = "Петров"){ // это конструктор $this->make_person($n,$f); } } $default_progr = new Programmer(); // создаст программиста Ивана Петрова $new_progr = new Programmer("Вася", "Сидоров"); // создаст программиста Васю Сидорова print_r($new_progr); /* выведет информацию о переменной $new_progr, т.е. свойства объекта и их значения */ ?>
Пример 6.5. Использование конструктора (html, txt)
Приведенные примеры будут работать и в PHP3, и в PHP4, конечно если дописать в них определение базового класса Person. Допустим, ситуация немного другая: конструктор имеется только у базового класса Person:
<?php class Person { // определяем класс Личности var $first_name; var $last_name; function Person($t,$a){ // конструктор $this->first_name = $t; $this->last_name = $a; } /* ... */ } class Programmer extends Person{ // определяем класс // Programmer, расширяющий Person var $langs = array ("Lisp"); function set_lang($new_lang){ $this->langs[] = $new_lang; } } $new_progr = new Programmer("Вася", "Сидоров"); ?>
Что произойдет в этом случае при создании объекта класса Programmer, будет ли автоматически вызвана какая-либо функция? В PHP3 ничего не произойдет, поскольку в этом классе нет функции с именем Programmer() (здесь конструктор – это функция, имя которой совпадает с именем класса). В PHP4 будет вызван конструктор базового класса, если он существует, т.е. вызовется функция Person() из класса Person (здесь конструктор – функция, имя которой совпадает с именем класса, в котором она определена).
Еще одна ситуация – в базовом классе есть функция, имя которой совпадает с именем расширяющего класса, а в расширяющем классе нет конструктора.
<?php class Person { // определяем класс Личности var $first_name; var $last_name; function Person($t,$a){ // конструктор $this->first_name = $t; $this->last_name = $a; } function Programmer($new_lang){ echo "Я – программист"; } } class Programmer extends Person{ // определяем класс // Programmer, расширяющий Person var $langs = array ("Lisp"); function set_lang($new_lang){ $this->langs[] = $new_lang; } } $new_progr = new Programmer("Вася", "Сидоров"); ?>
В этом случае PHP3 вызовет в качестве конструктора функцию Programmer() из описания класса Person. Поскольку конструктор – это функция, у которой то же имя, что и у класса. И неважно, определена ли эта функция в самом классе или она наследуется из базового класса. В PHP4 класс Programmer не будет иметь своего конструктора, поэтому вызовется конструктор базового класса.
Ни в PHP 3, ни в PHP 4 конструктор базового класса не вызывается автоматически из конструктора порожденного класса.
Кроме нового названия для конструкторов и появления деструкторов в PHP5 произошло еще достаточно много изменений. Мы не будем обсуждать их подробно, только опишем в общих чертах. Основное изменение – это передача значений параметров класса по ссылке и присвоение объектов по ссылке, а не по значению, как это было в PHP4. В PHP5 если создаются две равные переменные типа объект, то они указывают на одно значение и изменяются одновременно (мы приводили похожий пример с переменными строкового типа). В связи с этим появился новый механизм для создания копий объектов – так называемое клонирование. В PHP4 все методы и переменные класса доступны извне, т.е. они всегда являются открытыми. В PHP5 переменные и методы можно делать открытыми (доступными отовсюду), закрытыми (доступными только внутри класса) и защищенными (доступными внутри класса и в его производных классах). Кроме того, появилась возможность создавать интерфейсы и абстрактные классы и многое другое. В целом объектная модель в PHP5 значительно усовершенствована для более точного соответствия объектно-ориентированной парадигме программирования.
Объекты
В одной из первых лекций мы упоминали о существовании в PHP такого типа данных, как объект. Класс – это описание данных одного типа, данных типа объект. Классы являются как бы шаблонами для реальных переменных. Переменная нужного типа создается из класса с помощью оператора new. Создав объект, мы можем применять к нему все методы и получать все свойства, определенные в описании класса. Для этого используют такой синтаксис: $имя_объекта->название_свойства или $имя_объекта->название_метода(список аргументов). Заметим, что перед названием свойства или метода знака $ не ставят.
<?php $art = new Articles; // создаем объект $art echo ($art ->title); // выводим название объекта $art $another_art = new Articles; // создаем объект $another_art $another_art->show_article(); // вызываем метод для // отображения объекта в браузер ?>
Пример 6.1. Доступ к методам и свойствам объекта (html, txt)
Каждый из объектов класса имеет одни и те же свойства и методы. Так, у объекта $art и у объекта $another_art есть свойства title, description, author и методы Articles(), show_article(). Но это два разных объекта. Представим себе объект как директорию в файловой системе, а его характеристики – как файлы в этой директории. Очевидно, что в каждой директории могут лежать одинаковые файлы, но тем не менее они считаются различными, поскольку хранятся в разных директориях. Точно так же свойства и методы считаются различными, если они применяются к разным объектам. Чтобы получить нужный файл из директории верхнего уровня, мы пишем полный путь к этому файлу. При работе с классами нужно указывать полное имя функции, которую мы хотим вызвать. Директорией верхнего уровня в PHP будет пространство глобальных переменных, а путь указывается с помощью разделителя ->. Таким образом, имена $art->title и $another_art->title обозначают две разные переменные. Переменная в PHP имеет только один знак доллара перед именем, поэтому нельзя писать $art->$title. Эта конструкция будет рассмотрена не как обращение к свойству title объекта $art, а как обращение к свойству, имя которого задано переменной $title (например, $art->"").
<?php $art->title = "Введение в Internet"; // так можно установить // значение свойства объекта $art->$title = "Введение в Internet"; // так нельзя установить // значение свойства объекта $property = "title"; $art->$property = "Введение в Internet"; // так можно установить значение // свойства объекта ?>
Пример 6.2. Установка значений свойств (html, txt)
Создавая класс, мы не можем знать, какое имя будет иметь объект этого класса, тем более что объектов может быть много и все могут иметь разные имена. Соответственно мы не знаем, как обращаться к объекту внутри определения класса. Для того чтобы иметь доступ к функциям и переменным внутри определения класса, нужно использовать псевдопеременную $this. Например, $this->title возвращает значение свойства title у текущего объекта данного класса. Иногда эту переменную предлагают читать как «мое собственное» (к примеру, по отношению к свойству).
Оператор ::
Иногда внутри описания класса возникает необходимость сослаться на функции или переменные из базового класса. Бывает, что нужно ссылаться на функции в классе, ни один представитель которого еще не создан. Как быть в таком случае? В PHP4 для этого существует специальный оператор «::»
Например, вот так можно вызвать в описании класса Programmer функцию show_name() из базового класса Person и функцию say_hello(), заданную в описании класса Programmer, когда ни один объект этого класса еще не был создан:
<?php class Person { // определяем класс Личности var $first_name; var $last_name; function Person($t,$a){ // конструктор $this->first_name = $t; $this->last_name = $a; } function show_name(){ // метод отображает информацию о личности echo ("Меня зовут, " . $this->first_name . " " . $this->last_name . "!<br>"); } } class Programmer extends Person{ // определяем класс // Programmer, расширяющий Person function set_lang($new_lang){ // метод добавляет еще // один язык к списку известных $this->langs[] = $new_lang; Person::show_name(); // вызываем функцию из базового класса echo "И я знаю теперь еще и " . $new_lang; } function show_name(){ echo ("Я программист, " . $this->first_name . " " . $this->last_name . "!<br>"); } function say_hello(){ echo "Привет!<br>"; } } Programmer::say_hello(); // вызываем функцию, когда ни // один объект ее класса еще не создан $new_progr = new Programmer("Вася","Сидоров"); $new_progr->set_lang("PHP"); ?>
В результате работы этой программы получим следующее:
Привет! Меня зовут Вася Сидоров! И я знаю теперь еще и PHP
С помощью команды Programmer::say_hello(); мы вызываем функцию say_hello класса Programmer как таковую, а не как метод, применяемый к объекту данного класса. В этот момент переменных класса нет. Поэтому функции, вызываемые до создания объекта, не могут пользоваться переменными класса и конструкцией this, но могут пользоваться локальными и глобальными переменными.
В определении класса Programmer мы переопределили функцию show_name(), поэтому вызвать функцию show_name() из базового класса Person можно только с помощью оператора «::» Вообще говоря, внутри определения класса мы можем вызывать любые методы и свойства, заданные в его базовом классе с помощью обычного $this, если только порожденный класс не переопределяет эти свойства и методы, как в нашем примере.
Оператор parent
В приведенном выше примере, обращаясь к базовому классу, мы использовали его имя (мы писали Person::show_name()). Это не совсем удобно, потому что имя класса или иерархия классов может измениться, и тогда придется переписывать код описаний всех классов с тем, чтобы привести используемые в них имена в соответствие с новой иерархией. Чтобы избежать подобной ситуации, вместо имени базового класса нужно использовать ключевое слово parent (например, parent::show_name()). Parent ссылается на класс, прописанный после extends в объявлении вашего класса. Поэтому если вдруг иерархия классов изменится, то достаточно будет внести изменения в имена, указанные после extends в описаниях классов.
выводим название объекта
<?php $art = new Articles; // создаем объект $art echo ($art ->title); // выводим название объекта $art $another_art = new Articles; // создаем объект $another_art $another_art->show_article(); // вызываем метод для // отображения объекта в браузер ?> |
Пример 6.1. Доступ к методам и свойствам объекта |
Закрыть окно |
<?php $art->title = "Введение в Internet"; // так можно установить // значение свойства объекта $art->$title = "Введение в Internet"; // так нельзя установить // значение свойства объекта $property = "title"; $art->$property = "Введение в Internet"; // так можно установить значение // свойства объекта ?> |
Пример 6.2. Установка значений свойств |
Закрыть окно |
<?php class Person { // определяем класс Личности var $first_name; // имя личности var $last_name; // фамилия личности function make_person($t,$a){ // метод устанавливает // значения имени и фамилии объекта $this->first_name = $t; $this->last_name = $a; } function show_person(){ // метод отображает информацию о личности echo ("<h2>" . $this->first_name . " " . $this->last_name . "</h2>"); } } class Programmer extends Person{ // определяем класс // Programmer, расширяющий Person var $langs = array ("Lisp"); // константным массивом // задать переменную в var можно function set_lang($new_lang){ // метод добавляет еще // один язык к списку известных $this->langs[] = $new_lang; } } ?> |
Пример 6.3. Использование механизма наследования |
Закрыть окно |
<? php class Programmer extends Person{ // определяем класс // Programmer, расширяющий Person var $langs = array ("Lisp"); function Programmer(){ // этот конструктор будет // работать и в PHP3, и в PHP4 $this->make_person("Иван","Петров"); } } ?> |
Пример 6.4. Использование конструктора |
Закрыть окно |
<? php class Programmer extends Person{ // определяем класс // Programmer, расширяющий Person var $langs = array ("Lisp"); function Programmer($n = "Иван", $f = "Петров"){ // это конструктор $this->make_person($n,$f); } } $default_progr = new Programmer(); // создаст программиста Ивана Петрова $new_progr = new Programmer("Вася", "Сидоров"); // создаст программиста Васю Сидорова print_r($new_progr); /* выведет информацию о переменной $new_progr, т.е. свойства объекта и их значения */ ?> |
Пример 6.5. Использование конструктора |
Закрыть окно |
<?php // Создаем классы Статей и Личностей. // Статья имеет заголовок, автора и // описание. Личность имеет имя, фамилию // и e-mail class Article { var $title; var $author; var $description; // метод, который присваивает значения // атрибутам класса function Article($t="Название отсутствует", $a="Автор отсутствует", $d="Описание отсутствует"){ $this->title = $t; $this->author = $a; $this->description = $d; } // метод для отображения экземпляров класса function show(){ $art = "<h2>$this->title</h2><font size=-1>$this->description</font><p>Автор: $this->author</p>"; echo $art; } } // Определение класса Личностей class Person { var $first_name; var $last_name; var $email; //метод, который присваивает значения атрибутам класса function Person($t="Имя не введено", $a="Фамилия не введена",$d="Email не указан"){ $this->first_name = $t; $this->last_name = $a; $this->email = $d; } //метод для отображения экземпляров класса function show(){ $art = "<h2>$this->first_name</h2><font size=-1>$this->last_name</font><p>Автор: $this->email</p>"; echo $art; } } // Далее следует собственно создание и отображение // экземпляров выбранного класса if (isset($_GET["art_create"])){ //Если была выбрана статья $art = new Article; // создаем представителя класса статей $art_vars = get_class_vars(get_class($art)); //какие // аргументы этого класса нужно задать Make_form($art,$art_vars,"art_create"); //вызов функции // создания формы if (isset($_GET["create_real"])){ Show_($art_vars); } // если данные этой формы отправлены, то вызываем // функцию показа } //то же самое, если была выбрана личность if (isset($_GET["pers_create"])){ $art = new Person; $art_vars = get_class_vars(get_class($art)); Make_form($art,$art_vars,"pers_create"); if (isset($_GET["create_real"])){ Show_($art_vars); } } // функция создания формы function Make_form($art,$art_vars,$glob){ $str = "<form>"; // html код формы записывается // в строку $str //перебираем список переменных класса объекта $art foreach ($art_vars as $var_name => $var_value){ $str .="$var_name<input type=text name=$var_name><br>"; //создаем элемент формы с именем свойства класса } $str .= "<input type=hidden name=$glob>"; // чтобы не // забыть, что мы создаем $str .= "<input type=submit name=create_real value='Create and Show'></form>"; echo "$str"; // выводим форму } // функция показа объекта function Show_($art_vars){ global $art; //используется глобальное имя объекта $k = count($art_vars); //число свойств класса // (переменных в форме) $p=0; //вспомогательная переменная foreach ($art_vars as $name => $value){ $p++; if ($_GET["$name"]=="") $val= $art->$name; else $val = $_GET["$name"]; if ($p<>$k) $par .='"'. $val.'",'; else $par .='"'. $val.'"'; } $par = '$art->'.$const ."(" .$par.");"; // теперь $par представляет собой php-код для вызова // метода класса $art, изначально // записанного в $par // например, // $art->Person('Vasia','Petrov','vas@intuit.ru'); eval($par); // функция eval выполняет код, // содержащийся в $par $art->show(); } ?> |
Листинг 6.6. Использование объектно-ориентированного подхода |
Закрыть окно |
мы хотели по выбору пользователя
Итак, мы хотели по выбору пользователя генерировать форму для ввода описания статьи или человека и отображать данные, введенные в эту форму. Попробуем решить эту задачу, используя объектно-ориентированный подход. Для начала создадим форму, где пользователь выбирает, что он хочет создать, – описание статьи или человека (точнее, это будут две формы):
<form action="task1.php"> Создать описание статьи: <input type=submit name=art_create value="Create Article"> </form> <form action="task1.php"> Создать описание личности: <input type=submit name=pers_create value="Create Person"> </form>
Теперь напишем файл для обработки этих форм. В нем создадим два класса – статьи и личности. У каждого класса имеется метод для инициализации его переменных и метод для отображения объектов данного класса. При решении задачи будут использованы две функции, встроенные в PHP для работы с классами и объектами. Это функция get_class(объект), возвращающая имя класса, экземпляром которого является объект, переданный ей в качестве параметра. И функция get_class_vars(имя класса), которая возвращает массив всех свойств класса и их значений по умолчанию. Аналогично можно получить массив имен всех методов класса: get_class_methods (имя класса)
Листинг 6.6. Использование объектно-ориентированного подхода (html, txt)
мы изучили основные понятия объектной
Итак, мы изучили основные понятия объектной модели, используемой в языке PHP. Были описаны правила создания классов и их представителей-объектов, способы задания начальных значений переменным класса, способы получения значений свойств и вызов методов классов.
Функции asort, rsort, arsort
Если требуется сохранять индексы элементов массива после сортировки, то нужно использовать функцию asort (массив [, флаги]). Если необходимо отсортировать массив в обратном порядке, т.е. от наибольшего значения к наименьшему, то можно задействовать функцию rsort (массив [, флаги]). А если при этом нужно еще и сохранить значения ключей, то следует использовать функцию arsort(массив [, флаги]). Как вы, наверное, заметили синтаксис у этих функций абсолютно такой же, как у функции sort. Соответственно и значения флагов могут быть такими же, как у sort: SORT_REGULAR, SORT_NUMERIC, SORT_STRING. Кстати говоря, флаг SORT_NUMERIC появился только в PHP4.
<?php $books = array("Пушкин"=>"Руслан и Людмила", "Толстой"=>"Война и мир", "Лермонтов"=>"Герой нашего времени"); asort($books); // сортируем массив, // сохраняя значения ключей print_r($books); echo "<br>"; rsort($books); // сортируем массив в обратном порядке, // ключи будут заменены print_r($books); ?>
Пример 7.7. Применение функций asort, rsort, arsort (html, txt)
В результате работы этого скрипта получим:
Array ( [Толстой] => Война и мир [Лермонтов] => Герой нашего времени [Пушкин] => Руслан и Людмила ) Array ( [0] => Руслан и Людмила [1] => Герой нашего времени [2] => Война и мир )
Пример 7.8. Допустим, мы создаем каталог описаний документов. У каждого документа есть автор, название, дата публикации и краткое содержание. Мы уже не раз отображали описания, составленные из этих характеристик. Каждый раз порядок отображения этих элементов зависел от созданной нами программы. Теперь же мы хотим иметь возможность изменять порядок отображения элементов по желанию пользователя. Составим для этого следующую форму:
<form action=task.php> <table border=1> <tr><td>Название </td><td><input type=text name=title size=5> </td></tr> <tr><td>Краткое содержание </td><td><input type=text name=description size=5> </td></tr> <tr><td>Автор </td><td><input type=text name=author size=5> </td></tr> <tr><td>Дата публикации </td><td><input type=text name=published size=5></td></tr> </table> <input type=submit value="Отправить"> </form>
Пример 7.8a. Форма для примера 7.8 (html, txt)
Будем упорядочивать данные, переданные этой формой, по убыванию их значений, сохраняя при этом значения ключей. Для этого удобно воспользоваться функцией arsort(). Поскольку нам важен только новый порядок элементов, сохраним в новом массиве ключи исходного массива в нужном порядке. Мы сохраняем ключи исходного массива, поскольку они являются именами элементов, из которых конструируется описание документа, а помнить их важно. Итак, получаем такой скрипт:
<?php print_r($_GET); echo "<br>"; arsort ($_GET); // сортируем массив в обратном порядке, // сохраняя ключи print_r($_GET); echo "<br>"; $ordered_names = array_keys($_GET); // составляем новый массив foreach($ordered_names as $key => $val) echo "$key :$val <br>"; // выводим элементы нового массива ?>
Пример 7.8b. Программа обработки формы из примера 7.8 (html, txt)
Функция array_chunk
Есть еще одна функция, похожая на array_slice() – это array_chunk(). Она разбивает массив на несколько подмассивов заданной длины. Синтаксис ее такой:
array_chunk ( массив, размер [, сохранять_ключи])
В результате работы array_chunk() возвращает многомерный массив, элементы которого представляют собой полученные подмассивы. Если задать параметр сохранять ключи как true, то при разбиении будут сохранены ключи исходного массива. В противном случае ключи элементов заменяются числовыми индексами, которые начинаются с нуля.
Пример 7.15. У нас есть список приглашенных, оформленный в виде массива их фамилий. У нас имеются столики на три персоны. Поэтому нужно распределить всех приглашенных по трое.
<?php $persons = array("Иванов", "Петров", "Сидорова","Зайцева", "Волкова"); $triples = array_chunk($persons,3); // делим массив на подмассивы // по три элемента foreach ($triples as $k => $table){ // выводим полученные тройки echo "За столиком номер $k сидят: <ul>"; foreach ($table as $pers) echo "<li>$pers"; echo "</ul>"; } ?>
Пример 7.15. Использование функции array_chunk() (html, txt)
В результате получим:
за столиком номер 0 сидят: • Иванов • Петров • Сидорова за столиком номер 1 сидят: • Зайцева • Волкова
Функция array_keys
Функция array_keys() выбирает все ключи массива. Но у нее имеется дополнительный аргумент, с помощью которого можно получить список ключей элементов с конкретным значением. Синтаксис этой функции таков:
array_keys ("массив" [,"значение для поиска"])
Функция array_keys() возвращает как строковые, так и числовые ключи массива, организуя все значения в виде нового массива с числовыми индексами.
Пример 7.5. Мы записали массив языков, которые изучили. Список был длинным, и некоторые языки были записаны несколько раз. У нас возникло подозрение, что один из таких языков – Lisp. Давайте это проверим:
<?php $langs = array("Lisp","Python","Java","PHP", "Perl","Lisp"); $lisp_keys = array_keys($langs,"Lisp"); echo "Lisp входит в массив ". count($lisp_keys) ." раза:<br>"; foreach ($lisp_keys as $val){ echo "под номером $val <br>"; } ?>
Пример 7.5. Применение функции array_keys() (html, txt)
В результате получим:
Lisp входит в массив 2 раза: под номером 0 под номером 5
Функция array_keys(), как и две предыдущие, зависит от регистра, т.е. элементов LISP в массиве она не обнаружит. array_keys() появилась только в PHP4. В PHP3 для реализации ее функциональности нужно придумывать свою функцию.
Если есть функция для получения всех ключей массива, то можно предположить, что существует и функция для получения всех значений массива. Действительно, она существует. Это функция array_values(массив). Все значения переданного ей массива записываются в новый массив, проиндексированный целыми числами, т.е. все ключи массива теряются, остаются только значения. Но вернемся к нашему примеру.
Итак, мы выяснили, что язык Lisp случайно упомянут в нашем массиве дважды. Поскольку изучить один язык дважды нельзя («учил, но забыл» не считается), то нужно как-то избавиться от повторяющихся языков. Сделать это довольно просто с помощью функции array_unique().
Функция array_search
Это еще одна функция для поиска значения в массиве. В отличие от in_array в результате работы array_search возвращает значение ключа, если элемент найден, и ложь – в противном случае. А вот синтаксис у этих функций одинаковый:
array_search("искомое значение","массив", ["ограничение на тип"]);
Сравнение строк чувствительно к регистру, а если указан опциональный аргумент, то сравниваются еще и типы значений. До PHP 4.2.0, если искомое значение не было найдено, эта функция возвращала ошибку или пустое значение NULL.
Пример 7.4. Теперь, наоборот, пусть у нас есть массив языков программирования, которые мы знаем. Причем ключом каждого элемента является номер, указывающий, каким по счету был изучен этот язык.
<?php $langs = array("Lisp","Python","Java", "PHP","Perl"); if (!array_search("PHP",$langs)) echo "Надо бы изучить PHP<br>"; else { $k = array_search("PHP",$langs); echo "PHP я изучила $k – м"; } ?>
Пример 7.4. Применение функции array_search() (html, txt)
В результате мы получим строчку:
PHP я изучила 3 – м
Очевидно, что эта функция более функциональна, чем in_array, поскольку мы не только получаем информацию о том, что искомый элемент в массиве есть, но и узнаем, где именно в массиве он находится. А что будет, если искомых элементов в массиве несколько? В таком случае функция array_search() вернет ключ первого из найденных элементов. Чтобы получить ключи всех элементов, нужно воспользоваться функцией array_keys().
Функция array_slice
Поскольку массив – это набор элементов, вполне вероятно, потребуется выделить из него какой-нибудь поднабор. В PHP для этих целей есть функция array_slice. Ее синтаксис таков:
array_slice (массив, номер_элемента [, длина])
Эта функция выделяет подмассив длины длина в массиве массив, начиная с элемента, номер которого задан параметром номер_элемента. Положительный номер_элемента указывает на порядковый номер элемента относительно начала массива, отрицательный – на номер элемента с конца массива.
<?php $arr = array(1,2,3,4,5); $sub_arr = array_slice($arr,2); print_r($sub_arr); /* выведет Array ( [0] => 3 [1] =>4 [2] => 5 ), т.е. подмассив, состоящий из элементов 3, 4, 5 */ $sub_arr = array_slice($arr,-2); print_r($sub_arr); // выведет Array ( [0] => 4 [1] => 5 ), // т.е. подмассив, из элементов 4, 5 ?>
Пример 7.13. Использование функции array_slice() (html, txt)
Если задать параметр длина при использовании array_slice, то будет выделен подмассив, имеющий ровно столько элементов, сколько задано этим параметром. Длину можно указывать и отрицательную. В этом случае интерпретатор удалит с конца массива число элементов, равное модулю параметра длина.
<?php $arr = array(1,2,3,4,5); $sub_arr = array_slice($arr, 2, 2); // содержит массив из элементов 3, 4 $sub = array_slice($arr,-3, 2); // тоже содержит массив из элементов 3, 4 $sub1 = array_slice($arr,0, -1); // содержит массив из // элементов 1, 2, 3, 4 $sub2 = array_slice($arr,-4, -2); // содержит массив из элементов 2, 3 ?>
Пример 7.14. Использование функции array_slice(). Вариант 2 (html, txt)
Функция array_unique
Функция array_unique(массив) удаляет повторяющиеся значения из массива и возвращает новый массив. Таким образом, вместо нескольких одинаковых значений и их ключей мы имеем одно значение. Какой у него будет ключ? Как из нескольких ключей одинаковых элементов выбирается тот, который будет сохранен в новом массиве? Происходит следующее. Все элементы массива преобразуются в строки и сортируются. Затем обработчик запоминает первый ключ для каждого значения, а остальные ключи игнорирует.
Попробуем избавиться от повторяющихся языков в списке изученных.
<?php $langs = array("Lisp","Java","Python","Java", "PHP","Perl","Lisp"); print_r(array_unique($langs)); ?>
Получим следующее:
Array ( [0] => Lisp [1] => Java [2] => Python [4] => PHP [5] => Perl )
Далее рассмотрим задачу сортировки массива.
Функция count
Не раз уже мы использовали функцию count(), чтобы вычислить количество элементов массива. На самом деле эта функция вычисляет число элементов в переменной вообще. Если применить ее к любой другой переменной, она возвратит 1. Исключение составляет переменная типа NULL – count(NULL) есть 0. Кроме того, применяя эту функцию к многомерному массиву, чтобы получить число его элементов, нужно использовать дополнительный параметр COUNT_RECURSIVE.
<? $del_items = array("langs" => array( "10"=>"Python", "12"=>"Lisp"), "other"=>"Информатика"); echo count($del_items) . "<br>"; // выведет 2 echo count($del_items,COUNT_RECURSIVE); // выведет 4 ?>
Пример 7.3. Применение функции count() (html, txt)
Мы не будем повторять все, что было сказано о массивах в предыдущих лекциях. В этой лекции мы рассмотрим некоторые встроенные функции для работы с массивами. И начнем мы с функций для поиска значений в массиве.
Функция in_array
in_array("искомое значение","массив", ["ограничение на тип"]);
позволяет установить, содержится ли в заданном массиве искомое значение. Если третий аргумент задан как true, то в массиве нужно найти элемент, совпадающий с искомым не только по значению, но и по типу. Если искомое значение – строка, то сравнение чувствительно к регистру.
Например, имеется массив не изученных нами языков программирования. Мы хотим узнать, содержится ли в этом массиве язык PHP. Напишем следующую программу:
<?php $langs = array("Lisp","Python","Java", "PHP","Perl"); if (in_array("PHP",$langs)) echo "Надо бы изучить PHP<br>"; // выведет сообщение "Надо бы изучить PHP" if (in_array("php",$langs)) echo "Надо бы изучить php<br>"; // ничего не выведет, поскольку в массиве // есть строка "PHP", а не "php" ?>
В качестве искомого значения этой функции может выступать и массив. Правда, это свойство было добавлено только начиная с PHP 4.2.0.
Например:
<?php $langs = array("Lisp","Python",array("PHP","Java"),"Perl"); if (in_array(array("PHP","Java"),$langs)) echo "Надо бы изучить PHP и Java<br>"; ?>
Функция sort
Функция sort имеет следующий синтаксис
sort (массив [, флаги])
и сортирует массив, т.е. упорядочивает его значения по возрастанию. Эта функция удаляет все существовавшие в массиве ключи, заменяя их числовыми индексами, соответствующими новому порядку элементов. В случае успешного завершения работы она возвращает true, иначе – false.
Пример 7.6. Пусть у нас есть два массива: цены товаров – их названия и, наоборот, названия товаров – их цены. Упорядочим эти массивы по возрастанию:
$items = array(10 => "хлеб", 20 => "молоко", 30 => "бутерброд"); sort($items); // строки сортируются в алфавитном // порядке, ключи теряются print_r($items);
$rev_items = array("хлеб" => 10, "бутерброд" => 30, "молоко" => 20); sort($rev_items); // числа сортируются по возрастанию, // ключи теряются print_r($rev_items); ?>
Пример 7.6. Применение функции sort() (html, txt)
Получим:
Array ( [0] => бутерброд [1] => молоко [2] => хлеб ) Array ( [0] => 10 [1] => 20 [2] => 30 )
В качестве дополнительного аргумента флаги может использоваться одна из следующих констант:
SORT_REGULAR – сравнивать элементы массива обычным образом;SORT_NUMERIC – сравнивать элементы массива как числа;SORT_STRING – сравнивать элементы массива как строки.
Массивы
В одной из первых лекций мы рассказывали о том, как можно создать массив данных. Напомним, что массив можно создать двумя способами:
С помощью конструкции array
$array_name = array("key1"=>"value1", "key2"=>"value2");
Непосредственно задавая значения элементам массива
$array_name["key1"] = value1;
Например, нам нужно хранить список документов, которые будут удалены из базы данных. Естественно хранить его в виде массива, ключом в котором будет идентификатор документа (его уникальный номер), а значением – название документа. Этот массив можно создать таким образом:
<? $del_items = array("10"=>"Наука и жизнь", "12"=>"Информатика"); $del_items["13"] = "Программирование на Php"; // добавляем элемент в массив ?>
Операции с массивами
Массив – это тип данных, с данными этого типа должны быть определены операции. Какие же операции можно производить с массивами? Массивы можно складывать и сравнивать.
Складывают массивы с помощью стандартного оператора «+». Вообще говоря, эту операцию по отношению к массивам точнее назвать объединением. Если у нас есть два массива, $a и $b, то результатом их сложения (объединения) будет массив $c, состоящий из элементов $a, к которым справа дописаны элементы массива $b. Причем, если встречаются совпадающие ключи, то в результирующий массив включается элемент из первого массива, т.е. из $a. Таким образом, если складываются массивы в языке PHP, от перемены мест слагаемых сумма меняется.
<? $a = array("и"=>"Информатика", "м"=>"Математика"); $b = array("и"=>"История","м"=>"Биология", "ф"=>"Физика"); $c = $a + $b; $d = $b +$a; print_r($c); /* получим: Array([и]=>Информатика [м]=>Математика [ф]=>Физика) */ print_r($d); /* получим: Array([и]=>История [м]=>Биология [ф]=>Физика) */ ?>
Пример 7.1. Сложение массивов (html, txt)
Сравнивать массивы можно, проверяя их равенство или неравенство либо эквивалентность или неэквивалентность. Равенство массивов – это когда совпадают все пары ключ/значение элементов массивов. Эквивалентность – когда кроме равенства значений и ключей элементов требуется еще, чтобы элементы в обоих массивах были записаны в одном и том же порядке. Равенство значений в PHP обозначается символом «==», а эквивалентность – символом «===».
<? $a = array("и"=>"Информатика", "м"=>"Математика"); $b = array("м"=>"Математика", "и"=>"Информатика"); if ($a == $b) echo "Массивы равны и"; else echo "Массивы НЕ равны и "; if ($a === $b) echo " эквивалентны"; else echo " НЕ эквивалентны"; // получим echo "Массивы равны и НЕ эквивалентны" ?>
Пример 7.2. Сравнение массивов (html, txt)
Далее рассмотрим еще одну важную операцию с массивом – подсчет количества его элементов. Для ее реализации в PHP есть специальная функция.
Применение функции ко всем элементам массива
Функция array_walk(массив, функция [, данные]) применяет созданную пользователем функцию функция ко всем элементам массива массив и возвращает true в случае успешного выполнения операции и false – в противном случае.
Пользовательская функция, как правило, имеет два аргумента, в которые поочередно передаются значение и ключ каждого элемента массива. Но если при вызове функции array_walk() указан третий аргумент, то он будет рассмотрен как значение третьего аргумента пользовательской функции, смысл которого определяет сам пользователь. Если функция пользователя требует больше аргументов, чем в нее передано, то при каждом вызове array_walk() будет выдаваться предупреждение.
Если необходимо работать с реальными значениями массива, а не с их копиями, следует передавать аргумент в функцию по ссылке. Однако нужно иметь в виду, что нельзя добавлять или удалять элементы массива и производить действия, изменяющие сам массив, поскольку в этом случае результат работы array_walk() считается неопределенным.
<?php $books1 = array( "А.С. Пушкин"=>"Руслан и Людмила", "Л.Н. Толстой"=>"Война и мир", "М.Ю. Лермонтов"=>"Герой нашего времени"); // создаем функцию, которую хотим // применить к элементам массива
function try_walk($val,$key,$data){ echo "$data \"$val\" написал $key<br>"; } // применяем ко всем элементам массива // $book1 функцию try_walk array_walk($books1,"try_walk","Роман"); ?>
Пример 7.11. Применение функции ко всем элементам массива (html, txt)
В результате работы скрипта получим:
Роман "Руслан и Людмила" написал А.С. Пушкин Роман "Война и мир" написал Л.Н. Толстой Роман "Герой нашего времени" написал М.Ю. Лермонтов
Заметим, что мы не изменили значений у элементов массива. Чтобы их изменить, надо было передавать значения в переменную $val функции try_walk по ссылке.
<?php $books1 = array( "А.С. Пушкин"=>"Руслан и Людмила", "Л.Н. Толстой"=>"Война и мир", "М.Ю. Лермонтов"=>"Герой нашего времени"); // создаем функцию, которую хотим // применить к элементам массива
function try_walk(&$val,$key){ $key = "<p>Автор: " .$key ."<br>"; $val = "Название: \"" . $val ."\"</p>"; echo $key.$val; } // применяем ко всем элементам массива // $book1 функцию try_walk
array_walk($books1,"try_walk"); print_r($books1); ?>
Пример 7.12. Применение функции ко всем элементам массива. Вариант 2 (html, txt)
В результате работы скрипта получим:
Автор: А.С. Пушкин Название: "Руслан и Людмила" Автор: Л.Н. Толстой Название: "Война и мир" Автор: М.Ю. Лермонтов Название: "Герой нашего времени" Array ( [А.С. Пушкин] => Название: "Руслан и Людмила" [Л.Н. Толстой] => Название: "Война и мир" [М.Ю. Лермонтов] => Название: "Герой нашего времени")
Сложение массивов
<? $a = array("и"=>"Информатика", "м"=>"Математика"); $b = array("и"=>"История","м"=>"Биология", "ф"=>"Физика"); $c = $a + $b; $d = $b +$a; print_r($c); /* получим: Array([и]=>Информатика [м]=>Математика [ф]=>Физика) */ print_r($d); /* получим: Array([и]=>История [м]=>Биология [ф]=>Физика) */ ?> |
Пример 7. 1. Сложение массивов |
Закрыть окно |
<? $a = array("и"=>"Информатика", "м"=>"Математика"); $b = array("м"=>"Математика", "и"=>"Информатика"); if ($a == $b) echo "Массивы равны и"; else echo " Массивы НЕ равны и "; if ($a === $b) echo " эквивалентны"; else echo " НЕ эквивалентны"; // получим echo "Массивы равны и НЕ эквивалентны" ?> |
Пример 7.2. Сравнение массивов |
Закрыть окно |
<? $del_items = array("langs" => array( "10"=>"Python", "12"=>"Lisp"), "other"=>"Информатика"); echo count($del_items) . "<br>"; // выведет 2 echo count($del_items,COUNT_RECURSIVE); // выведет 4 ?> |
Пример 7.3. Применение функции count() |
Закрыть окно |
<?php $langs = array("Lisp","Python","Java", "PHP","Perl"); if (!array_search("PHP",$langs)) echo " Надо бы изучить PHP<br>"; else { $k = array_search("PHP",$langs); echo "PHP я изучила $k – м"; } ?> |
Пример 7.4. Применение функции array_search() |
Закрыть окно |
<?php $langs = array("Lisp","Python","Java","PHP", "Perl","Lisp"); $lisp_keys = array_keys($langs,"Lisp"); echo " Lisp входит в массив ". count($lisp_keys) ." раза:<br>"; foreach ($lisp_keys as $val){ echo "под номером $val <br>"; } ?> |
Пример 7.5. Применение функции array_keys() |
Закрыть окно |
$items = array(10 => "хлеб", 20 => "молоко", 30 => "бутерброд"); sort($items); // строки сортируются в алфавитном // порядке, ключи теряются print_r($items); $rev_items = array("хлеб" => 10, "бутерброд" => 30, "молоко" => 20); sort($rev_items); // числа сортируются по возрастанию, // ключи теряются print_r($rev_items); ?> |
Пример 7.6. Применение функции sort() |
Закрыть окно |
<?php $books = array("Пушкин"=>"Руслан и Людмила", "Толстой"=>"Война и мир", "Лермонтов"=>"Герой нашего времени"); asort($books); // сортируем массив, // сохраняя значения ключей print_r($books); echo "<br>"; rsort($books); // сортируем массив в обратном порядке, // ключи будут заменены print_r($books); ?> |
Пример 7.7. Применение функций asort, rsort, arsort |
Закрыть окно |
Форма для примера
<form action=task.php> <table border=1> <tr><td>Название </td><td><input type=text name=title size=5> </td></tr> <tr><td>Краткое содержание </td><td><input type=text name=description size=5> </td></tr> <tr><td>Автор </td><td><input type=text name=author size=5> </td></tr> <tr><td>Дата публикации </td><td><input type=text name=published size=5></td></tr> </table> <input type=submit value="Отправить"> </form> |
Пример 7.8a. Форма для примера 7.8 |
Закрыть окно |
<?php $books = array("Пушкин"=>"Руслан и Людмила", "Толстой"=>"Война и мир", "Лермонтов"=>"Герой нашего времени"); ksort($books); // сортируем массив, // сохраняя значения ключей print_r($books); ?> |
Пример 7.9. Сортировка массива по ключам |
Закрыть окно |
<?php // массив выглядит таким образом: $books = array("Герой нашего времени" => array ("Лермонтов", 1840), "Руслан и Людмила" => array("Пушкин",1820), "Война и мир" => array ("Толстой",1863), "Идиот" => array("Достоевский",1868)); /* можно, конечно переписать этот массив по-другому, сделав год издания, например, индексом, но гораздо удобнее написать свою функцию для сортировки */ uasort($books,"cmp"); // сортируем массив с помощью функции cmp foreach ($books as $key => $book) { echo "$book[0]: \"$key\"<br>"; } function cmp($a,$b){ // функция, определяющая способ сортировки if ($a[1] < $b[1]) return -1; elseif ($a[1]==$b[1]) return 0; else return 1; } ?> |
Пример 7.10. Сортировка с помощью пользовательских функций |
Закрыть окно |
<?php $books1 = array( "А.С. Пушкин"=>"Руслан и Людмила", "Л.Н. Толстой"=>"Война и мир", "М.Ю. Лермонтов"=>"Герой нашего времени"); // создаем функцию, которую хотим // применить к элементам массива function try_walk($val,$key,$data){ echo "$data \"$val\" написал $key<br>"; } // применяем ко всем элементам массива // $book1 функцию try_walk array_walk($books1,"try_walk","Роман"); ?> |
Пример 7.11. Применение функции ко всем элементам массива |
Закрыть окно |
<?php $books1 = array( "А.С. Пушкин"=>"Руслан и Людмила", "Л.Н. Толстой"=>"Война и мир", "М.Ю. Лермонтов"=>"Герой нашего времени"); // создаем функцию, которую хотим // применить к элементам массива function try_walk(&$val,$key){ $key = "<p>Автор: " .$key ."<br>"; $val = "Название: \"" . $val ."\"</p>"; echo $key.$val; } // применяем ко всем элементам массива // $book1 функцию try_walk array_walk($books1,"try_walk"); print_r($books1); ?> |
Пример 7.12. Применение функции ко всем элементам массива. Вариант 2 |
Закрыть окно |
<?php $arr = array(1,2,3,4,5); $sub_arr = array_slice($arr,2); print_r($sub_arr); /* выведет Array ( [0] => 3 [1] =>4 [2] => 5 ), т.е. подмассив, состоящий из элементов 3, 4, 5 */ $sub_arr = array_slice($arr,-2); print_r($sub_arr); // выведет Array ( [0] => 4 [1] => 5 ), // т.е. подмассив, из элементов 4, 5 ?> |
Пример 7.13. Использование функции array_slice() |
Закрыть окно |
<?php $arr = array(1,2,3,4,5); $sub_arr = array_slice($arr, 2, 2); // содержит массив из элементов 3, 4 $sub = array_slice($arr,-3, 2); // тоже содержит массив из элементов 3, 4 $sub1 = array_slice($arr,0, -1); // содержит массив из // элементов 1, 2, 3, 4 $sub2 = array_slice($arr,-4, -2); // содержит массив из элементов 2, 3 ?> |
Пример 7.14. Использование функции array_slice(). Вариант 2 |
Закрыть окно |
<?php $persons = array("Иванов", "Петров", "Сидорова","Зайцева", "Волкова"); $triples = array_chunk($persons,3); // делим массив на подмассивы // по три элемента foreach ($triples as $k => $table){ // выводим полученные тройки echo "За столиком номер $k сидят: <ul>"; foreach ($table as $pers) echo "<li>$pers"; echo "</ul>"; } ?> |
Пример 7.15. Использование функции array_chunk() |
Закрыть окно |
<?php //массив задается функцией array $arr = array(2,1,3,4,5,6,4); // перебираем каждый элемент массива $arr. // Внутри цикла текущий ключ массива // содержится в переменной $k, // текущее значение – в переменной $val foreach ($arr as $k => $val){ $p = $k + 1; // синтаксис array array_slice ( // array array,int offset [,int length]) // array_slice выделяет подмассив // длины length в массиве array, // начиная с элемента offset. $out_next = array_slice($arr,$p); // получаем массив элементов, // идущих после текущего $out_prev = array_slice($arr,0,$k); // получаем массив элементов, // идущих перед текущим // функция mixed array_sum (array array) // подсчитывает сумму элементов массива array $next_sum = array_sum($out_next); $prev_sum = array_sum($out_prev); // если сумма элементов до текущего равна // сумме элементов после, то выводим // значение текущего элемента if ($next_sum==$prev_sum) echo "value:$val"; // можно посмотреть, что представляют собой // рассмотренные массивы на каждом шаге // print_r($out_next); echo "<br>"; // print_r($out_prev); // echo "$next_sum, $prev_sum<br>"; echo "<hr>"; } ?> |
Пример 7.16. Программа поиска числа, такого что сумма элементов справа от него равна сумме элементов слева от него |
Закрыть окно |
Сортировка массива по ключам
Очевидно, что может возникнуть необходимость в сортировке массива по значениям ключей. Например, если у нас есть массив данных о книгах, как в приведенном выше примере, то вполне вероятно, что мы захотим отсортировать книги по именам авторов. Для этого в PHP также не нужно писать много строк кода – можно просто воспользоваться функцией ksort() для сортировки по возрастанию (прямой порядок сортировки) или krsort() – для сортировки по убыванию (обратный порядок сортировки). Синтаксис этих функций опять же аналогичен синтаксису функции sort().
<?php $books = array("Пушкин"=>"Руслан и Людмила", "Толстой"=>"Война и мир", "Лермонтов"=>"Герой нашего времени"); ksort($books); // сортируем массив, // сохраняя значения ключей print_r($books); ?>
Пример 7.9. Сортировка массива по ключам (html, txt)
Получим:
Array ( [Лермонтов] => Герой нашего времени [Пушкин] => Руслан и Людмила [Толстой] => Война и мир )
Сортировка массивов
Необходимость сортировки данных, в том числе и данных, хранящихся в виде массивов, очень часто возникает при решении самых разнообразных задач. Если в языке Си для того, чтобы решить эту задачу, нужно написать десятки строк кода, то в PHP это делается одной простой командой.
Сортировка с помощью функции, заданной пользователем
Кроме двух простых способов сортировки значений массива (по убыванию или по возрастанию) PHP предлагает пользователю возможность самому задавать критерии для сортировки данных. Критерий задается с помощью функции, имя которой указывается в качестве аргумента для специальных функций сортировки usort() или uksort(). По названиям этих функций можно догадаться, что usort() сортирует значения элементов массива, а uksort() – значения ключей массива с помощью определенной пользователем функции. Обе функции возвращают true, если сортировка прошла успешно, и false – в противном случае. Их синтаксис выглядит следующим образом:
usort (массив , сортирующая функция) uksort (массив , сортирующая функция)
Конечно же, нельзя сортировать массив с помощью любой пользовательской функции. Эта функция должна удовлетворять определенным критериям, позволяющим сравнивать элементы массива. Как должна быть устроена сортирующая функция? Во-первых, она должна иметь два аргумента. В них интерпретатор будет передавать пары значений элементов для функции usort() или ключей массива для функции uksort(). Во-вторых, сортирующая функция должна возвращать:
целое число, меньшее нуля, если первый аргумент меньше второго;число, равное нулю, если два аргумента равны;число большее нуля, если первый аргумент больше второго.
Как и для других функций сортировки, для функции usort() существует аналог, не изменяющий значения ключей, – функция uasort().
Пример 7.10. Допустим, у нас есть массив, содержащий такие сведения о литературных произведениях, как название, автор и год создания. Мы хотим упорядочить книги по дате создания.
<?php // массив выглядит таким образом: $books = array("Герой нашего времени" => array ("Лермонтов", 1840), "Руслан и Людмила" => array("Пушкин",1820), "Война и мир" => array ("Толстой",1863), "Идиот" => array("Достоевский",1868)); /* можно, конечно переписать этот массив по-другому, сделав год издания, например, индексом, но гораздо удобнее написать свою функцию для сортировки */
uasort($books,"cmp"); // сортируем массив с помощью функции cmp
foreach ($books as $key => $book) { echo "$book[0]: \"$key\"<br>"; } function cmp($a,$b){ // функция, определяющая способ сортировки if ($a[1] < $b[1]) return -1; elseif ($a[1]==$b[1]) return 0; else return 1; } ?>
Пример 7.10. Сортировка с помощью пользовательских функций (html, txt)
В результате получим:
Пушкин: "Руслан и Людмила" Лермонтов: "Герой нашего времени" Толстой: "Война и мир" Достоевский: "Идиот"
Мы применили нашу собственную функцию сортировки ко всем элементам массива. Далее рассмотрим, как применить к элементам массива любую другую пользовательскую функцию.
Сумма элементов массива
В этом разделе мы познакомимся с функцией, вычисляющей сумму всех элементов массива. Сама задача вычисления суммы значений массива предельно проста. Но зачем писать лишний раз один и тот же код, если можно воспользоваться специально созданной и всегда доступной функцией. Функция эта называется, как можно догадаться, array_sum(). И в качестве параметра ей передается только имя массива, сумму значений элементов которого нужно вычислить.
В качестве примера использования этой функции приведем решение более сложной задачи, чем просто вычисление суммы элементов. Этот пример также иллюстрирует применение функции array_slice(), которую мы обсуждали чуть раньше.
Пример 7.16. Пусть дан массив натуральных чисел. Нужно найти в нем такое число, что сумма элементов справа от него равна сумме элементов слева от него.
<?php //массив задается функцией array $arr = array(2,1,3,4,5,6,4); // перебираем каждый элемент массива $arr. // Внутри цикла текущий ключ массива // содержится в переменной $k, // текущее значение – в переменной $val foreach ($arr as $k => $val){ $p = $k + 1; // синтаксис array array_slice ( // array array,int offset [,int length]) // array_slice выделяет подмассив // длины length в массиве array, // начиная с элемента offset. $out_next = array_slice($arr,$p); // получаем массив элементов, // идущих после текущего $out_prev = array_slice($arr,0,$k); // получаем массив элементов, // идущих перед текущим // функция mixed array_sum (array array) // подсчитывает сумму элементов массива array $next_sum = array_sum($out_next); $prev_sum = array_sum($out_prev); // если сумма элементов до текущего равна // сумме элементов после, то выводим // значение текущего элемента if ($next_sum==$prev_sum) echo "value:$val"; // можно посмотреть, что представляют собой // рассмотренные массивы на каждом шаге // print_r($out_next); echo "<br>"; // print_r($out_prev); // echo "$next_sum, $prev_sum<br>"; echo "<hr>"; } ?>
Пример 7.16. Программа поиска числа, такого что сумма элементов справа от него равна сумме элементов слева от него (html, txt)
В этой лекции мы изучили
Итак, подведем итоги. В этой лекции мы изучили ряд функций, упрощающих работу с массивами данных. Мы рассмотрели функции для поиска значения среди элементов массива; функции для сортировки элементов массива, как по их значениям, так и по ключам; функции, позволяющие применять ко всем элементам массива функцию, созданную пользователем. Кроме того, мы изучили функцию, выделяющую подмассивы из элементов массива, и функцию, вычисляющую сумму всех элементов массива. Использование всех этих функций было продемонстрировано на примерах. Все функции для работы с массивами доступны без каких-либо конфигурационных настроек PHP, и пользоваться ими можно абсолютно свободно. Так что, прежде чем изобретать велосипед, загляните в руководство по языку PHP: – вполне возможно, что кто-то сделал это до вас.