Глава 1. Введение

Содержание

Что такое PHP?

PHP
(рекурсивный акроним словосочетания "PHP: Hypertext Preprocessor") - это широко используемый язык программирования общего назначения с открытым исходным кодом. PHP сконструирован специально для ведения Web-разработок и может внедряться в HTML-код.

Простой ответ, но что он может означать? Вот пример:

Пример 1-1. Пример программирования на PHP

<html>
    <head>
        <title>Пример </title>
    </head>
    <body>

      <?php
      echo "Привет, я - скрипт PHP!";
      ?>

    </body>
</html>

Обратите внимание на отличие этого скрипта от скриптов, написанных на других языках, например, на Perl или C - вместо того, чтобы создавать программу, которая занимается формированием HTML-кода и содержит бесчисленное множество предназначенных для этого команд, вы создаете HTML-код с несколькими внедренными командами PHP (в приведенном случае, предназначенными для вывода текста). Код PHP отделяется специальными начальным и конечным тегами, которые позволяют процессору PHP определять начало и конец участка HTML-кода, содержащего PHP-скрипт.

Значительным отличием PHP от какого-либо кода, выполняющегося на стороне клиента, например, JavaScript, является то, что PHP-скрипты выполняются на сервере. Если бы у вас на сервере был размещен скрипт, подобный вышеприведенному, клиент получил бы только результат выполнения скрипта, причем он не смог бы выяснить, какой именно код выполняется. Вы даже можете сконфигурировать свой сервер таким образом, чтобы HTML-файлы обрабатывались процессором PHP, так что клиенты даже не смогут узнать, получают ли они обычный HTML-файл или результат выполнения скрипта.

PHP крайне прост для освоения, но вместе с тем способен удовлетворить запросы профессиональных программистов. После того, как вы впервые услышали о PHP, и открыли это руководство, в течение нескольких часов вы уже сможете создавать простые PHP-скрипты.

Хотя PHP, главным образом, предназначен для работы в среде web-серверов, область его применения не ограничивается только этим. Читайте дальше и не пропустите главу Возможности PHP.

Возможности PHP

PHP может все. Главным образом, область применения PHP сфокусирована на написание скриптов, работающих на стороне сервера; таким образом, PHP способен выполнять всё то, что выполняет любая другая программа CGI, например, обрабатывать данных форм, генерировать динамические страницы или отсылать и принимать cookies. Но PHP способен выполнять и множество других задач.

Существуют три основных области, где используется PHP.

PHP доступен для большинства операционных систем, включая Linux, многие модификации Unix (такие, как HP-UX, Solaris и OpenBSD), Microsoft Windows, Mac OS X, RISC OS, и многих других. (Совершенно точно, что существует версия PHP для OS/2. Неизвестно, правда, насколько соответствующая нынешним реалиям - Прим.перев.) Также в PHP включена поддержка большинства современных вебсерверов, таких, как Apache, Microsoft Internet Information Server, Personal Web Server, серверов Netscape и iPlanet, сервера Oreilly Website Pro, Caudium, Xitami, OmniHTTPd и многих других. Для большинства серверов PHP поставляется в качестве модуля, для других, поддерживающих стандарт CGI, PHP может функционировать в качестве процессора CGI.

Таким образом, выбирая PHP, вы получаете свободу выбора операционной системы и вебсервера. Кроме того, у вас появляется выбор между использованием процедурного или объектно-ориентированного программирования или же их сочетания. Несмотря на то, что текущая версия PHP поддерживает не все особенности ООП, многие библиотеки кода и большие приложения (включая библиотеку PEAR) написаны только с использованием ООП.

PHP способен не только выдавать HTML. Возможности PHP включают формирование изображений, файлов PDF и даже роликов Flash (с использованием libswf и Ming), создаваемых "на лету". PHP также способен выдавать любые текстовые данные, такие, как XHTML и другие XML-файлы. PHP способен осуществлять автоматическую генерацию таких файлов и сохранять их в файловой системе вашего сервера вместо того, чтобы отдавать клиенту, организуя, таким образом, кеш динамического содержания, расположенный на стороне сервера.

Одним из значительных преимуществ PHP является поддержка широкого круга баз данных. Создание скрипта, использующего базы данных, - невероятно просто. В настоящее время PHP поддерживает следующие базы данных:

Также в PHP включена поддержка DBX для работы на абстрактном уровне, так что вы можете работать с любой базой данных, использующих DBX. Кроме того, PHP поддерживает ODBC (Open Database Connection standard), таким образом, вы можете работать с любой базой данных, поддерживающей этот всемирно признанный стандарт.

PHP также поддерживает "общение" с другими сервисами с использованием таких протоколов, как LDAP, IMAP, SNMP, NNTP, POP3, HTTP, COM (на платформах Windows) и многих других. Кроме того, вы получаете возможность работать с сетевыми сокетами "напрямую". PHP поддерживает стандарт обмена сложными структурами данных WDDX. Обращая внимание на взаимодействие между различными языками, следует упомянуть о поддержке объектов Java и возможности их использования в качестве объектов PHP. Для доступа к удаленным объектам вы можете использовать расширение CORBA.

PHP включает средства обработки текстовой информации, начиная с регулярных выражений Perl или POSIX Extended и заканчивая парсером документов XML. Для парсинга XML используются стандарты SAX и DOM. Для преобразования документов XML вы можете использовать расширение XSLT.

Используя PHP в области электронной коммерции, вы обратите внимание на функции осуществления платежей Cybercash, CyberMUT, VeriSign Payflow Pro и CCVS.

Последним по порядку, но не по значению, является поддержка многих других расширений, таких, как функции поисковой машины mnoGoSearch, функции IRC Gateway, функции для работы со сжатыми файлами (gzip, bz2), функции календарных вычислений, функции перевода...

Как вы видите, этой страницы не хватит для того, чтобы перечислить все, что может предложить вам PHP. Читайте следующую главу, Установка PHP и обратитесь к главе Справочник по функциям за более подробными сведениями о перечисленных выше расширениях.

Глава 2. Краткое руководство

Содержание

Что потребуется?

Первая страница на PHP

Делаем что-нибудь полезное

Работа с формами

Использование старых программ с новыми версиями PHP

Что дальше?

В данном кратком руководстве на примерах объясняются основы PHP. Это руководство включает в себя только создание динамических Web-страниц с помощью PHP, однако реальная область применения PHP гораздо шире. В разделе "Что может PHP" приведена дополнительная информация.

Созданные с использованием PHP Web-страницы обрабатываются, как обычные HTML-страницы. Их можно создавать и изменять точно таким же образом, как и обычные страницы на HTML.

Что потребуется?

В данном руководстве мы предполагаем, что ваш сервер имеет поддержку PHP и что все файлы, заканчивающиеся на .php, обрабатываются PHP. В большинстве серверов это расширение используется для PHP по умолчанию, но все-таки не лишним будет уточнить это у вашего администратора сервера. Если ваш сервер поддерживает PHP, то у вас есть все, что требуется. Просто создавайте ваши файлы .php и размещайте их в вашем каталоге Web-сервера - они будут обрабатываться автоматически. Не нужно ничего компилировать, не нужно никаких дополнительных программ. Считайте файлы PHP обычными файлами HTML с набором новых "волшебных" тегов, которые позволяют вам делать все, что угодно.

Первая страница на PHP

Создайте файл с именем hello.php в корневом каталоге ваших документов Web-сервера и запишите в него следующее:

Пример 2-1. Первый скрипт на PHP: hello.php

<html>
<head>
  <title>Тестируем PHP</title>
</head>
<body>
<?php echo "Привет!<p>"; ?>
</body>
</html>

Эта программа выведет следующее:

<html>
 <head>
  <title>Тестируем PHP</title>
 </head>
 <body>
 Привет!<p>
 </body>
</html>

Заметим, что сходства со скриптами на CGI нет. Файл не обязан быть выполнимым или отмеченным любым другим образом. Это просто обычный файл HTML, в котором есть набор специальных тегов, делающих много интересного.

Эта программа чрезвычайно проста, и для создания настолько простой странички даже необязательно использовать PHP. Все что она делает - это выводит "Привет! с использованием функции PHP echo().

Если у вас этот пример не отображает ничего или выводит окно загрузки, или если вы видите весь этот файл в текстовом виде, то весьма вероятно, что ваш Web-сервер не имеет поддержки PHP. Попросите вашего администратора сервера включить такую поддержку. Предложите ему инструкцию по установке - раздел "Установка" данной документации. Если же вы хотите разрабатывать скрипты на PHP дома, то вам в раздел необходимые файлы. Дома можно разрабатывать скрипты с использованием любой операционной системы, но вам понадобится установить соответствующий Web-сервер.

Цель примера - показать формат специальных тегов PHP. В этом примере мы использовали <?php в качестве открывающего тега, затем шли команды PHP, завершающиеся закрывающим тегом ?>. Таким образом можно сколько угодно раз переходить к коду PHP в файле HTML.

Пара слов о текстовых редакторах: Существует множество текстовых редакторов и интегрированных сред разработки (IDE), в которых вы можете создавать и редактировать файлы PHP. Список некоторых редакторов содержится в разделе "Список редакторов PHP". Если вы хотите порекомендовать какой-либо редактор, посетите данную страницу и попросите добавить данный редактор в список.

Пара слов о текстовых процессорах: Текстовые процессоры (StarOffice Writer, Microsoft Word, Abiword и др.) в большинстве случаев не подходят для редактирования файлов PHP.

Если вы используете текстовый процессор для создания скриптов на PHP, вы должны быть уверены, что сохраняете файл, как ЧИСТО ТЕКСТОВЫЙ. В противном случае PHP не сможет обработать и выполнить вашу программу.

Пара слов о "Блокноте" Windows: При написании скриптов PHP с использованием встроенного "Блокнота" Windows необходимо сохранять файлы с расширением .php. "Блокнот" автоматически добавляет расширение .txt. Для обхода этой проблемы существует несколько методов.

Можно поместить название файла в кавычки (пример:"hello.php").

Кроме того, можно выбрать "Все файлы" вместо "Текстовые документы" из ниспадающего списка с типами файлов в окне сохранения. После этого можно вводить имя файла без кавычек.

Делаем что-нибудь полезное

Давайте сделаем что-нибудь полезное. К примеру, определим, какой браузер использует тот, кто смотрит в данный момент нашу страницу. Для этого мы проверим строку с именем браузера, посылаемую нам в HTTP-запросе. Эта информация хранится в переменной. Переменные в PHP всегда предваряются знаком доллара. Интересующая нас в данный момент переменная называется $_SERVER["HTTP_USER_AGENT"].

Пару слов об автоматической глобализации переменных в PHP: $_SERVER - специальная зарезервированная переменная PHP, которая содержит всю информацию, полученную от Web-сервера. Она является автоглобализованной (или суперглобальной). Для более подробной информации смотрите раздел "Суперглобальные переменные". Эти специальные переменные появились в PHP, начиная с версии 4.1.0. До этого использовались массивы $HTTP_*_VARS, такие, как $HTTP_SERVER_VARS. Эти массивы, несмотря на то, что они уже устарели, до сих пор существуют (см. замечания по старым программам).

Для вывода данной переменной мы сделаем так:

Пример 2-2. Вывод значения переменной (элемента массива)

<?php echo $_SERVER["HTTP_USER_AGENT"]; ?>

Пример вывода данной программы:

Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)

В PHP есть огромное количество типов переменных. В предыдущем примере мы печатали элемент массива. Массивы в PHP являются очень мощным средством.

$_SERVER - просто переменная, которая предоставлена вам языком PHP. Список таких переменных можно посмотреть в разделе "Зарезервированные переменные". А можно получить их полный список с помощью такой программы:

Пример 2-3. Показываем все стандартные переменные с помощью функции phpinfo()

<?php phpinfo(); ?>

Если открыть данный файл в браузере, вы увидите страничку с информацией о PHP, а также список всех доступных вам переменных.

Внутрь тегов PHP можно помещать множество команд и создавать кусочки кода, делающие гораздо большее, чем просто вывод на экран. К примеру, если мы хотим сделать проверку на Internet Explorer, мы можем поступить так:

Пример 2-4. Пример использования управляющих структур и функций

<?php
if (strstr($_SERVER["HTTP_USER_AGENT"], "MSIE")) {
    echo "Вы используете Internet Explorer<br />";
}
?>

Пример вывода данной программы:

You are using Internet Explorer<br />

Здесь мы показали несколько новых элементов. Во-первых, здесь есть команда if. Если вам знаком основной синтаксис языка C, то вы уже заметили что-то схожее. Если же вы не знаете C или подобного по синтаксису языка, то лучший вариант - взять какую-либо книжку по PHP и прочитать ее. Другой вариант - почитать раздел "Описание языка" данного руководства.

Во-вторых, здесь есть вызов функции strstr(). strstr() - встроенная в PHP функция, которая ищет одну строку в другой. В данном случае мы ищем строку "MSIE" в $_SERVER["HTTP_USER_AGENT"]. Если строка не найдена, эта функция возвращает FALSE, если найдена - TRUE. Если она вернет TRUE, то условие в if окажется истинным, и код в командных скобках ({ }) выполнится. В противном случае данный код не выполняется. Попробуйте создать аналогичные примеры с использованием команд if, else, и других функций, таких, как strtoupper() и strlen(). Также примеры содержатся во многих описаниях функций в данном руководстве.

Продемонстрируем, как можно входить в режим кода PHP и выходить из него прямо внутри кода:

Пример 2-5. Смешение режимов HTML и PHP

<?php
if (strstr($_SERVER["HTTP_USER_AGENT"], "MSIE")) {
?>
<h3>strstr вернул true</h3>
<center><b>Вы используете Internet Explorer</b></center>
<?php
} else {
?>
<h3>strstr вернул false</h3>
<center><b>Вы не используете Internet Explorer</b></center>
<?php
}
?>

Пример вывода данной программы:

<h3>strstr вернул true</h3>

<center><b>Вы используете Internet Explorer</b></center>

Вместо использования команды PHP echo для вывода, мы вышли из режима кода и послали содержимое HTML. Важный момент здесь - то, что логическая структура кода PHP при этом не теряется. Только одна HTML-часть будет послана клиенту в зависимости от результата функции strstr() (другими словами, в зависимости от того, найдена строка "MSIE" или нет).

Работа с формами

Одно из главнейших достоинств PHP - то, как он работает с формами HTML. Здесь основным является то, что каждый элемент формы автоматически станет доступен вашим программам на PHP. Для подробной информации об использовании форм в PHP читайте раздел " Переменные из внешних источников". Вот пример формы HTML:

Пример 2-6. Простейшая форма HTML

<form action="action.php" method="POST">
 Ваше имя: <input type="text" name="name" />
 Ваш возраст: <input type="text" name="age" />
 <input type="submit">
</form>

В этой форме нет ничего особенного. Это обычная форма HTML без каких-либо специальных тегов. Когда пользователь заполнит форму и нажмет кнопку отправки, будет вызвана страница action.php. В этом файле может быть что-то вроде:

Пример 2-7. Выводим данные нашей формы

Здравствуйте, <?php echo $_POST["name"]; ?>.
Вам <?php echo $_POST["age"]; ?> лет.

Пример вывода данной программы:

Здравствуйте, Сергей.
Вам 30 лет.

Принцип работы данного кода прост и понятен. Переменные The $_POST["name"] и $_POST["age"] автоматически установлены для вас средствами PHP. Ранее мы использовали переменную $_SERVER, здесь же мы точно также используем суперглобальную переменную $_POST, которая содержит все POST-данные. Заметим, что метод отправки нашей формы - POST. Если бы мы использовали метод GET, то информация нашей формы была бы в суперглобальной переменной $_GET. Также можно использовать переменную $_REQUEST, если источник данных не имеет значения. Эта переменная содержит смесь данных GET, POST, COOKIE и FILE. Также советуем взглянуть на описание функции import_request_variables().

Использование старых программ с новыми версиями PHP

Сейчас PHP является популярным языком сценариев (скриптов). Становится все больше и больше распространяемых кусочков кода, которые вы можете использовать в своих скриптах. В большинстве случаев разработчики PHP старались сохранить совместимость с предыдущими версиями так, что код, написанный для более старой версии будет идеально работать и с новыми версиями языка без каких-либо изменений. Однако случается так, что изменения все-таки необходимы.

Есть два важных изменения, которые влияют на старые программы:

Для дополнительной информации касательно изменений, связанных с переменными, смотрите раздел "Предопределенные переменные"и ссылки этого раздела.

Что дальше?

То, что вы узнали, поможет вам понять большую часть руководства и разобраться в большинстве приведенных примеров программ. Другие примеры находятся на различных сайтах, ссылки на которые можно найти на php.net: http://www.php.net/links.php.

Глава 3. Основы синтаксиса

Содержание

Вставка в HTML

Разделение инструкций

Комментарии

Вставка в HTML

Когда PHP обрабатывает файл, он просто передаёт его текст, пока не встретит один из специальных тегов, который сообщает ему о необходимости начать интерпретацию текста как кода PHP. Затем он выполняет весь найденный код до закрывающего тега, говорящего интерпретатору, что далее снова идет просто текст. Этот механизм позволяет вам внедрять PHP-код в HTML - все за пределами тегов PHP остается неизменным, тогда как внутри - интерпретируется как код.

Существует четыре набора тегов, которые могут быть использованы для обозначения PHP-кода. Из них только два (<?php. . .?> и <script language="php">. . .</script>) всегда доступны;другие могут быть включены или выключены в конфигурационном файле php.ini. Хотя короткие теги и теги в стиле ASP могут быть удобны, они не так переносимы, как длинные версии. Кроме того, если вы намереваетесь вставлять PHP-код в XML или XHTML, чтобы соответствовать XML, вам следует использовать форму <?php. . .?>.

Теги, поддерживаемые PHP:

Пример 5-1. Способы вставки в HTML

1.  <?php echo("если вы хотите работать с документами XHTML или XML, делайте так\n"); ?>

2.  <? echo ("это простейшая инструкция обработки SGML\n"); ?>
    <?= выражение ?> Это синоним для "<? echo выражение ?>"
   
3.  <script language="php">
        echo ("некоторые редакторы (например, FrontPage) не
              любят инструкции обработки");
    </script>

4.  <% echo ("Вы можете по выбору использовать теги в стиле ASP"); %>
    <%= $variable; # Это синоним для "<% echo . . ." %>

Первый способ, <?php. . .?>, наиболее предпочтительный, так как он позволяет использовать PHP в коде, соответствующем правилам XML, таком как XHTML.

Второй способ не всегда доступен. Короткие теги доступны только когда они включены. Это можно сделать, используя функцию short_tags() (только в PHP 3), включив установку short_open_tag в конфигурационном файле PHP, либо скомпилировав PHP с параметром --enable-short-tags для configure. Даже если оно включено по умолчанию в php.ini-dist, использование коротких тегов не рекомендуется.

Четвертый способ доступен только если теги в стиле ASP были включены, используя конфигурационную установку asp_tags.

Замечание: Поддержка тегов в стиле ASP была добавлена в версии 3.0.4.

Замечание: Следует избегать использования коротких тегов при разработке приложений или библиотек, предназначенных для распространения или размещения на PHP-серверах, не находящихся под вашим контролем, так как короткие теги могут не поддерживаться на целевом сервере. Для создания переносимого, совместимого кода, не используйте короткие теги.

Закрывающий тег блока PHP-кода включает сразу следующий за ним перевод строки, если он имеется. Кроме того, закрывающий тег автоматически подразумевает точку с запятой; вам не нужно заканчивать последнюю строку кода в блоке точкой с запятой. Закрывающий тег PHP-блока в конце файла не является обязательным.

PHP позволяет использовать такие структуры:

Пример 5-2. Профессиональная вставка

<?php
if ($expression) {
    ?>
    <strong>Это истина.</strong>
    <?php
} else {
    ?>
    <strong>Это ложь.</strong>
    <?php
}
?>

Этот код работает так, как ожидается, потому что когда PHP встречает закрывающие теги ?>, он просто выводит все, что он находит до следующего открывающего тега. Приведенный здесь пример конечно придуманный, но для вывода больших блоков текста выход из режима интерпретации PHP обычно более эффективен, чем отправка всего текста через echo(), print() или что-либо подобное.

Разделение инструкций

Инструкции разделяются также как и в C или Perl - каждое выражение заканчивается точкой с запятой.

Закрывающий тег (?>) также подразумевает конец инструкции, поэтому два следующих фрагмента кода эквиваленты:

<?php
    echo "Это тест";
?>

<?php echo "Это тест" ?>

Комментарии

PHP поддерживает комметарии в стиле 'C', 'C++' и оболочки Unix. Например:

<?php
    echo "Это тест"; // Это однострочный комментарий в стиле c++
    /* Это многострочный комментарий
       еще одна строка комментария */
    echo "Это еще один тест";
    echo "Последний тест"; # Это комментарий в стиле оболочки Unix
?>

Однострочные комментарии идут только до конца строки или текущего блока PHP-кода, в зависимости от того, что идет перед ними.

<h1>Это <?php # echo "простой";?> пример.</h1>
<p>Заголовок вверху выведет 'Это пример'.

Будьте внимательны, следите за отсутствием вложенных 'C'-комментариев, они могут появиться во время комментирования больших блоков.

<?php
/*
    echo "Это тест"; /* Этот комментарий вызовет проблему */
*/
?>

Однострочные комментарии идут только до конца строки или текущего блока PHP-кода, в зависимости от того, что идет перед ними. Это означает, что HTML-код после // ?>БУДЕТ напечатан: ?> выводит из режима PHP и возвращает в режим HTML, но // не позволяет этого сделать. Если включена конфигурационная директива asp_tags, то же самое происходит и при // %>.

Глава 4. Типы

Содержание

Введение

Булев

Целые

Числа с плавающей точкой

Строки

Массивы

Объекты

Ресурс

NULL

Псевдо-типы, используемые в этой документации

Манипуляции с типами

Введение

PHP поддерживает восемь простых типов.

Четыре скалярных типа:

Два смешанных типа:

И, наконец, два специальных типа:

Для удобства понимания в этом руководстве используется также несколько псевдо-типов:

Вы также можете найти несколько упоминаний типа двойной точности. Рассматривайте его как число с плавающей точкой, два имени существуют только по историческим причинам.

Как правило, программист не устанавливает тип переменной;предпочтительнее, чтобы это делал PHP во время выполнения программы в зависимости от контекста, в котором используется переменная.

Замечание: Если вы желаете проверить тип и значение определенного выражения, используйте var_dump().

Замечание: Если же вам для отладки необходимо просто удобочитаемое представление типа, используйте gettype(). Чтобы проверить на определенный тип, не используйте gettype(), применяйте для этого is_type функции. Вот несколько примеров:

<?php
$bool = TRUE;   // логический
$str  = "foo";  // строковый
$int  = 12;     // целочисленный

echo gettype($bool); // выводит "boolean"
echo gettype($str);  // выводит "string"

// Если это целое, увеличить на четыре
if (is_int($int)) {
    $int += 4;
}

// Если $bool - это строка, вывести ее
// (ничего не выводит)
if (is_string($bool)) {
    echo "Строка: $bool";
}
?>

Если вы хотите принудительно изменить тип переменной, вы можете либо привести переменную, либо использовать функцию settype().

Обратите внимание, что переменная, в зависимости от ее типа в данный момент, в определенных ситуациях может иметь разные значения. Более подробную информацию смотрите в разделе Манипуляции с типами. Также вам, возможно, будет интересно посмотреть таблицы сравнения типов, поскольку в них приведены примеры связанных сравнений различных типов.

Булев

Это простейший тип. Он выражает истинность значения - это может быть либо TRUE, либо FALSE.

Замечание: Булев тип был введен в PHP 4.

Синтаксис

Чтобы определить булев тип, используйте ключевое слово TRUE или FALSE. Оба регистро-независимы.

<?php
$foo = True; // присвоить $foo значение TRUE
?>

Обычно вы используете некий оператор, который возвращает логическое выражение, а затем предает его управляющей конструкции.

<?php
// == это оператор, который проверяет
// эквивалентность и возвращает булево значение
if ($action == "показать_версию") {
    echo "Версия 1.23";
}

// это не обязательно...
if ($show_separators == TRUE) {
    echo "<hr>\n";
}

// ...потому что вы можете просто написать
if ($show_separators) {
    echo "<hr>\n";
}
?>

Преобразование в булев тип

Для несомненного преобразования значения в булев тип используйте приведение типа (bool) или (boolean). Однако в большинстве случаев вам нет необходимости использовать приведение типа, поскольку значение будет автоматически преобразовано, если оператор, функция или управляющая конструкция требует булев аргумент.

Смотрите также Манипуляции с типами.

При преобразовании в логический тип, следующие значения рассматриваются как FALSE:

Все остальные значения рассматриваются как TRUE (включая любой ресурс).

Внимание

-1 считается TRUE, как и любое ненулевое (отрицательное или положительное) число!

<?php
echo gettype((bool) "");        // bool(false)
echo gettype((bool) 1);         // bool(true)
echo gettype((bool) -2);        // bool(true)
echo gettype((bool) "foo");     // bool(true)
echo gettype((bool) 2.3e5);     // bool(true)
echo gettype((bool) array(12)); // bool(true)
echo gettype((bool) array());   // bool(false)
?>

Целые

Целое это число из множества Z = {..., -2, -1, 0, 1, 2, ...}.

Смотрите также: Целые произвольной длины / GMP, Числа с плавающей точкой и Произвольная точность / BCMath

Синтаксис

Целые могут быть указаны в десятичной, шестнадцатеричной или восьмеричной системе счисления, по желанию с предшествующим знаком (- или +).

Если вы используете восьмеричную систему счисления, вы должны предварить число 0 (нулем), для использования шестнадцатеричной системы нужно поставить перед числом 0x.

Пример 6-1. Целые

<?php
$a = 1234; # десятичное число
$a = -123; # отрицательное число
$a = 0123; # восьмеричное число (эквивалентно 83 в десятичной системе)
$a = 0x1A; # шестнадцатеричное число (эквивалентно 26 в десятичной системе)
?>

Формально возможная структура целых такова:

десятичные        : [1-9][0-9]*
                  | 0
 
шестнадцатеричные : 0[xX][0-9a-fA-F]+
 
восьмеричные      : 0[0-7]+
 
целые             : [+-]?десятичные
                  | [+-]?шестнадцатеричные
                  | [+-]?восьмеричные

Размер целого зависит от платформы, хотя, как правило, максимальное значение около двух миллиардов (это 32-битное знаковое). PHP не поддерживает беззнаковые целые.

Превышение размера целого

Если вы определите число, превышающее пределы целого типа, оно будет интерпретировано как число с плавающей точкой. Также, если вы используете оператор, результатом работы которого будет число, превышающее пределы целого, вместо него будет возвращено число с плавающей точкой.

<?php
$large_number 2147483647;
var_dump($large_number);
// вывод: int(2147483647)

$large_number 2147483648;
var_dump($large_number);
// вывод: float(2147483648)

// это справедливо и для шестнадцатеричных целых:
var_dump( 0x80000000 );
// вывод: float(2147483648)

$million = 1000000;
$large_number 50000 * $million;
var_dump($large_number);
// вывод: float(50000000000)
?>

Внимание

К сожалению, в PHP была ошибка, так что это не всегда верно работает, когда используются отрицательные числа. Например: когда вы умножаете -50000 * $million, результатом будет -429496728. Однако, если оба операнда положительны, проблем не возникает.

Эта ошибка устранена в PHP 4.1.0.

в PHP не существует оператора деления целых. Результатом 1/2 будет число с плавающей точкой 0.5. Вы можете привести значение к целому, что всегда округляет его в меньшую сторону, либо использовать функцию round().

<?php
var_dump(25/7);         // float(3.5714285714286)
var_dump((int) (25/7)); // int(3)
var_dump(round(25/7));  // float(4)
?>

Преобразование в целое

Для несомненного преобразования значения в целое используйте приведение типа (int) или (integer). Однако в большинстве случаев вам нет необходимости использовать приведение типа, поскольку значение будет автоматически преобразовано, если оператор, функция или управляющая конструкция требует целый аргумент. Вы также можете преобразовать значение в целое при помощи функции intval().

Смотрите также Манипуляции с типами.

Из булева типа

FALSE преобразуется в 0 (ноль), а TRUE - в 1 (единицу).

Из чисел с плавающей точкой

При преобразовании из числа с плавающей точкой в целое, число будет округлено в сторону нуля.

Если число с плавающей точкой превышает пределы целого (как правило, это +/- 2.15e+9 = 2^31), результат будет неопределенным, так как целое не имеет достаточной точности, чтобы вернуть верный результат. В этом случае не будет выведено ни предупреждения, ни даже замечания!

Внимание

Никогда не приводите неизвестную дробь к целому, так как это может иногда дать неожиданные результаты.

<?php
echo (int) ( (0.1+0.7) * 10 ); // выводит 7!
?>

Смотрите более подробно: предупреждение о точности чисел с плавающей точкой.

Из строк

Смотрите Преобразование строк в числа

Из других типов

Предостережение

Для других типов поведение преобразования в целое не определено. В настоящее время поведение такое же, как если бы значение сперва было преобразовано в булев тип. Однако не полагайтесь на это поведение, так как он может измениться без предупреждения.

Числа с плавающей точкой

Числа с плавающей точкой (они же числа двойной точности или действительные числа) могут быть определены при помощи любого из следующих синтаксисов:

<?php
$a = 1.234;
$b = 1.2e3;
$c = 7E-10;
?>

Формально:

LNUM          [0-9]+
DNUM          ([0-9]*[\.]{LNUM}) | ({LNUM}[\.][0-9]*)
EXPONENT_DNUM ( ({LNUM} | {DNUM}) [eE][+-]? {LNUM})

Размер целого зависит от платформы, хотя максимум, как правило, ~1.8e308 с точностью около 14 десятичных цифр (это 64-битный IEEE-формат).

Точность числа с плавающей точкой

Довольно часто простые десятичные дроби вроде 0.1 или 0.7 не могут быть преобразованы в свои внутренние двоичные аналоги без небольшой потери точности. Это может привести к неожиданным результатам: например, floor((0.1+0.7)*10) скорее всего возвратит 7 вместо ожидаемой 8 как результат внутреннего представления числа, являющегося в действительности чем-то вроде 7.9999999999....

Это связано с невозможностью точно выразить некоторые дроби в десятичной системе счисления конечным числом цифр. Например, 1/3 в десятичной форме принимает вид 0.3333333. . ..

Так что никогда не доверяйте точности последних цифр в результатах с числами с плавающей точкой и никогда не проверяйте их на равенство. Если вам действительно необходима высокая точность, вам следует использовать математические функции произвольной точности или gmp-функции.

Преобразование в число с плавающей точкой

О том, когда и как строки преобразуются в числа с плавающей точкой читайте в разделе Преобразование строк в числа. Для значений других типов преобразование будет таким же, как если бы значение сначала было преобразовано в целое, а затем в число с плавающей точкой. Дополнительную информацию смотрите в разделе Преобразование в целое.

Строки

Строка - это набор символов. В PHP символ это то же самое, что и байт, это значит, что возможно ровно 256 различных символов. Это также означает, что PHP не имеет встроенной поддержки Unicode'а. Некоторую поддержку Unicode'а обеспечивают функции utf8_encode() и utf8_decode().

Замечание: Нет никаких проблем, если строка очень велика. Практически не существует ограничений на размер строк, налагаемых PHP, так что нет абсолютно никаких причин беспокоиться об их длине.

Синтаксис

Строка может быть определена тремя различными способами.

  • одинарными кавычками
  • двойными кавычками
  • heredoc-синтаксисом

Одинарные кавычки

Простейший способ определить строку - это заключить ее в одинарные кавычки (символ ').

Чтобы использовать одинарную кавычку внутри строки, как и во многих других языках, ее необходимо предварить символом обратной косой черты (\), т. е. экранировать ее. Если обратная косая черта должна идти перед одинарной кавычкой либо быть в конце строки, вам необходимо продублировать ее. Обратите внимание, что если вы попытаетесь экранировать любой другой символ, обратная косая черта также будет напечатана! Так что, как правило, нет необходимости экранировать саму обратную косую черту.

Замечание: В PHP 3 в данном случае будет выдано сообщение уровня E_NOTICE.

Замечание: В отличие от двух других синтаксисов, переменные и экранирующие последовательности для специальных символов, встречающиеся в строках, заключенных в одинарные кавычки, не обрабатываются.

<?php
echo 'это простая строка';

echo 'Также вы можете вставлять в строки
символ новой строки таким образом,
поскольку это нормально';

// Выведет: Однажды Арнольд сказал: "I'll be back"
echo 'Однажды Арнольд сказал: "I\'ll be back"';

// Выведет: Вы удалили C:\*.*?
echo 'Вы удалили C:\\*.*?';

// Выведет: Вы удалили C:\*.*?
echo 'Вы удалили C:\*.*?';

// Выведет: Это не вставит: \n новую строку
echo 'Это не вставит: \n новую строку';

// Выведет: Переменные $expand также $either не подставляются
echo 'Переменные $expand также $either не подставляются';
?>
Двойные кавычки

Если строка заключена в двойные кавычки ("), PHP распознает большее количество управляющих последовательностей для специальных символов:

Таблица 6-1. Управляющие последовательности

последовательность

значение

\n

новая строка (LF или 0x0A (10) в ASCII)

\r

возврат каретки (CR или 0x0D (13) в ASCII)

\t

горизонтальная табуляция (HT или 0x09 (9) в ASCII)

\\

обратная косая черта

\$

знак доллара

\"

двойная кавычка

\[0-7]{1,3}

последовательность символов, соответствующая регулярному выражению, символ в восьмеричной системе счисления

\x[0-9A-Fa-f]{1,2}

последовательность символов, соответствующая регулярному выражению, символ в шестнадцатеричной системе счисления

Повторяем, если вы захотите мнемнонизировать любой другой символ, обратная косая черта также будет напечатана!

Но самым важным свойством строк в двойных кавычках является обработка переменных. Смотрите более подробно: обработка строк.

Heredoc

Другой способ определения строк - это использование heredoc-синтаксиса ("<<<"). После <<< необходимо указать идентификатор, затем идет строка, а потом этот же идентификатор, закрывающий вставку.

Закрывающий идентификатор должен начинаться в первом столбце строки. Кроме того, идентификатор должен соответствовать тем же правилам именования, что и все остальные метки в PHP: содержать только буквенно-цифровые символы и знак подчеркивания, и должен начинаться с нецифры или знака подчеркивания.

Внимание

Очень важно отметить, что строка с закрывающим идентификатором не содержит других символов, за исключением, возможно, точки с запятой (;). Это означает, что идентификатор не должен вводиться с отступом и что не может быть никаких пробелов или знаков табуляции до или после точки с запятой. Важно также понимать, что первым символом перед закрывающим идентификатором должен быть символ новой строки, определенный в вашей операционной системе. Например, на Macintosh это \r.

Если это правило нарушено и закрывающий идентификатор не является "чистым", считается, что закрывающий идентификатор отсутствует и PHP продолжит его поиск дальше. Если в этом случае верный закрывающий идентификатор так и не будет найден, то это вызовет ошибку в обработке с номером строки в конце скрипта.

Heredoc-текст ведет себя так же, как и строка в двойных кавычках, при этом их не имея. Это означает, что вам нет необходимости экранировать кавычки в heredoc, но вы по-прежнему можете использовать вышеперечисленные управляющие последовательности. Переменные обрабатываются, но с применением сложных переменных внутри heredoc нужно быть также внимательным, как и при работе со строками.

Пример 6-2. Пример определения heredoc-строки

<?php
$str = <<<EOD
Пример строки,
охватывающей несколько строчек,
с использованием heredoc-синтаксиса.
EOD;
/* Более сложный пример с переменными. */
class foo
{
    var $foo;
    var $bar;

    function foo()
    {
        $this->foo = 'Foo';
        $this->bar = array('Bar1', 'Bar2', 'Bar3');
    }
}

$foo = new foo();
$name = 'МоеИмя';

echo <<<EOT
Меня зовут "$name". Я печатаю $foo->foo.
Теперь я вывожу {$foo->bar[1]}.
Это должно вывести заглавную букву 'A': \x41
EOT;?>

Замечание: Поддержка heredoc была добавлена в PHP 4.

Обработка переменных

Если строка определяется в двойных кавычках, либо при помощи heredoc, переменные внутри нее обрабатываются.

Существует два типа синтаксиса: простой и сложный. Простой синтаксис более легок и удобен. Он дает возможность обработки переменной, значения массива (array) или свойства объекта (object).

Сложный синтаксис был введен в PHP 4 и может быть распознан по фигурным скобкам, окружающих выражение.

Простой синтаксис

Если интерпретатор встречает знак доллара ($), он захватывает так много символов, сколько возможно, чтобы сформировать правильное имя переменной. Если вы хотите точно определить конец имени, заключайте имя переменной в фигурные скобки.

<?php
$beer = 'Heineken';
echo "$beer's taste is great"; // работает, "'" это неверный символ для имени переменной
echo "He drank some $beers";   // не работает, 's' это верный символ для имени переменной
echo "He drank some ${beer}s"; // работает
echo "He drank some {$beer}s"; // работает
?>

Точно также могут быть обработаны элемент массива (array) или свойство объекта (object). В индексах массива закрывающая квадратная скобка (]) обозначает конец определения индекса. Для свойств объекта применяются те же правила, что и для простых переменных, хотя с ними невозможен трюк, как с переменными.

<?php
// Эти примеры специфически об использовании массивов внутри
// строк. Вне строк всегда заключайте строковые ключи вашего
// массива в кавычки и не используйте вне строк {скобки}.

// Давайте покажем все ошибки
error_reporting(E_ALL);

$fruits = array('strawberry' => 'red', 'banana' => 'yellow');

// Работает, но заметьте, что вне кавычек строки это работает по-другому
echo "A banana is $fruits[banana].";

//Работает
echo "A banana is {$fruits['banana']}.";

// Работает, но PHP, как описано ниже, сначала ищет
// константу banana.
echo "A banana is {$fruits[banana]}.";


// Не работает, используйте фигурные скобки. Это вызовет ошибку обработки.
echo "A banana is $fruits['banana'].";

// Работает
echo "A banana is " . $fruits['banana'] . ".";

// Работает
echo "This square is $square->width meters broad.";

// Не работает. Для решения см. сложный синтаксис.
echo "This square is $square->width00 centimeters broad.";
?>

Для чего-либо более сложного вы должны использовать сложный синтаксис.

Сложный (фигурный) синтаксис

Он называется сложным не потому, что труден в понимании, а потому что позволяет использовать сложные выражения.

Фактически, вы можете включить любое значение, находящееся в пространстве имени в строке с этим синтаксисом. Вы просто записываете выражение таким же образом, как и вне строки, а затем заключаете его в { и }. Поскольку вы не можете экранировать '{', этот синтаксис будет распознаваться только когда $ следует непосредственно за {. (Используйте "{\$" или "\{$" чтобы отобразить "{$"). Несколько поясняющих примеров:

<?php
// Давайте покажем все ошибки
error_reporting(E_ALL);

$great = 'fantastic';

// Не работает, выведет: This is { fantastic}
echo "This is { $great}";

// Работает, выведет: This is fantastic
echo "This is {$great}";
echo "This is ${great}";

// Работает
echo "Этот квадрат шириной {$square->width}00 сантиметров.";

//
Работает
echo "Это работает: {$arr[4][3]}";

// Это неверно по той же причине, что и $foo[bar] неверно вне
// строки. Другими словами, это по-прежнему будет работать,
// но поскольку PHP сначала ищет константу foo, это вызовет
// ошибку уровня E_NOTICE (неопределенная константа).
echo "Это неправильно: {$arr[foo][3]}";

// Работает. При использовании многомерных массивов, внутри
// строк всегда используйте фигурные скобки
echo "Это работает: {$arr['foo'][3]}";

// Работает.
echo "Это работает: " . $arr['foo'][3];

echo "Вы даже можете записать {$obj->values[3]->name}";

echo "Это значение переменной по имени $name: {${$name}}";
?>

Доступ к символу в строке и его изменение

Символы в строках можно использовать и модифицировать, определив их смещение относительно начала строки, начиная с нуля, в фигурных скобках после строки.

Замечание: Для обеспечения обратной совместимости, вы по-прежнему имеете возможность использовать в тех же целях скобки массива. Однако, начиная с PHP 4, этот синтаксис нежелателен к использованию.

Пример 6-3. Несколько примеров строк

<?php
// Получение первого символа строки
$str = 'Это тест.';
$
first = $str{0};

// Получение третьего символа строки
$third = $str{2};

// Получение последнего символа строки
$str = 'Это все еще тест.';
$last = $str{strlen($str)-1};

// Изменение последнего символа строки
$str = 'Посмотри на море';
$str{strlen($str)-1} = 'я';

?>

Полезные функции и операторы

Строки могут быть объединены при помощи оператора '.' (точка). Обратите внимание, оператор сложения '+' здесь не работает. Дополнительную информацию смотрите в разделе Строковые операторы.

Для модификации строк существует множество полезных функций.

Основные функции описаны в разделе строковых функций, функции регулярных выражений для расширенного поиска и замены (в двух частях: Perl и POSIX расширенный).

Также существуют функции для URL-строк, и функции для шифрования/дешифрования строк (mcrypt и mhash).

Наконец, если вы все еще не нашли, что искали, смотрите также функции для символьного типа.

Преобразование в строку

Вы можете преобразовать значение в строку, используя приведение (string), либо функцию strval(). В выражениях, где необходима строка, преобразование происходит автоматически. Это происходит, когда вы используете функции echo() или print(), либо когда вы сравниваете значение переменной со строкой. Прочтение разделов руководства Типы и Манипуляции с типами сделает следующее более понятным. Смотрите также settype().

Булево (boolean) значение TRUE преобразуется в строку "1", а значение FALSE представляется как "" (пустая строка). Этим способом вы можете преобразовывать значения в обе стороны - из булева типа в строковый и наоборот.

Целое (integer) или число с плавающей точкой (float) преобразуется в строку, представленную числом, состоящим из его цифр (включая показатель степени для чисел с плавающей точкой).

Массивы всегда преобразуются в строку "Array", так что вы не можете отобразить содержимое массива (array), используя echo() или print(), чтобы узнать, что он содержит. Чтобы просмотреть один элемент, вам нужно сделать что-то вроде echo $arr['foo']. Смотрите ниже советы о том, как отобразить/просмотреть все содержимое.

Объекты всегда преобразуются в строку "Object". Если вы хотите вывести значение переменной-члена объекта (object) с целью отладки, прочтите следующие абзацы. Если вы хотите получить имя класса требуемого объекта, используйте get_class().

Ресурсы всегда преобразуются в строки со структурой "Resource id #1", где 1 - это уникальный номер ресурса (resource), присвоенный ему PHP во время выполнения. Если вы хотите получить тип ресурса, используйте get_resource_type().

NULL всегда преобразуется в пустую строку.

Как вы могли видеть выше, вывод массивов, объектов или ресурсов не предоставляет вам никакой полезной информации о самих значениях. Более подходящий способ вывода значений для отладки - использовать функции print_r() и var_dump().

Вы также можете преобразовать значения PHP в строки для постоянного хранения. Этот метод называется сериализацией и может быть выполнен при помощи функции serialize(). Кроме того, если в вашей установке PHP есть поддержка WDDX, вы можете сериализовать значения PHP в структуры XML.

Преобразование строк в числа

Если строка распознается как числовое значение, результирующее значение и тип определяется так как показано далее.

Строка будет распознана как float, если она содержит любой из символов '.', 'e', или 'E'. Иначе она будет определена как целое.

Значение определяется по начальной части строки. Если строка начинается с верного числового значения, будет использовано это значение. Иначе значением будет 0 (ноль). Верное числовое значение - это одна или более цифр (могущих содержать десятичную точку), по желанию предваренных знаком, с последующим необязательным показателем степени. Показатель степени - это 'e' или 'E' с последующими одной или более цифрами.

<?php
$foo = 1 + "10.5";                // $foo это float (11.5)
$foo = 1 + "-1.3e3";              // $foo это float (-1299)
$foo = 1 + "bob-1.3e3";           // $foo это integer (1)
$foo = 1 + "bob3";                // $foo это integer (1)
$foo = 1 + "10 Small Pigs";       // $foo это integer (11)
$foo = 4 + "10.2 Little Piggies"; // $foo это float (14.2)
$foo = "10.0 pigs " + 1;          // $foo это float (11)
$foo = "10.0 pigs " + 1.0;        // $foo это float (11)     
?>

Более подробную информацию об этом преобразовании смотрите в разделе о strtod(3) документации Unix.

Если вы хотите протестировать любой из примеров этого раздела, вы можете скопировать и вставить его и следующую строку, чтобы увидеть, что происходит:

<?php
echo "\$foo==$foo; тип: " . gettype ($foo) . "<br />\n";
?>

Не ожидайте получить код символа, преобразовав его в целое (как вы могли бы сделать, например, в C). Для преобразования символов в их коды и обратно используйте функции ord() и chr().

Массивы

На самом деле массив в PHP - это упорядоченная карта. Карта - это тип, который вносит значения в ключи. Этот тип оптимизирован в нескольких направлениях, поэтому вы можете использовать его как собственно массив, список (вектор), хэш-таблицу (являющуюся реализацией карты), словарь, коллекцию, стэк, очередь или, возможно, как что-то еще. Поскольку вы можете иметь в качестве значения другой массив PHP, вы можете также легко эмулировать деревья.

Объяснение этих структур данных выходит за рамки данного справочного руководства, но вы найдете как минимум один пример каждой из них. За дополнительной информацией вы можете обратиться к соответствующей литературе по этой обширной теме.

Синтаксис

Определение при помощи array()

Массив может быть создан языковой конструкцией array(). В качестве параметров она принимает определенное количество разделенных запятыми пар key => value (ключ => значение).

array( [key =>] value
     , ...
     )
// key может быть integer или string
// value может быть любым значением

<?php
$arr = array("foo" => "bar", 12 => true);

echo $arr["foo"]; // bar
echo $arr[12];    // 1
?>

key может быть либо integer, либо string. Если ключ - это стандартное представление integer, он так и будет интерпретироваться (т.е. "8" будет восприниматься как 8, тогда как "08" будет интерпретироваться как "08"). В PHP нет разницы между индексными и ассоциативными массивами; существует только один тип массива, который может содержать и числовые, и строковые индексы.

Значение может быть любого имеющегося в PHP типа.

<?php
$arr = array("somearray" => array(6 => 5, 13 => 9, "a" => 42));

echo $arr["somearray"][6];    // 5
echo $arr["somearray"][13];   // 9
echo $arr["somearray"]["a"];  // 42
?>

Если вы не указываете ключ для приведенного значения, то берется максимальный числовой индекс и новый ключ будет равен этому максимуму + 1. Если вы укажите ключ, которому уже присвоено значение, оно будет перезаписано.

<?php
// Этот массив эквивалентен ...
array(5 => 43, 32, 56, "b" => 12);

// ...этому массиву
array(5 => 43, 6 => 32, 7 => 56, "b" => 12);
?>

Внимание

Начиная с PHP 4.3.0, вышеописанное поведение генерации индекса изменено. Теперь, если вы будете использовать массив, в котором максимальным в настоящий момент является отрицательный ключ, то следующий созданный ключ будет нулевым (0). Раньше новым индексом становился самый большой существующий ключ + 1, так же как и у положительных индексов.

Используя в качестве ключа TRUE вы получите ключ 1 типа integer. Используя в качестве ключа FALSE вы получите ключ 0 типа integer. Используя в качестве ключа NULL, вы получите пустую строку. Использование в качестве ключа пустой строки создаст (или перезапишет) ключ с пустой строкой и его значение; это не то же самое, что использование пустых квадратных скобок.

Вы не можете использовать в качестве ключей массивы или объекты. Это вызовет предупреждение: Illegal offset type ('Недопустимый тип смещения').

Создание/модификация с помощью синтаксиса квадратных скобок

Также вы можете изменять существующий массив, явно устанавливая значения в нем.

Это выполняется присвоением значений массиву при указании в скобках ключа. Кроме того, вы можете опустить ключ, в этом случае добавьте к имени переменной пустую пару скобок ("[]").

$arr[key] = value;
$arr[] = value;
// key может быть integer или string
// value может быть любым значением

Если массив $arr еще не существует, он будет создан. Таким образом, это еще один способ определить массив. Для изменения определенного значения просто присвойте элементу с его ключом новое значение. Если вы хотите удалить пару ключ/значение, вам нужно использовать функцию unset().

<?php
$arr = array(5 => 1, 12 => 2);

$arr[] = 56;    // В этом месте скрипта это
                // эквивалентно $arr[13] = 56;

$arr["x"] = 42; // Это добавляет к массиву новый
                // элемент с ключом "x"
               
unset($arr[5]); // Это удаляет элемент из массива

unset($arr);    // Это удаляет массив полностью
?>

Замечание: Как уже говорилось выше, если вы не укажите в скобках ключа, то будет взят максимальный из существующих целочисленных индексов, и новым ключом будет это максимальное значение + 1. Если целочисленных индексов еще нет, то ключом будет 0 (ноль). Если вы укажите ключ, которому уже присвоено значение, оно будет перезаписано.

Внимание

Начиная с PHP 4.3.0, вышеописанное поведение генерации индекса изменено. Теперь, если вы будете использовать массив, в котором максимальным в настоящий момент является отрицательный ключ, то следующий созданный ключ будет нулевым (0). Раньше новым индексом становился самый большой существующий ключ + 1, так же как и у положительных индексов.

Обратите внимание, что максимальный числовой ключ, используемый для этого не обязательно должен существовать в массиве в настоящий момент. Он просто должен был существовать с момента последнего переиндексирования массива. Это иллюстрирует следующий пример:

<?php
// Создаем простой массив.
$array = array(1, 2, 3, 4, 5);
print_r($array);

// Теперь удаляем каждый элемент, но сам массив оставляем нетронутым:
foreach ($array as $i => $value) {
    unset($array[$i]);
}
print_r($array);

// Создаем элемент (обратите внимание, что новым ключом будет 5,
// а не 0, как вы возможно ожидали).
$array[] = 6;
print_r($array);

// Переиндексация:
$array = array_values($array);
$array[] = 7;
print_r($array);
?>

Вышеприведенный пример выведет следующее:

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
)
Array
(
)
Array
(
    [5] => 6
)
Array
(
    [0] => 6
    [1] => 7
)

Полезные функции

Для работы с массивами существует достаточное количество полезных функций. Смотрите раздел функции для работы с массивами.

Замечание: Функция unset() позволяет удалять ключи массива. Обратите внимание, что массив НЕ будет переиндексирован. Если вы использовали только "обычные числовые индексы" (увеличивающиеся на единицу, начиная с нуля), вы можете переиндексировать массив используя array_values().

<?php
$a = array(1 => 'один', 2 => 'два', 3 => 'три');
unset($a[2]);
/* даст массив, представленный так:
   $a = array(1 => 'один', 3 => 'три');
   а НЕ так:
   $a = array(1 => 'один', 2 =>'три');
*/

$b = array_values($a);
// Теперь $b это array(0 => 'один', 1 =>'три')
?>

Управляющая конструкция foreach существует специально для массивов. Она предоставляет возможность легко пройтись по массиву.

Массив делает и не делает

Почему $foo[bar] это неверно?

Вы всегда должны заключать индекс ассоциативного массива в кавычки. К примеру, пишите $foo['bar'], а не $foo[bar]. Но почему $foo[bar] это неверно? Возможно, вы встречали в старых скриптах следующий синтаксис:

<?php
$foo[bar] = 'враг';
echo $foo[bar];
// и т. д.
?>

Это неверно, хотя и работает. Тогда почему же это неверно? Причина в том, что этот код содержит неопределенную константу (bar), а не строку ('bar' - обратите внимание на кавычки), и PHP в будущем может определить константу, которая к несчастью для вашего кода будет иметь то же самое имя. Это работает, потому что PHP автоматически преобразует голую строку (не заключенную в кавычки строку, которая не соответствует ни одному из известных символов) в строку, которая содержит голую строку. Например, если константа с именем bar не определена, то PHP заменит bar на строку 'bar' и использует ее.

Замечание: Это не означает, что нужно всегда заключать ключ в кавычки. Нет необходимости заключать в кавычки константы или переменные, поскольку это помешает PHP обрабатывать их.

<?php
error_reporting(E_ALL);
ini_set('display_errors', true);
ini_set('html_errors', false);
// Простой массив:
$array = array(1, 2);
$count = count($array);
for ($i = 0; $i < $count; $i++) {
    echo "\nПроверяем $i: \n";
    echo "Плохо: " . $array['$i'] . "\n";
    echo "Хорошо: " . $array[$i] . "\n";
    echo "Плохо: {$array['$i']}\n";
    echo "Хорошо: {$array[$i]}\n";
}
?>

Замечание: Вышеприведенный код выведет следующее:

Проверяем 0: 
Notice: Undefined index:  $i in /path/to/script.html on line 9
Плохо: 
Хорошо: 1
Notice: Undefined index:  $i in /path/to/script.html on line 11
Плохо: 
Хорошо: 1
 
Проверяем 1: 
Notice: Undefined index:  $i in /path/to/script.html on line 9
Плохо: 
Хорошо: 2
Notice: Undefined index:  $i in /path/to/script.html on line 11
Плохо: 
Хорошо: 2

Дополнительные примеры, демонстрирующие этот факт:

<?php
// Давайте покажем все ошибки
error_reporting(E_ALL);

$arr = array('fruit' => 'apple', 'veggie' => 'carrot');

// Верно
print $arr['fruit'];  // apple
print $arr['veggie']; // carrot

// Неверно. Это работает, но из-за неопределенной константы с
// именем fruit также вызывает ошибку PHP уровня E_NOTICE
//
// Notice: Use of undefined constant fruit - assumed 'fruit' in...
print $arr[fruit];    // apple

// Давайте определим константу, чтобы продемонстрировать, что
// происходит. Мы присвоим константе с именем fruit значение 'veggie'.
define('fruit', 'veggie');

// Теперь обратите внимание на разницу
print $arr['fruit'];  // apple
print $arr[fruit];    // carrot

// Внутри строки это нормально. Внутри строк константы не
// рассматриваются, так что ошибки E_NOTICE здесь не произойдет
print "Hello $arr[fruit]";      // Hello apple

// С одним исключением: фигурные скобки вокруг массивов внутри
// строк позволяют константам находится там
print "Hello {$arr[fruit]}";    // Hello carrot
print "Hello {$arr['fruit']}"// Hello apple

// Это не будет работать и вызовет ошибку обработки, такую как:
// Parse error: parse error, expecting T_STRING' or T_VARIABLE' or T_NUM_STRING'
// Это, конечно, также приложимо и к использованию в строках автоглобальных переменных
print "Hello $arr['fruit']";
print "Hello $_GET['foo']";

// Еще одна возможность - конкатенация
print "Hello " . $arr['fruit']; // Hello apple
?>

Когда вы переведете error_reporting() в режим отображения ошибок уровня E_NOTICE (такой как E_ALL), вы увидите эти ошибки. По умолчанию error_reporting установлена их не отображать.

Как указано в разделе синтаксис, внутри квадратных скобок ('[' и ']') должно быть выражение. Это означает, что вы можете писать подобно этому:

<?php
echo $arr[somefunc($bar)];
?>

Это пример использования возвращаемого функцией значения в качестве индекса массива. PHP известны также и константы, как вы, возможно, видели упоминание E_* раньше.

<?php
$error_descriptions[E_ERROR]   = "Произошла фатальная ошибка";
$error_descriptions[E_WARNING] = "PHP сообщает предупреждение";
$error_descriptions[E_NOTICE]  = "Это лишь неофициальное замечание";
?>

Обратите внимание, что E_ERROR - это такой же верный идентификатор, как и bar в первом примере. Но последний пример по сути эквивалентен такой записи:

<?php
$error_descriptions[1] = "Произошла фатальная ошибка";
$error_descriptions[2] = "PHP сообщает предупреждение";
$error_descriptions[8] = "Это лишь неофициальное замечание";
?>

поскольку E_ERROR соответствует 1 и т. д.

Как мы уже объяснили в вышеприведенных примерах, $foo[bar] по-прежнему работает, но это неверно. Это работает, поскольку в соответствии со своим синтаксисом bar ожидается как константа. Однако, в данном случае константы с именем bar не существует. В таком случае PHP предполагает, что, написав bar, вы имели ввиду строку "bar", но забыли указать кавычки.

Так что же в этом плохого?

Когда-нибудь в будущем команда разработчиков PHP возможно пожелает добавить еще одну константу или ключевое слово, либо вы можете ввести в ваше приложение еще одну константу и тогда у вас могут возникнуть проблемы. Например, вы уже не можете использовать таким образом слова empty и default, поскольку они являются зарезервированными ключевыми словами.

Замечание: Повторим, внутри строки (string), заключенной в двойные кавычки правильным является не окружать индексы массива кавычками, поэтому "$foo[bar]" является верным. Более подробно почему - смотрите вышеприведенные примеры, а также раздел обработка переменных в строках.

Преобразование в массив

Для любого из типов: integer, float, string, boolean и resource, если вы преобразуете значение в массив, вы получите массив с одним элементом (с индексом 0), являющимся скалярным значением, с которого вы начали.

Если вы преобразуете в массив объект (object), вы получите в качестве элементов массива свойства (переменные-члены) этого объекта. Ключами будут имена переменных-членов.

Если вы преобразуете в массив значение NULL, вы получите пустой массив.

Сравнение

Массивы можно сравнивать при помощи функции array_diff() и Операторов массивов.

Примеры

Тип массив в PHP является очень гибким, поэтому мы приведем здесь несколько примеров, чтобы продемонстрировать вам все возможности массивов.

<?php
// это
$a = array( 'color' => 'red',
            'taste' => 'sweet',
            'shape' => 'round',
            'name'  => 'apple',
                       4        // ключом будет 0
          );

// полностью соответствует
$a['color'] = 'red';
$a['taste'] = 'sweet';
$a['shape'] = 'round';
$a['name']  = 'apple';
$a[]        = 4;        // ключом будет 0

$b[] = 'a';
$b[] = 'b';
$b[] = 'c';
// создаст массив array(0 => 'a' , 1 => 'b' , 2 => 'c'),
// или просто array('a', 'b', 'c')
?>

Пример 6-4. Использование array()

<?php
// Массив как карта (свойств)
$map = array( 'version'    => 4,
              'OS'         => 'Linux',
              'lang'       => 'english',
              'short_tags' => true
            );
           
// исключительно числовые ключи
$array = array( 7,
                8,
                0,
                156,
                -10
              );
// это то же самое, что и array(0 => 7, 1 => 8, ...)

$switching = array(         10, // ключ = 0
                    5    =>  6,
                    3    =>  7,
                    'a'  =>  4,
                            11, // ключ = 6 (максимальным числовым индексом был 5)
                    '8'  =>  2, // ключ = 8 (число!)
                    '02' => 77, // ключ = '02'
                    0    => 12  // значение 10 будет перезаписано на 12
                  );
                 
// пустой массив
$empty = array();
?>

Пример 6-5. Коллекция

<?php
$colors = array('красный', 'синий', 'зеленый', 'желтый');

foreach ($colors as $color) {
    echo "Вам нравится $color?\n";
}

/* вывод:
Вам нравится красный?
Вам нравится синий?
Вам нравится зеленый?
Вам нравится желтый?
*/
?>

Обратите внимание, что в настоящее время невозможно изменять значения массива в таком цикле напрямую. Однако можно сделать так:

Пример 6-6. Коллекция
<?php
foreach ($colors as $key => $color) {
    // не будет работать:
    //$color = strtoupper($color);
   
    // работает:
    $colors[$key] = strtoupper($color);
}
print_r($colors);

/* вывод:
Array
(
    [0] => КРАСНЫЙ
    [1] => СИНИЙ
    [2] => ЗЕЛЕНЫЙ
    [3] => ЖЕЛТЫЙ
)
*/
?>

Следующий пример создает начинающийся с единицы массив.

Пример 6-7. Индекс, начинающийся с единицы

<?php
$firstquarter  = array(1 => 'Январь', 'Февраль', 'Март');
print_r($firstquarter);

/* вывод:
Array
(
    [1] => 'Январь'
    [2] => 'Февраль'
    [3] => 'Март'
)
*/
?>
Пример 6-8. Заполнение массива

<?php
// заполняет массив всеми элементами директории
$handle = opendir('.');
while (false !== ($file = readdir($handle))) {
    $files[] = $file;
}
closedir($handle);
?>

Массивы упорядочены. Вы можете изменять порядок элементов, используя различные функции сортировки. Для дополнительной информации смотрите раздел функции для работы с массивами. Вы можете подсчитать количество элементов в массиве, используя функцию count().

Пример 6-9. Сортировка массива

<?php
sort($files);
print_r($files);
?>

Поскольку значение массива может быть чем угодно, им также может быть другой массив. Таким образом вы можете создавать рекурсивные и многомерные массивы.

Пример 6-10. Рекурсивные и многомерные массивы

<?php
$fruits = array ( "фрукты" => array ( "a" => "апельсин",
                                      "b" => "банан",
                                      "c" => "яблоко"
                                    ),
                  "числа"  => array ( 1,
                                      2,
                                      3,
                                      4,
                                      5,
                                      6
                                    ),
                  "дырки"  => array (      "первая",
                                      5 => "вторая",
                                           "третья"
                                    )
                );

// Несколько примеров доступа к значениям предыдущего массива
echo $fruits["дырки"][5];    // напечатает "вторая"
echo $fruits["фрукты"]["a"]; // напечатает "апельсин"
unset($fruits["дырки"][0]);  // удалит "первая"

// Создаст новый многомерный массив
$juices["яблоко"]["зеленое"] = "хорошее";
?>

Обратите внимание, что при присваивании массива всегда происходит копирование значения. Чтобы копировать массив по ссылке, вам нужно использовать оператор ссылки.

<?php
$arr1 = array(2, 3);
$arr2 = $arr1;
$arr2[] = 4; // $arr2 изменился,
             // $arr1 по прежнему array(2,3)
             
$arr3 = &$arr1;
$arr3[] = 4; // теперь $arr1 и $arr3 эквивалентны
?>

Объекты

Инициализация объекта

Для инициализации объекта используется выражение new, создающее в переменной экземпляр объекта.

<?php
class foo
{
    function do_foo()
    {
        echo "Doing foo.";
    }
}

$bar = new foo;
$bar->do_foo();
?>

Полное рассмотрение производится в разделе Классы и Объекты.

Преобразование в объект

Если объект преобразуется в объект, он не изменяется. Если же в объект преобразуется значение любого иного типа, создается новый экземпляр встроенного класса stdClass. Если значение было пустым, новый экземпляр также будет пустым. При любом другом значении оно будет содержатся в переменной-члене scalar.

<?php
$obj = (object) 'ciao';
echo $obj->scalar// выведет 'ciao'
?>

Ресурс

Ресурс - это специальная переменная, содержащая ссылку на внешний ресурс. Ресурсы создаются и используются специальными функциями. Полный перечень этих функций и соответствующих типов ресурсов смотрите в приложении.

Замечание: Тип ресурс был введен в PHP 4

Преобразование в ресурс

Поскольку тип ресурс содержит специальные указатели на открытые файлы, соединения с базой данных, область изображения и тому подобное, вы не можете преобразовать какое-либо значение в ресурс.

Освобождение ресурсов

В связи с системой подсчета ссылок, введенной в движке Zend PHP 4 автоматически определяется, что ресурс больше никуда не ссылается (как в Java). Когда это происходит, все ресурсы, которые использовались для данного ресурса, освобождаются сборщиком мусора. По этой причине маловероятно, что когда-либо будет необходимо освобождать память вручную, используя какую-нибудь free_result функцию.

Замечание: Постоянные ссылки базы данных являются особыми, они не уничтожаются сборщиком мусора. Смотрите также раздел о постоянных соединениях.

NULL

Специальное значение NULL говорит о том, что эта переменная не имеет значения. NULL - это единственно возможное значение типа NULL.

Замечание: Пустой тип был введен в PHP 4

Переменная считается NULL если

Синтаксис

Существует только одно значение типа NULL - регистро-независимое ключевое слово NULL.

<?php
$var = NULL;       
?>

Смотрите также is_null() и unset().

Псевдо-типы, используемые в этой документации

mixed

mixed говорит о том, что параметр может принимать множество (но не обязательно все) типов.

gettype(), например, принимает все типы PHP, тогда как str_replace() принимает строки и массивы.

number

number говорит о том, что параметр может быть либо integer, либо float.

callback

Некоторые функции, такие как call_user_func() или usort() принимают в качестве параметра определенные пользователем callback-функции. Callback-функции могут быть не только простыми функциями, но также методами объектов, включая статические методы классов.

PHP-функция передается просто как строка ее имени. Вы можете передать любую встроенную или определенную пользователем функцию за исключением array(), echo(), empty(), eval(), exit(), isset(), list(), print() и unset().

Метод созданного объекта передается как массив, содержащий объект в элементе с индексом 0 и имя метода в элементе с индексом 1.

Методы статических классов также могут быть переданы без создания экземпляра объекта передачей имени класса вместо имени объекта в элементе с индексом 0.

Пример 6-11. Примеры callback-функций
<?php

// простой пример callback
function my_callback_function() {
    echo 'hello world!';
}
call_user_func('my_callback_function');

// примеры callback-метода
class MyClass {
    function myCallbackMethod() {
        echo 'Hello World!';
    }
}

// вызов метода статического класса без создания объекта
call_user_func(array('MyClass', 'myCallbackMethod'));

// вызов метода объекта
$obj = new MyClass();
call_user_func(array(&$obj, 'myCallbackMethod'));
?>

Манипуляции с типами

PHP не требует (и не поддерживает) явного определения типа при объявлении переменной; тип переменной определяется по контексту, в котором она используется. То есть, если вы присвоите строковое значение переменной $var, $var станет строкой. Если вы затем присвоите $var целочисленное значение, она станет целым числом.

Примером автоматического преобразования типа является оператор сложения '+'. Если любой из операндов является числом с плавающей точкой, то все операнды интерпретируются как числа с плавающей точкой, результатом будет также число с плавающей точкой. В противном случае операнды будут интерпретироваться как целые числа и результат также будет целочисленным. Обратите внимание, что это НЕ меняет типы самих операндов; меняется только то, как они вычисляются.

<?php
$foo = "0"// $foo это строка (ASCII 48)
$foo += 2;   // $foo теперь целое число (2)
$foo = $foo + 1.3// $foo теперь число с плавающей точкой (3.3)
$foo = 5 + "10 Little Piggies"; // $foo это целое число (15)
$foo = 5 + "10 Small Pigs";     // $foo это целое число (15)
?>

Если последние два примера вам непонятны, смотрите Преобразование строк в числа.

Если вы хотите, чтобы переменная принудительно вычислялась как определенный тип, смотрите раздел приведение типов. Если вы хотите изменить тип переменной, смотрите settype().

Если вы хотите протестировать любой из примеров, приведенных в данном разделе, вы можете использовать функцию var_dump().

Замечание: Поведение автоматического преобразования в массив в настоящий момент не определено.

<?php
$a = "1";     // $a это строка
$a[0] = "f"// А как же смещение строки? Что произойдет?
?>

Поскольку PHP (по историческим причинам) поддерживает индексирование в строках с использованием такого же синтаксиса, как и при индексировании массива, вышеприведенный пример приводит к проблеме: следует ли $a стать массивом, первым элементом которого будет "f" или "f" должна стать первым символом строки $a?

Текущая версия PHP воспринимает второе присваивание как определение смещения строки, поэтому $a станет "f", результат же этого автоматического преобразования следует, однако, рассматривать как неопределенный. В PHP 4 для доступа к символам строки был введен новый синтаксис фигурных скобок, используйте этот синтаксис вместо вышеприведенного:

<?php
$a    = "abc"; // $a это строка
$a{1} = "f";   // $a теперь содержит "afc"
?>

Для дополнительной информации смотрите раздел Доступ к символу в строке.

Приведение типов

Приведение типов в PHP работает так же, как и в C: имя требуемого типа записывается в круглых скобках перед приводимой переменной.

<?php
$foo = 10;   // $foo это целое число
$bar = (boolean) $foo;   // $bar это булев тип
?>

Допускаются следующие приведения типов:

Обратите внимание, что внутри скобок допускаются пробелы и символы табуляции, поэтому следующее равносильно по своему действию:

<?php
$foo = (int) $bar;
$foo = ( int ) $bar;
?>

Замечание: Вместо приведения переменной к строке, вы можете заключить ее в двойные кавычки.

<?php
$foo = 10;            // $foo это целое число
$str = "$foo";        // $str это строка
$fst = (string) $foo; // $fst это также строка

// Это напечатает "они одинаковы"
if ($fst === $str) {
    echo "они одинаковы";
}
?>

Возможно, вам не совсем ясно, что происходит при приведении между типами. Для дополнительной информации смотрите разделы:

Глава 5. Переменные

Содержание

Основы

Предопределенные переменные

Область видимости переменной

Переменные переменные

Переменные вне PHP

Основы

Переменные в PHP представлены знаком доллара с последующим именем переменной. Имя переменной чувствительно к регистру.

Имена переменных соответствуют тем же правилам, что и остальные наименования в PHP. Правильное имя переменной должно начинаться с буквы или символа подчеркивания с последующими в любом количестве буквами, цифрами или символами подчеркивания Это можно отобразить регулярным выражением: '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'

Замечание: Для наших целей буквы здесь - это a-z, A-Z, и ASCII-символы со 127 по 255 (0x7f-0xff).

<?php
$var = "Bob";
$Var = "Joe";
echo "$var, $Var";      // выведет "Bob, Joe"

$4site = 'not yet';     // неверно; начинается с цифры
$_4site = 'not yet';    // верно; начинается с символа подчеркивания
$tдyte = 'mansikka';    // верно; 'д' это (Дополнительный) ASCII 228.
?>

В PHP 3 переменные всегда присваивались по значению. То есть, когда вы присваиваете выражение переменной, все значение оригинального выражения копируется в эту переменную. Это означает, к примеру, что после присвоения одной переменной значения другой, изменение одной из них не влияет на значение другой. Дополнительную информацию об этом способе присвоения смотрите в разделе Выражения.

PHP 4 предлагает иной способ присвоения значений переменным: присвоение по ссылке. Это означает, что новая переменная просто ссылается (иначе говоря, "становится псевдонимом" или "указывает") на оригинальную переменную. Изменения в одной переменной отражаются на оригинале, и наоборот. Это также означает, что копирования не происходит; таким образом, присвоение осуществляется быстрее. Однако, любое увеличение скорости будет хорошо заметно только в сжатых циклах или при присвоении больших массивов или объектов.

Для присвоения по ссылке, просто добавьте амперсанд (&) к началу имени присваиваемой (исходной) переменной. Например, следующий фрагмент кода дважды выводит 'My name is Bob':

<?php
$foo = 'Bob';              // Присваивает $foo значение 'Bob'
$bar = &$foo;              // Ссылка на $foo через $bar.
$bar = "My name is $bar";  // Изменение $bar...
echo $bar;
echo $foo;                 // меняет и $foo.
?>

Важно отметить, что по ссылке могут быть присвоены только именованные переменные.

<?php
$foo = 25;
$bar = &$foo;      // Это верное присвоение.
$bar = &(24 * 7);  // Неверно; ссылка на неименованное выражение.

function test()
{
   return 25;
}

$bar = &test();    // Неверно.
?>

Предопределенные переменные

Любому запускаемому скрипту PHP предоставляет большое количество предопределенных переменных. Однако, многие из этих переменных не могут быть полностью задокументированы, поскольку они зависят от запущенного сервера, его версии и настроек, а также других факторов. Некоторые из этих переменных не доступны, когда PHP запущен из командной строки. Перечень этих переменных смотрите в разделе Зарезервированные предопределенные переменные.

Внимание

Начиная с PHP 4.2.0, значение директивы register_globals по умолчанию установлено в off (отключено). Это большое изменение в PHP. Положение register_globals в off делает предопределенные переменные доступными в глобальной области видимости. Например, чтобы получить DOCUMENT_ROOT, вам необходимо будет использовать $_SERVER['DOCUMENT_ROOT'] вместо $DOCUMENT_ROOT, или $_GET['id'] из URL http://www.example.com/test.php?id=3 вместо $id, или $_ENV['HOME'] вместо $HOME.

Дополнительную информацию, связанную с этим изменением, вы можете получить, прочитав описание register_globals в разделе о настройках, главу о безопасности Использование Register Globals , а также сообщения о выпусках PHP 4.1.0 и 4.2.0.

Использование доступных зарезервированных предопределенных переменных PHP, таких как суперглобальные массивы, является предпочтительным.

Начиная с версии 4.1.0, PHP предоставляет дополнительный набор предопределенных массивов, содержащих переменные web-сервера (если они доступны), окружения и пользовательского ввода. Эти новые массивы являются особыми, поскольку они автоматически глобальны--то есть, автоматически доступны в любой области видимости. По этой причине они также известны как 'автоглобальные' или 'суперглобальные' переменные. (В PHP нет механизма определяемых пользователем суперглобальных переменных.) Суперглобальные переменные перечислены ниже; однако, перечисление их содержимого и дальнейшее обсуждение предопределенных переменных PHP и их сути смотрите в разделе Зарезервированные предопределенные переменные. Также вы заметите, что старые предопределенные переменные ($HTTP_*_VARS) все еще существуют. Начиная с PHP 5.0.0, длинные предопределенные переменные массивов PHP могут быть отключены директивой register_long_arrays.

Переменные переменных: Суперглобальные переменные не могут быть переменными переменных.

Если некоторые из переменных в variables_order не установлены, соответствующие им предопределенные массивы также останутся пустыми.

Суперглобальные переменные PHP

$GLOBALS

Содержит ссылку на каждую переменную, доступную в данный момент в глобальной области видимости скрипта. Ключами этого массива являются имена глобальны переменных. $GLOBALS существует, начиная с PHP 3.

$_SERVER

Переменные, установленные web-сервером либо напрямую связанные с окружением выполнения текущего скрипта. Аналог старого массива $HTTP_SERVER_VARS (который по-прежнему доступен, но не рекомендуется).

$_GET

Переменные, передаваемые скрипту через HTTP GET. Аналог старого массива $HTTP_GET_VARS (который по-прежнему доступен, но не рекомендуется).

$_POST

Переменные, передаваемые скрипту через HTTP POST. Аналог старого массива $HTTP_POST_VARS (который по-прежнему доступен, но не рекомендуется).

$_COOKIE

Переменные, передаваемые скрипту через HTTP cookies. Аналог старого массива $HTTP_COOKIE_VARS (который по-прежнему доступен, но не рекомендуется).

$_FILES

Переменные, передаваемые скрипту через HTTP post-загрузку файлов. Аналог старого массива $HTTP_POST_FILES (который по-прежнему доступен, но не рекомендуется). Для дополнительной информации смотрите Загрузка методом POST.

$_ENV

Переменные, передаваемые скрипту через окружение. Аналог старого массива $HTTP_ENV_VARS (который по-прежнему доступен, но не рекомендуется).

$_REQUEST

Переменные, передаваемые скрипту через механизмы ввода GET, POST и COOKIE, и которым, следовательно, нельзя доверять. Наличие и порядок включения переменных в этот массив определяется в соответствии с директивой конфигурации PHP variables_order. Этот массив не имеет прямых аналогов в версиях PHP до 4.1.0. Смотрите также import_request_variables().

Предостережение

Начиная с PHP 4.3.0, информация о файле из $_FILES больше не существует в $_REQUEST.

Замечание: При запуске из командной строки , этот массив не будет содержать записей argv и argc; они находятся в массиве $_SERVER.

$_SESSION

Переменные, зарегистрированные на данный момент в сессии скрипта. Аналог старого массива $HTTP_SESSION_VARS (который по-прежнему доступен, но не рекомендуется). Дополнительную информацию смотрите в разделе Функции обработки сессии.

Область видимости переменной

Область видимости переменной - это среда, в которой она определена. В большинстве случаев все переменные PHP имеют единую область видимости. Эта единая область видимости охватывает также включаемые (include) и требуемые (require) файлы. Например:

<?php
$a = 1;
include "b.inc";
?>

Здесь переменная $a будет доступна внутри включенного скрипта b.inc. Однако, внутри определенных пользователем функций вводится локальная область видимости функции. Любая, используемая внутри функции переменная, по умолчанию ограничена локальной областью видимости функции. Например:

<?php
$a = 1; /* глобальная область видимости */

function Test()
{
    echo $a; /* ссылка на переменную локальной области видимости */
}

Test();
?>

Этот скрипт не сгенерирует никакого вывода, поскольку выражение echo указывает на локальную версию переменной $a, а в пределах этой области видимости ей не не было присвоено значение. Возможно вы заметили, что это немного отличается от языка C в том, что глобальные переменные в C автоматически доступны функциям, если только они не были перезаписаны локальным определением. Это может вызвать некоторые проблемы, поскольку люди могут нечаянно изменить глобальную переменную. В PHP, если глобальная переменная будет использоваться внутри функции, она должна быть объявлена глобальной внутри нее.

Ключевое слово global

Сначала пример использования global:

Пример 7-1. Использование global

<?php
$a = 1;
$b = 2;

function Sum()
{
    global $a, $b;

    $b = $a + $b;
}

Sum();
echo $b;
?>

Вышеприведенный скрипт выведет "3". После определения $a и $b внутри функции как global все ссылки на любую из этих переменных будут указывать на их глобальную версию. Не существует никаких ограничений на количество глобальных переменных, которые могут обрабатываться функцией.

Второй способ доступа к переменным глобальной области видимости - использование специального, определяемого PHP массива $GLOBALS. Предыдущий пример может быть переписан так:

Пример 7-2. Использование $GLOBALS вместо global

<?php
$a = 1;
$b = 2;

function Sum()
{
    $GLOBALS["b"] = $GLOBALS["a"] + $GLOBALS["b"];
}

Sum();
echo $b;
?>

$GLOBALS - это ассоциативный массив, ключом которого является имя, а значением - содержимое глобальной переменной. Обратите внимание, что $GLOBALS существует в любой области видимости, это объясняется тем, что этот массив является суперглобальным. Ниже приведен пример, демонстрирующий возможности суперглобальных переменных:

Пример 7-3. Суперглобальные переменные и область видимости

<?php
function test_global()
{
    // Большинство предопределенных переменных не являются
    // "супер" и чтобы быть доступными в локальной области
    // видимости функции требуют указания 'global'.
    global $HTTP_POST_VARS;
   
    echo $HTTP_POST_VARS['name'];
   
    // Суперглобальные переменные доступны в любой области
    // видимости и не требуют указания 'global'.
    // Суперглобальные переменные доступны, начиная с PHP 4.1.0
    echo $_POST['name'];
}
?>

Использование статических переменных

Другой важной возможностью области видимости переменной является статическая переменная. Статическая переменная существует только в локальной области видимости функции, но не теряет своего значения, когда выполнение программы выходит из этой области видимости. Рассмотрим следующий пример:

Пример 7-4. Демонстрация необходимости статических переменных

<?php
function Test ()
{
    $a = 0;
    echo $a;
    $a++;
}
?>

Эта функция абсолютно бесполезна поскольку при каждом вызове она устанавливает $a в 0 и выводит "0". Инкремент переменной $a++ здесь не играет роли, так как при выходе из функции переменная $a исчезает. Чтобы написать полезную считающую функцию, которая не будет терять текущего значения счетчика, переменная $a объявляется как static:

Пример 7-5. Пример использования статических переменных

<?php
function Test()
{
    static $a = 0;
    echo $a;
    $a++;
}
?>

Теперь при каждом вызове функция Test() будет выводить значение $a и инкрементировать его.

Статические переменные также дают возможность работать с рекурсивными функциями. Рекурсивной является функция, вызывающая саму себя. При написании рекурсивной функции нужно быть внимательным, поскольку есть вероятность сделать рекурсию бесконечной. Вы должны убедиться, что существует адекватный способ завершения рекурсии. Следующая простая функция рекурсивно считает до 10, используя для определения момента остановки статическую переменную $count:

Пример 7-6. Статические переменные и рекурсивные функции

<?php
function Test()
{
    static $count = 0;

    $count++;
    echo $count;
    if ($count < 10) {
        Test ();
    }
    $count--;
}
?>

Замечание: Статические переменные могут быть объявлены так, как показано в предыдущем примере. Попытка присвоить этим переменным значения, являющиеся результатом выражений, вызовет ошибку обработки.

Пример 7-7. Объявление статических переменных

<?php
function foo(){
    static $int = 0;          // верно
    static $int = 1+2;        // неверно  (поскольку это выражение)
    static $int = sqrt(121);  // неверно  (поскольку это тоже выражение)

    $int++;
    echo $int;
}
?>

Ссылки с глобальными и статическими переменными

Движок Zend 1, лежащий в основе PHP 4, оперирует модификаторами переменных static и global как ссылками. Например, реальная глобальная переменная, внедренная в область видимости функции указанием ключевого слова global, в действительности создает ссылку на глобальную переменную. Это может привести к неожиданному поведению, как это показано в следующем примере:

<?php
function test_global_ref() {
    global $obj;
    $obj = &new stdclass;
}

function test_global_noref() {
    global $obj;
    $obj = new stdclass;
}

test_global_ref();
var_dump($obj);
test_global_noref();
var_dump($obj);
?>

Выполнение этого примера сгенерирует следующий вывод:

NULL
object(stdClass)(0) {
}

Аналогично ведет себя и выражение static. Ссылки не хранятся статично:

<?php
function &get_instance_ref() {
    static $obj;

    echo "Static object: ";
    var_dump($obj);
    if (!isset($obj)) {
        // Присвоить ссылку статической переменной
        $obj = &new stdclass;
    }
    $obj->property++;
    return $obj;
}

function &get_instance_noref() {
    static $obj;

    echo "Static object: ";
    var_dump($obj);
    if (!isset($obj)) {
        // Присвоить объект статической переменной
        $obj = new stdclass;
    }
    $obj->property++;
    return $obj;
}

$obj1 = get_instance_ref();
$still_obj1 = get_instance_ref();
echo "\n";
$obj2 = get_instance_noref();
$still_obj2 = get_instance_noref();
?>

Выполнение этого примера сгенерирует следующий вывод:

Static object: NULL
Static object: NULL
 
Static object: NULL
Static object: object(stdClass)(1) {
  ["property"]=>
  int(1)
}

Этот пример демонстрирует, что при присвоении ссылки статической переменной она не запоминается, когда вы вызываете функцию &get_instance_ref() во второй раз.

Переменные переменные

Иногда бывает удобно иметь переменными имена переменных. То есть, имя переменной, которое может быть определено и изменено динамически. Обычная переменная определяется примерно таким выражением:

<?php
$a = "hello";
?>

Переменная переменная берет значение переменной и рассматривает его как имя переменной. В вышеприведенном примере hello может быть использовано как имя переменной при помощи двух знаков доллара. То есть:

<?php
$$a = "world";
?>

Теперь в дереве символов PHP определены и содержатся две переменные: $a, содержащая "hello", и $hello, содержащая "world". Таким образом, выражение

<?php
echo "$a ${$a}";
?>

выведет то же, что и

<?php
echo "$a $hello";
?>

то есть, они оба выведут: hello world.

Для того чтобы использовать переменные переменные с массивами, вы должны решить проблему двусмысленности. То есть, если вы напишете $$a[1], обработчику необходимо знать, хотите ли вы использовать $a[1] в качестве переменной, либо вам нужна как переменная $$a, а затем ее индекс [1]. Синтаксис для разрешения этой двусмысленности таков: ${$a[1]} для первого случая и ${$a}[1] для второго.

Внимание

Пожалуйста, обратите внимание, что переменные переменные не могут использоваться с Суперглобальными массивами PHP. Это означает, что вы не можете делать что-то вроде ${$_GET}. Если вы ищете способ использовать суперглобальные переменные и старые HTTP_*_VARS, вы можете попробовать ссылаться на них.

Переменные вне PHP

HTML-формы (GET и POST)

Когда происходит отправка данных формы PHP-скрипту, информация из этой формы автоматически становится доступной ему. Существует много способов получения этой информации, например:

Пример 7-8. Простая HTML-форма

<form action="foo.php" method="post">
    Имя:  <input type="text" name="username" /><br />
    Email: <input type="text" name="email" /><br />
    <input type="submit" name="submit" value="Отправь меня!" />
</form>

В зависимости от вашей индивидуальной установки и личных настроек существует много способов доступа к данным из ваших HTML-форм. Вот несколько примеров:

Пример 7-9. Доступ к данным из простой HTML POST-формы

<?php 
// Доступно, начиная с PHP 4.1.0
 
   echo $_POST['username'];
   echo $_REQUEST['username'];
 
   import_request_variables('p', 'p_');
   echo $p_username;
 
// Доступно, начиная с PHP 3. Начиная с PHP 5.0.0, эти длинные предопределенные
// переменные могут быть отключены директивой register_long_arrays.
 
   echo $HTTP_POST_VARS['username'];
 
// Доступно, если директива PHP register_globals = on. Начиная
// с PHP 4.2.0, значение по умолчанию register_globals = off.
// Использование/доверие этому методу непредпочтительно.
 
   echo $username;
?>

GET-форма используется аналогично, за исключением того, что вместо POST вам нужно будет использовать соответствующую предопределенную переменную GET. GET относится также к QUERY_STRING (информация в URL после '?'). Так, например, http://www.example.com/test.php?id=3 содержит GET-данные, доступные как $_GET['id']. Смотрите также $_REQUEST и import_request_variables().

Замечание: Суперглобальные массивы, такие как $_POST и $_GET, стали доступны в PHP 4.1.0

Как уже говорилось, до PHP 4.2.0 значением register_globals по умолчанию было on (включено). А в PHP 3 оно всегда было включено. Сообщество PHP рекомендует всем не полагаться на эту директиву, поскольку предпочтительно присвоить ей значение off и писать программы исходя из этого.

Замечание: Конфигурационная директива magic_quotes_gpc влияет на значения Get, Post и Cookie. Если она включена, значение (It's "PHP!") автоматически станет (It\'s \"PHP!\"). Мнемонизация необходима при добавлении в базу данных. Смотрите также addslashes(), stripslashes() и magic_quotes_sybase.

PHP также понимает массивы в контексте переменных формы. К примеру, вы можете сгруппировать связанные переменные вместе или использовать эту возможность для получения значений списка множественного выбора select. Например, давайте отправим форму самой себе, а после отправки отобразим данные:

Пример 7-10. Более сложные переменные формы

<?php
if (isset($_POST['action']) && $_POST['action'] == 'submitted') {
    echo '<pre>';
    print_r($_POST);
    echo '<a href="'. $_SERVER['PHP_SELF'] .'">Попробуйте еще раз</a>';

    echo '</pre>';
} else {
?>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
    Имя:  <input type="text" name="personal[name]" /><br />
    Email: <input type="text" name="personal[email]" /><br />
    Пиво: <br />
    <select multiple name="beer[]">
        <option value="warthog">Warthog</option>
        <option value="guinness">Guinness</option>
        <option value="stuttgarter">Stuttgarter Schwabenbrдu</option>
    </select><br />
    <input type="hidden" name="action" value="submitted" />
    <input type="submit" name="submit" value="Отправь меня!" />
</form>
<?php
}
?>

В PHP 3 использование массивов в переменных формы ограничено одномерными массивами. В PHP 4 таких ограничений нет.

Имена переменных кнопки-изображения

При отправке формы вместо стандартной кнопки можно использовать изображение с помощью тега такого вида:

<input type="image" src="image.gif" name="sub" />

Когда пользователь щелкнет где-нибудь на изображении, соответствующая форма будет передана на сервер с двумя дополнительными переменными - sub_x и sub_y. Они содержат координаты нажатия пользователя на изображение. Опытные программисты могут заметить, что на самом деле имена переменных, отправленных браузером, содержат точку, а не подчеркивание, но PHP автоматически конвертирует точку в подчеркивание.

HTTP Cookies

PHP явно поддерживает HTTP cookies как определено в спецификации Netscape. Cookies - это механизм для хранения данных в удаленном браузере и отслеживания и идентификации таким образом вернувшихся пользователей. Вы можете установить cookies, используя функцию setcookie(). Cookies являются частью HTTP-заголовка, поэтому функция SetCookie должна вызываться до того, как браузеру будет отправлен какой бы то ни было вывод. Это ограничение аналогично ограничению функции header(). Данные, хранящиеся в cookie, доступны в соответствующих массивах данных cookie, таких как $_COOKIE, $HTTP_COOKIE_VARS, а также в $_REQUEST. Подробности и примеры смотрите на странице setcookie() руководства.

Если вы хотите присвоить множество значений одной переменной cookie, вы можете присвоить их как массив. Например:

<?php
  setcookie("MyCookie[foo]", "Тест 1", time()+3600);
  setcookie("MyCookie[bar]", "Тест 2", time()+3600);
?>

Это создаст две разные cookie, хотя в вашем скрипте MyCookie будет теперь одним массивом. Если вы хотите установить именно одну cookie со множеством значений, примите во внимание сначала применение к значениям таких функций, как serialize() или explode().

Обратите внимание, что cookie заменит предыдущую cookie с тем же именем в вашем браузере, если только путь или домен не отличаются. Так, для приложения корзины покупок вы, возможно, захотите сохранить счетчик. То есть:

Пример 7-11. A setcookie() example

<?php
if (isset($_COOKIE['count'])) {
    $count = $_COOKIE['count'] + 1;
} else {
    $count = 1;
}
setcookie("count", $count, time()+3600);
setcookie("Cart[$count]", $item, time()+3600);
?>

Точки в именах приходящих переменных

Как правило, PHP не меняет передаваемых скрипту имен переменных. Однако следует отметить, что точка не является корректным символом в имени переменной PHP. Поэтому рассмотрим такую запись:

<?php
$varname.ext/* неверное имя переменной */
?>

В данном случае интерпретатор видит переменную $varname, после которой идет оператор конкатенации, а затем голая строка (то есть, не заключенная в кавычки строка, не соответствующая ни одному из ключевых или зарезервированных слов) 'ext'. Очевидно, что это не даст ожидаемого результата.

По этой причине важно заметить, что PHP будет автоматически заменять любые точки в именах приходящих переменных на символы подчеркивания.

Определение типов переменных

Поскольку PHP определяет и конвертирует типы переменных (в большинстве случаев) как надо, не всегда очевидно, какой тип имеет данная переменная в конкретный момент времени. PHP содержит несколько функций, позволяющих определить тип переменной, таких как: gettype(), is_array(), is_float(), is_int(), is_object() и is_string(). Смотрите также раздел Типы.

Глава 6. Константы

Содержание

Синтаксис

Предопределенные константы

Константы - это идентификаторы простых значений. Исходя из их названия, нетрудно понять, что их значение не может изменяться в ходе выполнения скрипта (исключения представляют "волшебные" константы, которые на самом деле не являются константами в полном смысле этого слова). Имена констант чувствительны к регистру. По принятому соглашению, имена констант всегда пишутся в верхнем регистре.

Имя константы должно соответствовать тем же правилам, которыми руководствуются и другие имена в PHP. Правильное имя начинается с буквы или символа подчеркивания и состоит из букв, цифр и подчеркиваний. Регулярное выражение для проверки правильности имени константы выглядит так: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*

Замечание: Понятие "буквы" здесь - это символы a-z, A-Z, и другие символы с ASCII-кодами от 127 до 255 (0x7f-0xff).

Как и суперглобальные переменные, константы доступны из любой области видимости. Вы можете использовать константы в любом месте вашего скрипта, не обращая внимания на текущую область видимости. Подробную информацию об областях видимости можно найти здесь.

Синтаксис

Вы можете определить константу с помощью функции define(). После того, как константа определена, ее значение не может быть изменено или аннулировано.

Константы могут содержать только скалярные данные (логического, целого, плавающего и строкового типов).

Получить значение константы можно, указав ее имя. В отличие от переменных, вам не потребуется предварять имя константы символом $. Также вы можете использовать функцию constant() для получения значения константы, если вы формируете имя константы динамически. Используйте функцию get_defined_constants() для получения списка всех объявленных констант.

Замечание: Константы и (глобальные) переменные находятся в разном пространстве имен. Это означает, что, например, TRUE и $TRUE являются совершенно разными вещами.

Если вы используете неопределенную константу, PHP предполагает, что вы имеете ввиду само имя константы, как если бы вы указали переменную типа строка (CONSTANT и "CONSTANT"). При этом будет сгенерирована ошибка типа E_NOTICE. Смотрите также главу руководства, которая разъясняет, почему $foo[bar] - это неправильно (конечно, если вы перед этим не объявили bar как константу с помощью define()). Если вы просто хотите проверить, определена ли константа, используйте функцию defined().

Различия между константами и переменными:

Пример 8-1. Определение констант

<?php
define("CONSTANT", "Здравствуй, мир.");
echo CONSTANT; // выводит "Здравствуй, мир."
echo Constant; // выводит "Constant" и выводит предупреждениее.
?>

Предопределенные константы

PHP предоставляет большой список предопределенных констант для каждого выполняемого скрипта. Многие из этих констант определяются различными модулями и будут присутствовать только в том случае, если эти модули доступны в результате динамической загрузки или в результате статической сборки.

Есть пять волшебных констант, которые меняют свое значение в зависимости от контекста, в котором они используются. Например, значение __LINE__ зависит от строки в скрипте, на которой эта константа указана. Специальные константы нечувствительны к регистру и их список приведен ниже:

Таблица 8-1. Некоторые "волшебные" константы PHP

Имя

Описание

__LINE__

Текущая строка в файле.

__FILE__

Полный путь и имя текущего файла.

__FUNCTION__

Имя функции. (Добавлена в PHP 4.3.0.)

__CLASS__

Имя класса. (Добавлена в PHP 4.3.0.)

__METHOD__

Имя метода класса. (Добавлена в PHP 5.0.0)

С полным списком предопределенных констант можно ознакомиться в соответствующем разделе.

Глава 9. Выражения

Выражения - это краеугольный камень PHP. Почти все, что вы пишите в PHP, является выражением. Самое простое и точное определение выражения - "все что угодно, имеющее значение".

Основными формами выражений являются константы и переменные. Если вы записываете "$a = 5", вы присваиваете '5' переменной $a. '5', очевидно, имеет значение 5 или, другими словами, '5' это выражение со значением 5 (в данном случае '5' это целочисленная константа).

После этого присвоения вы ожидаете, что значением $a также является 5, поэтому, если вы написали $b = $a, вы полагаете, что работать это будет так же, как если бы вы написали $b = 5. Другими словами, $a это также выражение со значением 5. Если все работает верно, то именно так и произойдет.

Немного более сложными примерами выражений являются функции. Например, рассмотрим следующую функцию:

<?php
function foo ()
{
    return 5;
}
?>

Исходя из того, что вы хорошо знакомы с концепцией функций (если нет, то прочитайте главу о функциях), вы полагаете, что запись $c = foo() абсолютно эквивалента записи $c = 5, и вы правы. Функции - это выражения, значением которых является то, что возвращает функция. Поскольку foo() возвращает 5, значением выражения 'foo()' является 5. Как правило, функции возвращают не просто статическое значение, а что-то вычисляют.

Разумеется, значения в PHP не обязаны быть целочисленными, и очень часто ими не являются. PHP поддерживает три типа скалярных значений: целочисленные, с плавающей точкой и строковые значения (скалярными являются значения, которые вы не можете 'разбить' на меньшие части, в отличие, например, от массивов). PHP поддерживает также два комбинированных (не скалярных) типа: массивы и объекты. Каждый из этих типов значений может присваиваться переменной или возвращаться функцией.

До сих пор пользователи PHP/FI 2 не должны были почувствовать каких-либо изменений. Однако PHP, как и многие другие языки, понимает гораздо больше выражений. PHP - это язык, ориентированный на выражения и рассматривающий почти все как выражение. Вернемся к примеру, с которым мы уже имели дело: '$a = 5'. Легко заметить, что здесь присутствуют два значения - значение целочисленной константы '5' и значение переменной $a, также принимающей значение 5. Но на самом деле здесь присутствует и еще одно значение - значение самого присвоения. Само присвоение вычисляется в присвоенное значение, в данном случае - в 5. На практике это означает, что '$a = 5', независимо от того, что оно делает, является выражением со значением 5. Таким образом, запись '$b = ($a = 5)' равносильна записи '$a = 5; $b = 5;' (точка с запятой обозначает конец выражения). Поскольку операции присвоения анализируются справа налево, вы также можете написать '$b = $a = 5'.

Другой хороший пример ориентированности на выражения - пре- и постфиксный инкремент и декремент. Пользователи PHP/FI 2 и многих других языков возможно уже знакомы с формой записи переменная++ и переменная--. Это операторы инкремента и декремента. В PHP/FI 2 операция '$a++' не имеет значения (это не выражение), и, таким образом, вы не можете присвоить ее или каким-либо образом использовать. PHP увеличивает возможности инкремента/декремента, также сделав их выражениями, как в C. Также как и C, PHP поддерживает два типа инкремента - префиксный и постфиксный. Они оба инкрементируют значение переменной и эффект их действия на нее одинаков. Разница состоит в значении выражения инкремента. Префиксный инкремент, записываемый как '++$variable', вычисляется в инкрементированное значение (PHP инкрементирует переменную перед тем как прочесть ее значение, отсюда название 'пре-инкремент'). Постфиксный инкремент, записываемый как '$variable++', вычисляется в первоначальное значение переменной $variable перед ее приращением (PHP инкрементирует переменную после прочтения ее значения, отсюда название 'пост-инкремент').

Очень распространенным типом выражений являются выражения сравнения. Они вычисляются в 0 или 1, означающих соответственно FALSE (ложь) или TRUE (истину). PHP поддерживает > (больше), >= (больше либо равно), == (равно), != (не равно), < (меньше) и <= (меньше либо равно). Он также поддерживает операторы строгого равенства: === (равно и одного типа) и !== (не равно или не одного типа). Чаще всего эти выражения используются в условиях выполнения операторов, таких как if.

Последний пример выражений, который мы здесь рассмотрим, это смешанные выражения операции и присвоения. Вы уже знаете, что если вы хотите увеличить $a на 1, вы можете просто написать '$a++' или '++$a'. Но что, если вы хотите прибавить больше, чем единицу, например, 3? Вы могли бы написать '$a++' много раз, однако, очевидно это не очень рациональный или удобный способ. Гораздо более распространенной практикой является запись вида '$a = $a + 3'. '$a + 3' вычисляется в значение $a плюс 3 и снова присваивается $a, увеличивая в результате $a на 3. В PHP, как и в некоторых других языках, таких как C, вы можете записать это более коротким образом, что увеличит очевидность смысла и быстроту понимания кода по прошествии времени. Прибавить 3 к текущему значению $a можно с помощью записи '$a += 3'. Это означает дословно "взять значение $a, прибавить к нему 3 и снова присвоить его переменной $a". Кроме большей понятности и краткости, это быстрее работает. Значением '$a += 3', как и обычного присвоения, является присвоенное значение. Обратите внимание, что это НЕ 3, а суммированное значение $a плюс 3 (то, что было присвоено $a). Таким образом может использоваться любой двухместный оператор, например, '$a -= 5' (вычесть 5 из значения $a), '$b *= 7' (умножить значение $b на 7) и т.д.

Существует еще одно выражение, которое может выглядеть необычным, если вы не встречали его в других языках - тернарный условный оператор:

<?php
$first ? $second : $third
?>

Если значением первого подвыражения является TRUE (не ноль), выполняется второе подвыражение, которое и будет результатом условного выражения. В противном случае, будет выполнено третье подвыражение и его значение будет результатом.

Следующий пример должен помочь вам немного улучшить понимание префиксного и постфиксного инкремента и выражений:

<?php
function double($i)
{
    return $i*2;
}
$b = $a = 5;        /* присвоить значение пять переменным $a и $b */
$c = $a++;          /* постфиксный инкремент, присвоить значение $a
                       (5) переменной $c */
$e = $d = ++$b;     /* префиксный инкремент, присвоить увеличенное
                       значение $b (6) переменным $d и $e */

/* в этой точке и $d, и $e равны 6 */

$f = double($d++);  /* присвоить удвоенное значение $d перед
                       инкрементом (2*6 = 12) переменной $f */
$g = double(++$e);  /* присвоить удвоенное значение $e после
                       инкремента (2*7 = 14) переменной $g */
$h = $g += 10;      /* сначала переменная $g увеличивается на 10,
                       приобретая, в итоге, значение 24. Затем значение
                       присвоения (24) присваивается переменной $h,
                       которая в итоге также становится равной 24. */
?>

Некоторые выражения могут рассматриваться как инструкции. В данном случае инструкция имеет вид 'выражение' ';' - выражение с последующей точкой с запятой. В записи '$b=$a=5;', $a=5 - это верное выражение, но само по себе не инструкция. Тогда как '$b=$a=5;' является верной инструкцией.

Последнее, что стоит упомянуть, это истинность значения выражений. Во многих случаях, как правило, в условных операторах и циклах, вас может интересовать не конкретное значение выражения, а только значат ли они TRUE или FALSE. Константы TRUE и FALSE (регистро-независимые) - это два возможных булевых значения. При необходимости выражение автоматически преобразуется в булев тип. Подробнее о том, как это происходит, смотрите в разделе о приведении типов.

PHP предоставляет полную и мощную реализацию выражений, и их полное документирование выходит за рамки этого руководства. Вышеприведенные примеры должны дать вам представление о том, что они из себя представляют и как вы сами можете создавать полезные выражения. Далее, для обозначения любого верного выражения PHP в этой документации мы будем использовать сокращение expr.

Глава 7. Безопасность

Содержание

Вступление

Общие рассуждения

Если PHP установлен как CGI

Если PHP установлен как модуль Apache

Безопасность файловой системы

Безопасность баз данных

Сообщения об ошибках

Использование глобальных переменных (Register_Globals)

Данные, введенные пользователем

Сокрытие PHP

Необходимость обновлений

Вступление

PHP является мощным языком программирования и интерпретатором, взаимодействующим с веб-сервером как модуль либо как независимое бинарное CGI приложение. PHP способен обращаться к файлам, выполнять различные команды на сервере и открывать сетевые соединения. Именно поэтому все скрипты, исполняемые на сервере являются потенциально опасными. PHP изначально разрабатывался как более защищенный (относительно Perl, C) язык для написания CGI-приложений. При помощи ряда настроек во время компиляции, а также настроек во время работы приложения, вы всегда сможете найти подходящее сочетание свободы действий и безопасности.

Поскольку существует много различных способов использования PHP, имеется и множество опций, управляющих его поведением. Широкий выбор опций гарантирует вам возможность использовать PHP в разных целях, но также означает, что некоторые комбинации опций делают сервер незащищенным.

Гибкость конфигурирования PHP можно сравнить с гибкостью самого языка. PHP можно использовать для создания полноценных серверных приложений, использующих доступные для указанного пользователя возможности операционной системы, также возможна реализация включения файлов, хранящихся на сервере с минимальным риском в жестко контролируемой среде. То, насколько безопасен ваш сервер и как настроено окружение, в большей части зависит от PHP-разработчика.

Эта глава начинается с рассмотрения некоторых общих вопросов безопасности, различных конфигурационных опций и их комбинаций, а также ситуаций, когда их использование является безопасным. Кроме того, приводятся некоторые рассуждения касательно безопасного кодирования.

Общие рассуждения

Так как абсолютно безопасные системы являются не более, чем мифом, на практике приходится балансировать между комфортом и безопасностью. Если каждая переменная, вводимая пользователем, будет требовать две биометрические проверки (например, сканирование сетчатки глаза и отпечатки пальцев), то вы получите предельно высокую достоверность данных. Но поскольку заполнение сложной формы будет занимать около получаса, у пользователей такой системы непременно возникнет желание обойти навязчивую защиту.

Правильно поставленная защита должна соответствовать основным требованиям безопасности, не ухудшая при этом работу пользователя и не усложняя самому программисту разработку продукта. Вместе с тем, некоторые атаки могут основываться именно на таком подходе к защите, что приводит к ее постепенному ослаблению.

Следует помнить хорошую поговорку: надежность системы определяется ее самым слабым звеном. Например, если все транзакции логируются по времени, месторасположению, типу транзакции и ряду других параметров, но авторизация пользователя происходит всего лишь по куке (cookie), то связь конкретной записи в логе с пользователем системы весьма сомнительна.

При тестировании следует помнить, что вы не можете проверить все возможные варианты даже для простейшей страницы. Данные, которые вы ожидаете, могут совершенно не соответствовать тому, что введет разъяренный служащий, хакер со стажем или домашний кот, разгуливающий по клавиатуре. Поэтому лучше логически подумать над вопросом: в каком месте могут быть введены неожиданные данные, как их можно модифицировать, усечь либо, наоборот, дополнить.

Интернет наполнен людьми, которые хотят сделать себе имя на том, что взломают ваш код, разрушат сайт, опубликуют на нем неуместный контент или просто сделают ваши дни интереснее. И не важно, маленький у вас сайт или большой, если у вас есть онлайн-сервер - вы уже потенциальная жертва. Многие программы-взломщики несомтрят на размер, они просто перебирают массивы IP-адресов, выискивая очередную жертву. Постарайтесь не стать одной из них.

Если PHP установлен как CGI

Возможные атаки

Использование PHP как бинарного CGI-приложения является одним из вариантов, когда по каким-либо причинам нежелательно интегрировать PHP в веб-сервер (например Apache) в качестве модуля, либо предполагается использование таких утилит, как chroot и setuid для организации безопасного окружения во время работы скриптов. Такая установка обычно сопровождается копированием исполняемого файла PHP в директорию cgi-bin веб-сервера. CERT (организация, следящая за угрозами безопасности) CA-96.11 рекомендует не помещать какие-либо интерпретаторы в каталог cgi-bin. Даже если PHP используется как самостоятельный интерпрктатор, он спроектирован так, чтобы предотвратить возможность следующих атак:

  • Доступ к системным файлам: http://my.host/cgi-bin/php?/etc/passwd

    Данные, введенные в строке запроса (URL) после вопросительного знака, передаются интерпретатору как аргументы командной строки согласно CGI протоколу. Обычно интерпретаторы открывают и исполняют файл, указанный в качестве первого аргумента.

    В случае использования PHP посредством CGI-протокола он не станет интерпретировать аргументы командной строки.

  • Доступ к произвольному документу на сервере: http://my.host/cgi-bin/php/secret/doc.html

    Согласно общепринятому соглашению часть пути в запрошенной странице, которая расположена после имени выполняемого модуля PHP, /secret/doc.html, используется для указания файла, который будет интерпретирован как CGI-программа Обычно, некоторые конфигурационные опции веб-серевера (например, Action для сервера Apache) используются для перенаправления документа, к примеру, для перенаправления запросов вида http://my.host/secret/script.php интерпретатору PHP. В таком случае веб-сервер вначале проверяет права доступа к директории /secret, и после этого создает перенаправленный запрос http://my.host/cgi-bin/php/secret/script.php. К сожалению, если запрос изначально задан в полном виде, проверка на наличие прав для файла /secret/script.php не выполняется, она происходит только для файла /cgi-bin/php. Таким образом, пользователь имеет возможность обратиться к /cgi-bin/php, и, как следствие, к любому защищенному документу на сервере.

    В PHP, указывая во время компиляции опцию --enable-force-cgi-redirect, а таке опции doc_root и user_dir во время выполнения скрипта, можно предотвратить подобные атаки для директорий с ограниченным доступом. Более детально приведенные опции, а также их комбинации будут рассмотрены ниже.

    Вариант 1: обслуживаются только общедоступные файлы

    В случае, если на вашем сервере отсутствуют файлы, доступ к которым ограничен паролем либо фильтром по IP-адресам, нет никакой необходимости использовать данные опции. Если ваш веб-сервер не разрешает выполнять перенаправления либо не имеет возможности взаимодействовать с исполняемым PHP-модулем на необходимом уровне безопасности, вы можете использовать опцию --enable-force-cgi-redirect во время сборки PHP. Но при этом вы должны убедиться, что альтернативные способы вызова скрипта, такие как непосредственно вызов http://my.host/cgi-bin/php/dir/script.php либо с переадресацией http://my.host/dir/script.php, недоступны.

    В веб-сервере Apache перенаправление может быть сконфигурировано при помощи директив AddHandler и Action (описано ниже).

    Вариант 2: использование --enable-force-cgi-redirect

    Эта опция, указываемая во время сборки PHP, предотвращает вызов скриптов непосредственно по адресу вида http://my.host/cgi-bin/php/secretdir/script.php. Вместо этого, PHP будет обрабатывать пришедший запрос только в том случае, если он был перенаправлен веб-сервером.

    Обычно перенаправление в веб-сервере Apache настраивается при помощи следующих опций:

    Action php-script /cgi-bin/php
    AddHandler php-script .php

    Эта опция проверена только для веб-сервера Apache, ее работа основывается на установке в случае перенаправления нестандартной переменной REDIRECT_STATUS, находящейся в CGI-окружении. В случае, если ваш веб-сервер не предоставляет возможности однозначно идентифицировать, является ли данный запрос перенаправленным, вы не можете использовать описываемую в данном разделе опцию и должны воспользоваться любым другим методом работы с CGI-приложениями.

    Вариант 3: использование опций doc_root и user_dir

    Размещение динамического контента, такого как скрипты либо любые другие исполняемые файлы, в директории веб-сервера делает его потенциально опасным. В случае, если в конфигурации сервера допущена ошибка, возможна ситуация, когда скрипты не выполняются, а отображаются в браузере, как обычные HTML-документы, что может привести к утечке конфиденциальной информации (например, паролей), либо информации, являющейся интеллектуальной собственностью. Исходя из таких соображений, многие системные администраторы предпочитают использовать для хранения скриптов отдельную директорию, работая со всеми размещенными в ней файлами по CGI-интерфейсу.

    В случае, если невозможно гарантировать, что запросы не перенаправляются, как было показано в предыдущем разделе, необходимо указывать переменную doc_root, которая отличается от корневой директории веб-документов.

    Вы можете установить корневую директорию для PHP-скриптов, настроив параметр doc_root в конфигурационном файле, либо установив переменную окружения PHP_DOCUMENT_ROOT. В случае, если PHP используется посредством CGI, полный путь к открываемому файлу будет построен на основании значения переменной doc_root и указанного в запросе пути. Таким образом, вы можете быть уверены, что скрипты будут выполняться только внутри указанной вами директории (кроме директории user_dir, которая описана ниже).

    Еще одна используемая при настройке безопасности опция - user_dir. В случае, если переменная user_dir не установлена, путь к открываемому файлу строится относительно doc_root. Запрос вида http://my.host/~user/doc.php приводит к выполнению скрипта, находящегося не в домашнем каталоге соответствующего пользователя, а находящегося в подкаталоге doc_root скрипта ~user/doc.php (да, имя директории начинается с символа ~).

    Но если переменной public_php присвоено значение, например, http://my.host/~user/doc.php, тогда в приведенном выше примере будет выполнен скрипт doc.php, находящийся в домашнем каталоге пользователя, в директории public_php. Например, если домашний каталог пользователя /home/user, будет выполнен файл /home/user/public_php/doc.php.

    Установка опции user_dir происходит независимо от установки doc_root, таким образом вы можете контролировать корневую директорию веб-сервера и пользовательские директории независимо друг от друга.

    Вариант 4: PHP вне дерева веб-документов

    Один из способов существенно повысить уровень безопасности - поместить исполняемый модуль PHP вне дерева веб-документов, например в /usr/local/bin. Единственным недостатком такого подхода является то, что первая строка каждого скрипта должна иметь вид:

    #!/usr/local/bin/php

    Также необходимо сделать все файлы скриптов исполняемыми. Таким образом, скрипт будет рассматриваться так же, как и любое другое CGI-приложение, написанное на Perl, sh или любом другом скриптовом языке, который использует дописывание #! в начало файла для запуска самого себя.

    Что бы внутри скрипта вы могли получить корректные значения переменных PATH_INFO и PATH_TRANSLATED, PHP должен быть сконфигурирован с опцией --enable-discard-path.

    Если PHP установлен как модуль Apache

    Когда PHP используется как модуль Apache, он наследует права пользователя, с которыми был запущен веб-сервер (обычно это пользователь 'nobody'). Это влияет на обеспечение безопасности и реализацию авторизации. Например, если вы используете базу данных, которая не имеет встроенного механизма разграничения доступа, вам прийдется обеспечить доступ к БД для пользователя 'nobody'. В таком случае зловредный скрипт может получить доступ к базе данных и модифицировать ее, даже не зная логина и пароля. Вполне возможна ситуация, когда веб-паук неверными запросами страницы администратора базы данных уничтожит все данные или даже структуру БД. Вы можете избежать такой ситуации при помощи авторизации Apache или разработав собственную модель доступа, используя LDAP, файлы .htaccess или любые другие технологии, внедряя соответствующий код в ваши скрипты.

    Достаточно часто используются такие настройки безопасности, при которых PHP (имеется ввиду пользователь, с правами которого выполняется Apache) имеет минимальные привелегии, например отсутствует возможность записи в пользовательские директории. Или, например, отсутствует возможность работать с базой данных. При этом система безопасности не позволяет записывать как "хорошие", так и "плохие" файлы, аналогично позволяет производить как "хорошие", так и "плохие" транзакции.

    Распространенной ошибкой является запуск Apache с правами суперпользователя или любое другое расширение полномочий веб-сервера.

    Расширение привилегий веб-сервера до полномочий угрожает работоспособности всей системы, такие команды, как sudo, chroot должны выполняться исключительно теми, кто считает себя профессионалами в вопросах безопасности.

    Существует несколько простых решений. Используя open_basedir, вы можете ограничить дерево доступных директорий для PHP. Вы так же можете определить область доступа Apache, ограничив все веб-сервисы не-пользовательскими или не-системными файлами.

    Безопасность файловой системы

    PHP является одним из важных моментов в вопросе безопасности сервера, поскольку PHP-скрипты могут манипулировать файлами и каталогами на диске. В связи с этим существуют конфигурационные настройки, указывающие, какие файлы могут быть доступны и какие операции с ними можно выполнять. Необходимо проявлять осторожность, поскольку любой из файлов с соответствующими правами доступа может быть прочитан каждым, кто имеет доступ к файловой системе.

    Поскольку в PHP изначально предполагался полноправный пользовательский доступ к файловой системе, можно написать скрипт, который позволит читать системные файлы, такие как /etc/passwd, управлять сетевыми соединениями, отправлять задания принтеру, и так далее. Как следствие вы всегда должны быть уверены в том, что файлы, которые вы читаете или модифицируете, соответствуют вашим намерениям.

    Рассмотрим следующий пример, в коротом пользователь создал скрипт, удаляющий файл из его домашней директории. Предполагается ситуация, когда веб-интерфейс, написанный на PHP, регулярно используется для работы с файлами, и настройки безопасности позволяют удалять файлы в домашнем каталоге.

    Пример 15-1. Недостаточная проверка внешних данных.

    <?php
    // Удаление файла из домашней директории пользователя
    $username = $_POST['user_submitted_name'];
    $homedir = "/home/$username";
    $file_to_delete = "$userfile";
    unlink ("$homedir/$userfile");
    echo "$file_to_delete has been deleted!";
    ?>
  • Поскольку переменные вводятся в пользовательской форме, существует возможность удалить файлы, принадлежащие кому-либо другому, введя соответствующие значения. В этом случае может понадобиться авторизация. Посмотрим, что произойдет, если будут отправлены значения "../etc/" и "passwd". Скрипт выполнит следующие действия:

    Пример 15-2. Атака на файловую систему

    <?php
    // Удаление любого файла, доступного из PHP-скрипта.
    // В случае, если PHP работает с правами пользователя root:
    $username = "../etc/";
    $homedir = "/home/../etc/";
    $file_to_delete = "passwd";
    unlink ("/home/../etc/passwd");
    echo "/home/../etc/passwd has been deleted!";
    ?>

    Cуществуют два решения описанной проблемы.

    Вот улучшеный вариант кода:

    Пример 15-3. Более безопасная проверка имени файла

    <?php
    // Удаление любого файла, доступного из PHP-скрипта.
    $username = $_SERVER['REMOTE_USER']; // использование авторизации

    $homedir = "/home/$username";

    $file_to_delete = basename("$userfile"); // усечение пути
    unlink ($homedir/$file_to_delete);

    $fp = fopen("/home/logging/filedelete.log","+a"); //логируем удаление
    $logstring = "$username $homedir $file_to_delete";
    fputs ($fp, $logstring);
    fclose($fp);

    echo "$file_to_delete has been deleted!";
    ?>

    Однако и такая проверка не учитывает все возможные ситуации. Если система авторизации позволяет пользователям выбирать произвольные логины, вломщик может создать учетную запись вида "../etc/" и система опять окажется уязвимой. Исходя из этого, вам может понадобиться более строгая проверка:

    Пример 15-4. Более строгая проверка имени файла

    <?php
    $username = $_SERVER['REMOTE_USER']; // использование авторизации
    $homedir = "/home/$username";

    if (!ereg('^[^./][^/]*$', $userfile))
         die('bad filename'); //завершение работы

    if (!ereg('^[^./][^/]*$', $username))
         die('bad username'); //завершение работы
    //etc...
    ?>

    В зависимости от используемой вами операционной системы необходимо предусматривать возможность атаки на разнообразные файлы, включая системные файлы устройств (/dev/ или COM1), конфигурационные файлы (например /etc/ или файлы с расширением .ini), хорошо известные области хранения данных (/home/, My Documents), и так далее. Исходя из этого, как правило, легче реализовать такую политику безопасности, в которой запрещено все, исключая то, что явно разрешено.

    Безопасность баз данных

    На сегодняшний день базы данных являются ключевыми компонентами большинства веб-приложений, позволяя предоставлять на сайтах динамический контент. Поскольку в таких БД может храниться очень точная или конфиденциальная информация, вы должны обеспечить хорошую защиту данных.

    Для извлечения или сохранения любых данных вам необходимо открыть соединение с базой данных, отправить верный запрос, извлечь результат и закрыть соединение. В настоящее время наиболее распространенный стандарт общения - структурированный язык запросов (SQL). Всегда следует помнить о возможности атаки посредством SQL-запроса.

    Очевидно, что сам по себе PHP не может защитить вашу базу данных. Этот раздел документации рассказывает об основах безопасного доступа и управления данными в PHP-скриптах.

    Запомните простое правило: максимальная защита. Чем больше потенциально опасных участков системы вы проработаете, тем сложнее будет потенциальному взломщику получить доступ к базе данных или повредить ее. Хороший дизайн базы данных и программных приложений поможет вам справиться с вашими страхами.

    Проектирование базы данных

    Первый шаг - это всегда создание БД, исключая тот случай, когда вы хотите использовать готовую базу, предоставляемую третьим лицом. После того, как база данных создана, она назначается пользователю, который выполнил создавший БД запрос. Как правило, только владелец (или суперпользователь) может выполнять различные действия с различными объектами, хранимыми в базе данных. Для того, чтобы и другие пользователи имели к ней доступ, их необходимо наделить соответствующими привелегиями.

    Приложения не должны соединяться с базой данных, используя учетную запись владельца или суперпользователя, иначе они смогут модифицировать структуру таблиц (например, удалить некоторые таблицы) или даже удалить все содержимое БД целиком.

    Вы можете создать различные учетные записи пользователей БД для каждой индивидуальной потребности приложения с соответствующими функциональными ограничениями. Рекомендуется назначать только самые необходимые привилегии, также вы должны избегать ситуаций, когда один и тот же пользователь может взаимодействовать с базой данных в нескольких режимах. Вы должны понимать, что если злоумышленник сможет воспользоваться какой-либо учетной записью вашей БД, он сможет вносить в базу все те изменения, что и программа, которая использует текущую учетную запись.

    Вам не обязательно реализовывать всю бизнес-логику в веб-приложении (т.е. в скриптах), для этого также можно использовать возможности, предоставляемые базой данных: триггеры, представления, правила. В случае роста системы вам понадобятся новые соединения с БД, и логику работы понадобиться дублировать для каждого нового интерфейса доступа. Исходя из вышесказанного, триггеры могут использоваться для прозрачной и автоматической обработки записей, что часто необходимо при отладке приложений или при трассировке отката транзакций.

    Соединение с базой данных

    Вы можете использовать безопасные SSL или ssh соединения, для шифрования данных, которыми обмениваются клиент и сервер. Если вы реализуете что-нибудь из этого, то мониторинг трафика и сбор данных о вашей базе данных для потенциального взломщика существенно усложнится.

    Защита хранилища базы данных

    SSL/SSH защищает данные, которыми обмениваются клиент и сервер, но не защищают сами данные, хранимые в базе данных. SSL - протокол шифрования на уровне сеанса передачи данных.

    В случае, если взломщик получил непосредственный доступ к БД (в обход веб-сервера), он может извлечь интересующие данные или нарушить их целостность, поскольку информация не защищена на уровне самой БД. Шифрование данных - хороший способ предотвратить такую ситуацию, но лишь незначительное количество БД предоставляют такую возможность.

    Наиболее простое решение этой проблемы - установить вначале обыкновенный программный пакет для шифрования данных, а затем использовать его в ваших скриптах. PHP, в таком случае, может помочь вам в работе с такими расширениями как Mcrypt и Mhash, реализующими различные алгоритмы криптования. При таком подходе скрипт вначале шифрует сохраняемые данные, а затем дешифрует их при запросе. Ниже приведены примеры того, как работает шифрование данных в PHP-скриптах.

    В случае работы со скрытыми служебными данными их нешифрованное представление не требуется (т.е. не отображается), и, как следствие, можно использовать хеширование. Хорошо известный пример хэширования - хранение MD5-хеша от пароля в БД, вместо хранения оригинального значения. Более детальная информация доступна в описании функций crypt() and md5().

    Пример 15-5. Использование хешированных паролей

    // сохранение хешированного пароля
    $query  = sprintf("INSERT INTO users(name,pwd) VALUES('%s','%s');",
                addslashes($username), md5($password));
    $result = pg_exec($connection, $query);

    // проверка введенного пользователем логина и пароля на корректность
    $query = sprintf("SELECT 1 FROM users WHERE name='%s' AND pwd='%s';",
                addslashes($username), md5($password));
    $result = pg_exec($connection, $query);

    if (pg_numrows($result) > 0) {
        echo "Welcome, $username!";
    }
    else {
        echo "Authentication failed for $username.";
    }

    SQL-инъекции

    Многие веб-разработчики даже не догадываются, что SQL-запросы могут быть подделаны, и считают, что SQL-запросы всегда достоверны. На самом деле поддельные запросы могут обойти ограничения доступа, стандартную проверку авторизации, а некоторые виды запросов могут дать возможность выполнять команды операционной системы.

    Принудительное внедрение вредоносных инструкций в SQL-запросы - методика, в которой взломщик создает или изменяет текущие SQL-запросы для работы со скрытыми данными, их изменения или даже выполнения опасных комманд операционной системы на сервере базы данных. Атака выполняется на базе приложения, строящего SQL-запросы из пользовательского ввода и статических переменных. Следующие примеры, к сожалению, построены на реальных фактах.

    Благодаря отсутствию проверки пользовательского ввода и соединением с базой данных под учетной записью суперпользователя (или любого другого пользователя, наделенного соответствующими привелегиями), взломщик может создать еще одного пользователя БД с правами суперпользователя.

    Пример 15-6. Постраничный вывод результата... и создание суперпользователя в PostgreSQL и MySQL

    $offset = argv[0]; // проверка пользовательских данных отсутствует
    $query  = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
    // используя PostgreSQL
    $result = pg_exec($conn, $query);
    // используя MySQL
    $result = mysql_query($query);

    Обычно пользователи кликают по ссылкам 'вперед' и 'назад', вследствии чего значение переменной $offset заносится в адресную строку. Скрипт ожидает, что $offset - десятиричное число. Однако, взломщик может попытаться взломать систему, присоединив к строке запроса дополнительную подстроку, обработанную функцией urlencode():

    // используя PostgreSQL 
    0;
    insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
        select 'crack', usesysid, 't','t','crack'
        from pg_shadow where usename='postgres';
    --
     
    // используя MySQL
    0;
    UPDATE user SET Password=PASSWORD('crack') WHERE user='root';
    FLUSH PRIVILEGES;

    Если это произойдет, скрипт предоставит взломщику доступ к базе с правами суперпользователя. Заметим, что 0; использован для того, чтобы задать правильное значение смещения для первого запроса и корректно его завершить.

    Замечание: Уже привычна технология, когда разработчики указывают принудительное игнорирование парсером SQL оставшейся части запроса при помощи нотации --, означающей комментарий.

    Еще один вероятный способ получить пароли учетных записей в БД - атака страниц, предоставляющих поиск по базе. Взломщику нужно лишь проверить, используется ли в запросе передаваемая на сервер и необрабатываемая надлежащим образом переменная. Это может быть один из устанавливаемых на предыдущей странице фильтров, таких как WHERE, ORDER BY, LIMIT и OFFSET, используемых при построении запросов SELECT. В случае, если используемая вами база данных поддерживает конструкцию UNION, взломщик может присоединить к оригинальному запросу еще один дополнительный, для извлечения пользовательских паролей. Настоятельно рекомендуем использовать только зашифрованные пароли.

    Пример 15-7. Листинг статей... и некоторых паролей (для любой базы данных)

    $query  = "SELECT id, name, inserted, size FROM products
                      WHERE size = '$size'
                      ORDER BY $order LIMIT $limit, $offset;";
    $result = odbc_exec($conn, $query);

    Статическая часть запроса может комбинироваться с другим SQL-запросом, который откроет все пароли:

    '
    union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable;
    --

    Если этот запрос (использующий ' и --) присоединить к значению одной из переменных, используемых для формирования $query, запрос заметно преобразится.

    Команды UPDATE также могут использоваться для атаки. Опять же, есть угроза разделения инструкции на несколько запросов, присоединения дополнительного запроса. Также взломщик может видоизменить выражение SET. В этом случае потенциальному взломщику необходимо обладать некоторой дополнительной информацией для успешного манипулирования запросами. Эту информацию можно получить, проанализировав используемые в форме имена переменных либо просто перебирая все наиболее распространенные варианты названия соответствующих полей (а их не так уж и много).

    Пример 15-8. От восстановления пароля... до получения дополнительных привилегий (для любой базы данных)

    $query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";

    Но злоумышленник может ввести значение ' or uid like'%admin%'; -- для переменной $uid для изменения пароля администратора или просто присвоить переменной $pwd значение "hehehe', admin='yes', trusted=100 " (с завершающими пробелами) для получения дополнительных привелегий. При выполнении запросы переплетаются:

    // $uid == ' or uid like'%admin%'; --
    $query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%'; --";

    // $pwd == "hehehe', admin='yes', trusted=100 "
    $query = "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE ...;"

    Пугающий пример того, как на сервере баз данных могут выполняться команды операционной системы.

    Пример 15-9. Выполнение команд операционной системы на сервере (для базы MSSQL)

    $query  = "SELECT * FROM products WHERE id LIKE '%$prod%'";
    $result = mssql_query($query);

    Если взломщик введет значениме a%' exec master..xp_cmdshell 'net user test testpass /ADD' -- для переменной $prod, тогда запрос $query будет выглядеть так:

    $query  = "SELECT * FROM products
                        WHERE id LIKE '%a%'
                        exec master..xp_cmdshell 'net user test testpass /ADD'--";
    $result = mssql_query($query);

    MSSQL сервер выполняет SQL-команды в пакетном режиме, в том числе и операции по заведению локальных учетных записей базы данных. В случае, если приложение работает с привилегиями администратора и сервис MSSQL запущен с необходимыми привилегиями, выполнив приведенные выше действия, взломщик получит аккаунт для доступа к серверу.

    Замечание: Некоторые приведенные в этой главе примеры касаются конкретной базы данных. Это не означает, что аналогичные атаки на другие программные продукты невозможны. Работоспособность вашей базы данных может быть нарушена каким-либо другим способом.

    Способы защиты

    Вы можете утешать себя тем, что в большинстве случаев, взломщик должен обладать некоторой информацией о структуре базы данных. Вы правы, но вы не знаете, когда и как будет предпринята попытка взлома, в случае если это произойдет ваша БД окажется незащищенной. Если вы используете программный продукт с открытыми исходными кодами или просто общедоступный пакет для работы с базой данных (например контент менеджер или форум), взломщик легко сможет воспроизвести интересующие его участки кода. В случае если они плохо спроектированы, это может являться одной из угроз вашей безопасности.

    Большинство успешных атак основывается на коде, написанном без учета соответствующих требований безопасности. Не доверяйте никаким вводим данным, особенно если они поступают со стороны клиента, даже если это списки в форме, скрытые поля или куки. Приведенные примеры показывают, к каким последствиям могут привести подделанные запросы.

  • Старайтесь не открывать соединение с базой, используя учетную запись владельца или администратора. Всегда старайтесь использовать специально созданных пользователей с максимально ограниченными правами.

  • Всегда проверяйте введенные данные на соответствие ожидаемому типу. В PHP есть множество функций для проверки данных: начиная от простейших Функций для работы с переменными и Функции определения типа символов (такие как is_numeric(), ctype_digit()) и заканчивая Perl-совместимыми регулярными выражениями.

  • В случае, если приложение ожидает цифровой ввод, примените функцию is_numeric() для проверки введенных данных, или принудительно укажите их тип при помощи settype(), или просто используйте числовое представление при помощи функции sprintf().

    Пример 15-10. Более безопасная реализация постраничной навигации

    settype($offset, 'integer');
    $query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";

    // обратите внимание на формат %d, использование %s было бы бессмысленно
    $query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;",
                     $offset);

    Помимо всего вышесказанного, вы можете логировать запросы в вашем скрипте либо на уровне базы данных, если она это поддерживает. Очевидно, что логирование не может предотвратить нанесение ущерба, но может помочь при трассировке взломанного приложения. Лог-файл полезен не сам по себе, а информацией, которая в нем содержится. Причем, в большинстве случаев полезно логировать все возможные детали.

    Сообщения об ошибках

    С точки зрения безопасности вывод сообщений об ошибках несет в себе как плюсы, так и минусы.

    Одна из стандартных методик, применяемых в атаках - ввод некорректных данных с последующим анализом содержания и характера сообщений об ошибках. Это дает взломщику возможность проверить скрипты и данные сервера на наличие потенциальных дыр. Например, если взломщик получил некоторую информацию о странице на основании отправки формы, он попробует предопределить некоторые передаваемые значения или модифицировать их:

    Пример 15-11. Атака на переменные в HTML-странице

    <form method="post" action="attacktarget?username=badfoo&amp;password=badfoo">

    <input type="hidden" name="username" value="badfoo" />

    <input type="hidden" name="password" value="badfoo" />

    </form>

    Возникаемые во время работы скриптов ошибки являются достаточно ценной информацией для разработчика, содержащей такие данные, как функция или файл, а также номер строки, в которой возникла ошибка. Вся эта информация может быть использована для взлома. Для PHP-разработчика достаточно привычно пользоваться такими функциями, как show_source(), highlight_string() или highlight_file() в целях отладки, но в живых сайтах это может открыть информацию о скрытых переменных, непроверяемом синтаксисе и других потенциально опасных моментах. Особенно опасно наличие кода со встроенным механизмом отладки в публичных частях сайта. Взломщик может попытаться запустить отладочный механизм, подбирая основные признаки отладки:

    Пример 15-12. Использование стандартных отладочных переменных

    <form method="post" action="attacktarget?errors=Y&amp;showerrors=1&amp;debug=1">

    <input type="hidden" name="errors" value="Y" />

    <input type="hidden" name="showerrors" value="1" />

    <input type="hidden" name="debug" value="1" />

    </form>

    Независимо от метода обработки ошибок возможность проверки системы на наличие ошибок снабжает взломщика дополнительной информацией.

    Например, стандартный вывод об ошибке указывает операционную систему, в которой выполняются PHP скрипты. Если взломщик анализирует обыкновенную HTML-страницу, пытаясь найти уязвимые места, используя ввод неверных данных он может обнаружить использование PHP скриптов в данной системе.

    Также уведомление об ошибке может дать информацию о том, какая база данных используется, или, к примеру, как построена логика работы скриптов. Это, в свою очередь, может позволить взломщику подключиться к открытому порту базы данных либо найти специфичные ошибки в коде. Пробуя поочередно различные неверные блоки данных, злоумышленник может определить порядок аутентификации в скрипте (например, по номерам строк с ошибками) или проверять на наличие дыр различные участки кода.

    Вывод стандартных ошибок, связанных с файловой системой, может указать, с какими привелегиями запущен веб-сервер, и как организованы каталоги сайта. Обработка подобных ошибок, написанная разработчиками приложения, может только усугубить проблему, если взломщиком будет найден способ обнаружить "скрытую" отладочную информацию.

    Существует три основныч способа решения этой проблемы. Первый заключается в том, чтобы структурировать все функции и попытаться компенсировать объем выдаваемых ошибок. Второй способ - полностью отключить в работающем коде вывод сообщений об ошибках. И, наконец, третий способ - использовать специальные средства PHP для создания собственного обработчика ошибок. В зависимости от используемой вами политики безопасности вы можете применять в вашей конкретной ситуации все три способа.

    Один из возможных способов обезопасить ваш код перед его публикацией для общего доступа - индивидуальное использование error_reporting(), чтобы выявить потенциально опасные переменные. Тестируя код перед выпуском релиза при помощи значения E_ALL, вы достаточно легко можете обнаружить участки кода, в которых переменные могут быть подменены либо модифицированы. После окончания тестирования, установив значение E_NONE, вы можете полностью отключить вывод сообщений об ошибках.

    Пример 15-13. Поиск потенциально опасных переменных при помощи E_ALL

    <?php
    if ($username) {  // Переменная не инициализируется перед использованием
        $good_login = 1;
    }
    if ($good_login == 1) { // Если предыдущая проверка потерпела неудачу, переменная оказывается неинициализированной
        readfile ("/highly/sensitive/data/index.html");
    }
    ?>

    Использование глобальных переменных (Register_Globals)

    Наверное, наиболее спорным моментом в разработке PHP стала замена значения по умолчанию для опции register_globals с ON на OFF в версии 4.2.0. Большинство пользователей доверились разработчикам, даже не зная, что это за опция и как она влияет на работу PHP. Эта страница документации призвана показать, как эта настройка сочетается с вопросами безопасности при разработке приложений. Следует понимать, что сама по себе эта опция никак не влияет на безопасность, ургозу представляет некорректное использование предоставляемых ею возможностей.

    В случае, если значение параметра register_globals ON, перед выполнением вашего кода будут инициализированы различные переменные, например, переменные, переданные при отправке формы. Также, учитывая тот факт, что PHP не требует инициализации переменных, написать потенциально опасный код очень легко. Это было очень спорным решением, но общество разработчиков PHP решило изменить значение по умолчанию этой директивы на OFF. В противном случае при написании кода разработчики не могли бы с уверенностью сказать, откуда пришла та или иная переменная и насколько она достоверна. До такого нововведения переменные, определяемые разработчиком внутри скрипта, и передаваемые пользователем внешние данные могли перемешиваться. Приведем простой пример злоупотребления конфигурационной опцией register_globals:

    Пример 15-14. Пример опасного кода с register_globals = on

    <?php
    // устанавливаем переменную $authorized = true только для пользователей, прошедших авторизацию
    if (authenticated_user()) {
        $authorized = true;
    }

    // Поскольку в случае неудачи при проверке авторизации переменная $authorized
    // не установлена, она может быть установлена автоматически, благодаря register_globals,
    // например, при GET запросе GET auth.php?authorized=1.
    // Таким образом, пройти эту проверку можно без авторизации
    if ($authorized) {
        include "/highly/sensitive/data.php";
    }
    ?>

    В случае register_globals = on логика работы скрипта может быть нарушена. В случае, если установленное значение off, переменная $authorized не может быть установлена из внешних данных запроса, и скрипт будет работать корректно. Но все же инициализация переменных - один из признаков хорошего тона в программировании. Например, в приведенном выше участке кода мы могли поместить $authorized = false в качестве первой строки. Такой код работал бы как со значением on, так и off опции register_globals, и подразумевая, что по умолчанию пользователь не проходил авторизацию.

    Приведем еще один пример, использующий сессии. В случае, если register_globals = on, мы можем использовать переменную $username в приведенном ниже примере, но тогда у нас не будет уверенности в достоверности ее значения (к примеру, она могла быть передана в GET-запросе).

    Пример 15-15. Пример использования сессий со значением register_globals on или off

    <?php
    // Мы не знаем, откуда получена переменная $username, но точно знаем, что
    // переменная $_SESSION хранит в себе данные сессии
    if (isset($_SESSION['username'])) {

        echo "Hello <b>{$_SESSION['username']}</b>";

    } else {

        echo "Hello <b>Guest</b><br />";
        echo "Would you like to login?";

    }
    ?>

    Также существует возможность реализации оперативного реагирования в случае попытки подмены переменных. Так как во время разработки приложения мы знаем ожидаемое значение переменной, а также знаем ее достоверное значение, мы можем их сопоставить. Это не защитит код от подмены переменных, но усложнит перебор возможных вариантов. Если вы не хотите знать, как именно были получены внешние данные, используйте переменную $_REQUEST, которая состоит из данных GET и POST запросов, а также данных COOKIE. Также, информацию об этом можно найти в разделе внешние данные в PHP.

    Пример 15-16. Обнаружение попытки подмены переменных

    <?php
    if (isset($_COOKIE['MAGIC_COOKIE'])) {

        // MAGIC_COOKIE получена из достоверного источника.
        // Для полной уверенности необходимо проверить ее значение.

    } elseif (isset($_GET['MAGIC_COOKIE']) || isset($_POST['MAGIC_COOKIE'])) {

       mail("admin@example.com", "Обнаружена попытка взлома", $_SERVER['REMOTE_ADDR']);
       echo "Обнаружено нарушение безопасности, администратор уведомлен.";
       exit;

    } else {

       // MAGIC_COOKIE в данных запроса не присутствует
    }
    ?>

    Следует понимать, что установка register_globals в off не сделает ваш код безопасным. Каждую полученную от пользователя переменную следует проверять на соответствие ожидаемому значению. Всегда проверяйте ввод пользователя и инициализируйте все используемые переменные. Для проверки на наличие неинициализированных переменных можно включить в опцию error_reporting() отображение ошибок категории E_NOTICE.

    Суперглобальные переменные: замечание о доступности: Начиная с PHP 4.1.0, стали доступными суперглобальные массивы, такие как $_GET, $_POST, $_SERVER и т.д. Дополнительную информацию смотрите в разделе руководства superglobals

    Данные, введенные пользователем

    Наиболее опасные дыры во многих PHP-скриптах возникают не столько из-за самого языка, сколько из-за кода, написанного без учета соответствующих требований безопасности. Как следствие, вы всегда должны выделять время на исследование разрабатываемого участка кода, чтобы оценить потенциальную угрозу от ввода переменной с нестандартным значением.

    Пример 15-17. Потенциально опасное использование переменных

    <?php
    // удалить файлы из домашней директории пользователя...
    // а может, еще что нибудь?
    unlink ($evil_var);

    // записать в лог-файл выполняемое действие...
    // может быть, даже /etc/passwd?
    fputs ($fp, $evil_var);

    // выполнение тривиальных действий... или rm -rf *?
    system ($evil_var);
    exec ($evil_var);

    ?>

    Вы должны тщательно проверять ваш код и быть абсолютно уверены в том, что все данные, передаваемые веб-браузером, проверяются надлежащим образом. Попробуйте ответить для себя на следующие вопросы:

    Ответив на эти вопросы во время написания скрипта, а не после, вы, возможно, предотвратите последующую доработку скрипта в целях повышения его безопасности. Начиная разработку с этих вопросов, вы не гарантируете полную безопасность вашей системы, но сможете значительно повысить её.

    Вы также можете предусмотреть отключение таких конфигурационных опций, как register_globals, magic_quotes и некоторых других, которые могут приводить к сомнениям относительно происхождения или значения получаемых переменных. Использование при написании кода режима error_reporting(E_ALL) может помочь, предупреждая вас об использовании переменных до инициализации или проверки (что предотвратит работу с данными, отличныи от ожидаемых).

    Сокрытие PHP

    В общем случае внесение неясности ненамного улучшает защищенность системы. Но бывают случаи, когда следует использовать малейшую возможность.

    Несколько несложных методик могут помочь вам скрыть PHP, что усложняет работу потенциального взломщика, который пытается найти брешь в вашей системе. Установив опцию expose_php = off в конфигурационном файле php.ini, вы уменьшите количество доступной хакеру информации.

    Еще одна методика заключается в настройке веб-сервера таким образом, чтобы он обрабатывал файлы с различными расширениями как PHP-скрипты. Это можно указать как в .htaccess файлах, так и конфигурационном файле Apache. В таком случае вы сможете использовать при написании кода нестандартные расширения:

    Пример 15-18. Маскировка PHP под другие языки программирования

    # Теперь PHP-скрипты могут иметь те же расширения, что и другие языки программирования

    AddType application/x-httpd-php .asp .py .pl

    Или скрыть его совсем:

    Пример 15-19. Использование неизвестных расширений для PHP-скриптов

    # Теперь PHP-скрипты могут иметь неизвестные типы файлов

    AddType application/x-httpd-php .bop .foo .133t

    Также можно спрятать его под видом HTML-кода, что приведет к потере производительности, так как все HTML файлы будут обрабатываться как PHP-код:

    Пример 15-20. Маскировка PHP-кода под html-файлы

    # Теперь PHP-скртпы могут выглядеть как обыкновенный HTML

    AddType application/x-httpd-php .htm .html

    Чтобы достичь желаемого эффекта, вы должны переименовать все ваши скрипты в соответствии с выбранным вами расширением. Описанное в этом разделе документации повышение безопасности через сокрытие PHP имеет мало недостатков при небольших затратах.

    Необходимость обновлений

    PHP, как и любая другая система, все время тщательно проверяется и улучшается. Каждая новая версия содержит множество как существенных, так и мелких новшеств и доработок, которые, в том числе, касаются повышения безопасности, расширения конфигурационных возможностей, а так же стабильности вашей системы.

    Как и в других языках программирования, рекомендуется регулярно обновлять PHP и быть в курсе изменений, сделанных в последних версиях.

        Поддержать сайт на родительском проекте КГБ